Index: include/llvm/Analysis/PValue.h =================================================================== --- /dev/null +++ include/llvm/Analysis/PValue.h @@ -0,0 +1,413 @@ +//===- PValue.h -------- isl C++ interface and wrapper --------*- C++ -*---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// High-level interface for the genration and manipulation of polyhedral values +// (PV) with an isl backedn. Abstractions for sets (isl_set -> PVSet), maps +// (isl_map -> PVMap) and piece-wise affine functions (isl_pw_aff -> PVAff) +// exist. Memory managment and space adjustments happen automatically. +// +// Parts of the code and ideas have been ported from the Polly [0] project. +// +// [0] http://polly.llvm.org/ +// +// NOTE: This is a work in progress and therefor not a stable interface. +// +// TODO: Comments and descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PVALUE_H +#define LLVM_SUPPORT_PVALUE_H + +#include "llvm/IR/Instructions.h" +#include "llvm/Support/raw_ostream.h" + +struct isl_id; +struct isl_ctx; +struct isl_set; +struct isl_map; +struct isl_space; +struct isl_pw_aff; + +namespace llvm { +class PVAff; + +class PVBase { + friend class PVAff; + friend class PVMap; + friend class PVCtx; + friend class PVSet; + friend class PVId; + + virtual isl_ctx *getIslCtx() const = 0; + virtual isl_space *getSpace() const = 0; + +public: + virtual std::string str() const; + + virtual bool isComplex() const { return false; } + + virtual operator bool() const = 0; + + /// Return @p Prefix + @p Middle + @p Suffix but Isl compatible. + static std::string getIslCompatibleName(const std::string &Prefix, + const std::string &Middle, + const std::string &Suffix); +}; + +class PVCtx : public PVBase { + isl_ctx *Obj; + +public: + PVCtx(); + PVCtx(isl_ctx *Ctx) : Obj(Ctx) {} + PVCtx(const PVCtx &Other) : Obj(Other.Obj) {} + + isl_ctx *getIslCtx() const { return Obj; } + isl_space *getSpace() const; + + std::string str() const; + operator bool() const { return Obj != nullptr; } +}; + + +class PVId : public PVBase { + friend class PVAff; + friend class PVMap; + friend class PVSet; + + isl_id *Obj; + + operator isl_id *() const; + +public: + PVId() : Obj(nullptr) {} + PVId(isl_id *Obj) : Obj(Obj) {} + PVId(const PVId &Other); + PVId(PVId &&Other); + PVId(const PVBase &Base, const std::string &Name, void *Payload = nullptr); + + ~PVId(); + + PVId &operator=(const PVId &Other); + PVId &operator=(PVId &&Other); + + void *getPayload() const; + template T getPayloadAs() const { + return static_cast(getPayload()); + } + + isl_ctx *getIslCtx() const; + isl_space *getSpace() const; + std::string str() const; + + operator bool() const { return Obj != nullptr; } +}; + + +class PVSet : public PVBase { + friend class PVAff; + friend class PVMap; + + isl_set *Obj; + + operator isl_set *() const; + +protected: + + isl_ctx *getIslCtx() const; + isl_space *getSpace() const; + +public: + + PVSet() : Obj(nullptr) {} + PVSet(const PVSet &Other); + PVSet(PVSet &&Other) : Obj(Other.Obj) { Other.Obj = nullptr; } + + static PVSet createParameterRange(const PVId &Id, int LowerBound, + int UpperBound); + + PVSet(isl_set *S); + + ~PVSet(); + + PVSet &operator=(const PVSet &Other); + PVSet &operator=(PVSet &&Other); + operator bool() const { return Obj != nullptr; } + + /// Return the number of dimensions of this set. + size_t getNumDimensions() const; + + /// Return the number of parameters of this set. + size_t getNumParameters() const; + + bool isEmpty() const; + bool isUniverse() const; + bool isBounded() const; + + int getParameterPosition(const PVId &Id) const; + + bool hasLowerBoundForParam(const PVId &Id); + PVSet &minForParam(const PVId &Id); + + void dropUnusedParameters(); + + PVSet &simplify(const PVSet &S); + PVSet &simplifyParameters(const PVSet &S); + + /// Unify this set inplace with @p S. + PVSet &unify(const PVSet &S); + + /// Subtract ... + PVSet &subtract(const PVSet &S); + + /// Intersect this set inplace with @p S. + PVSet &intersect(const PVSet &S); + + /// + PVSet &complement(); + + PVSet &maxInLastInputDims(unsigned Dims); + + PVSet &addInputDims(unsigned Dims); + PVSet &dropFirstInputDims(unsigned Dims); + PVSet &dropLastInputDims(unsigned Dims); + PVSet &dropDimsFrom(unsigned FirstDim); + PVSet &fixInputDim(unsigned Dim, int64_t Value); + PVSet &equateInputDim(unsigned Dim, const PVId &Id); + PVSet &setInputLowerBound(unsigned Dim, int64_t Value); + + PVSet &preimage(const PVAff &PWA); + PVSet &projectParameter(const PVId &Id); + + PVSet &getNextIteration(unsigned Dim); + PVSet &getNextIterations(unsigned Dim); + + PVId getParameter(unsigned No) const; + void getParameters(SmallVectorImpl &Parameters) const; + void getParameters(SmallVectorImpl &Parameters) const; + + static PVSet empty(const PVBase &Base); + static PVSet empty(const PVBase &Base, unsigned Dims); + static PVSet universe(const PVBase &Base); + + static PVSet unify(const PVSet &S0, const PVSet &S1); + static PVSet intersect(const PVSet &S0, const PVSet &S1); + + std::string str() const; +}; + + +class PVMap : public PVBase { + friend class PVAff; + friend class PVSet; + + isl_map *Obj; + + operator isl_map *() const; + +protected: + isl_ctx *getIslCtx() const; + isl_space *getSpace() const; + +public: + + PVMap() : Obj(nullptr) {} + PVMap(const PVMap &Other); + PVMap(PVMap &&Other) : Obj(Other.Obj) { Other.Obj = nullptr; } + PVMap(ArrayRef Affs, const PVId &Id); + PVMap(const PVAff &Aff, long Factor); + PVMap(const PVId &ValId, const PVId &InputId = PVId(), + const PVId &OutputId = PVId()); + + PVMap(isl_map *M); + + ~PVMap(); + + PVMap &operator=(const PVMap &Other); + PVMap &operator=(PVMap &&Other); + operator bool() const { return Obj != nullptr; } + + bool isEmpty() const; + + /// Return the number of input dimensions of this function. + size_t getNumInputDimensions() const; + + /// Return the number of output dimensions of this function. + size_t getNumOutputDimensions() const; + + /// Return the number of parameters of this function. + size_t getNumParameters() const; + + /// Use the intersection of the domain with the set @p S as new domain. + /// + /// @param Dom The set to intersect the domain with. + /// + /// @returns A reference to this object (*this). + PVMap &intersectDomain(const PVSet &Dom); + + PVMap &intersect(const PVMap &Other); + + PVMap &addToOutputDimension(const PVMap &Other, unsigned Dim); + + int getParameterPosition(const PVId &Id) const; + void eliminateParameter(const PVId &Id); + + PVSet getParameterSet() const; + + PVId getParameter(unsigned No) const; + + PVId getInputId() const; + PVId getOutputId() const; + + void dropUnusedParameters(); + + void getParameters(SmallVectorImpl &Parameters) const; + void getParameters(SmallVectorImpl &Parameters) const; + + void simplify(const PVAff &Aff); + + using IslCombinatorFn = std::function; + using CombinatorFn = std::function; + static CombinatorFn getCombinatorFn(IslCombinatorFn Fn); + + PVMap &union_add(const PVMap &PM); + PVMap &floordiv(int64_t V); + + std::string str() const; +}; + +class PVAff : public PVBase { + friend class PVMap; + friend class PVSet; + + isl_pw_aff *Obj; + + operator isl_pw_aff *() const; + +protected: + isl_ctx *getIslCtx() const; + isl_space *getSpace() const; + +public: + + /// Constructors + ///{ + PVAff() : Obj(nullptr) {} + PVAff(isl_pw_aff *Obj) : Obj(Obj) {} + PVAff(const PVAff &Other); + PVAff(PVAff &&Other) : Obj(Other.Obj) { Other.Obj = nullptr; } + PVAff(const PVId &Id); + PVAff(const PVBase &Base, int64_t ConstVal); + PVAff(const PVSet &S); + PVAff(const PVSet &S, int64_t ConstVal); + PVAff(const PVBase &Base, unsigned CoeffPos, int64_t CoeffVal, const PVId &Id); + ///} + + ~PVAff(); + + bool operator==(const PVAff &Other); + bool operator!=(const PVAff &Other) { return !(*this == Other); } + + PVAff &operator=(const PVAff &Other); + PVAff &operator=(PVAff &&Other); + operator bool() const { return Obj != nullptr; } + + /// Return the number of input dimensions of this function. + size_t getNumInputDimensions() const; + + /// Return the number of output dimensions of this function. + size_t getNumOutputDimensions() const; + + /// Return the number of parameters of this function. + size_t getNumParameters() const; + + /// Return the number of pieces + size_t getNumPieces() const; + + bool isComplex() const; + bool isConstant() const; + + int getParameterPosition(const PVId &Id) const; + bool involvesId(const PVId &Id) const; + + void addInputDims(unsigned Dims); + void insertInputDims(unsigned Pos, unsigned Dims); + void dropInputDim(unsigned Dim); + void dropLastInputDims(unsigned Dims); + void dropParameter(const PVId &Id); + void dropUnusedParameters(); + + PVId getParameter(unsigned No) const; + void getParameters(SmallVectorImpl &Parameters) const; + void getParameters(SmallVectorImpl &Parameters) const; + + PVAff extractFactor(const PVAff &Aff) const; + int getFactor(const PVAff &Aff) const; + + PVAff &add(const PVAff &PV); + PVAff &sub(const PVAff &PV); + PVAff &multiply(const PVAff &PV); + PVAff &union_add(const PVAff &PV); + + PVAff &select(const PVAff &PV0, const PVAff &PV1); + + PVAff &fixParamDim(unsigned Dim, int64_t Value); + PVAff &fixInputDim(unsigned Dim, int64_t Value); + PVAff &setInputLowerBound(unsigned Dim, int64_t Value); + PVAff &floordiv(int64_t V); + + PVSet zeroSet() const; + PVSet nonZeroSet() const; + + PVAff getParameterCoeff(const PVId &Id); + PVAff perPiecePHIEvolution(const PVId &Id, int LD) const; + + /// Use the intersection of the domain with the set @p S as new domain. + /// + /// @param Dom The set to intersect the domain with. + /// + /// @returns A reference to this object (*this). + PVAff &intersectDomain(const PVSet &Dom); + + PVAff &simplify(PVSet &S); + + PVSet getLessThanDomain(const PVAff &Aff) const; + PVSet getLessEqualDomain(const PVAff &Aff) const; + PVSet getGreaterEqualDomain(const PVAff &Aff) const; + PVSet getDomain() const; + + static PVAff getExpPWA(const PVAff &PWA); + static PVAff createAdd(const PVAff &LHS, const PVAff &RHS); + static PVAff createUnionAdd(const PVAff &LHS, const PVAff &RHS); + static PVAff createSub(const PVAff &LHS, const PVAff &RHS); + static PVAff createMultiply(const PVAff &LHS, const PVAff &RHS); + static PVAff createSDiv(const PVAff &LHS, const PVAff &RHS); + static PVAff createShiftLeft(const PVAff &LHS, const PVAff &RHS); + static PVAff createSelect(const PVAff &CondPV, const PVAff &TruePV, + const PVAff &FalsePV); + + /// Create the conditions under which @p L @p Pred @p R is true. + static PVSet buildConditionSet(ICmpInst::Predicate Pred, const PVAff &PV0, + const PVAff &PV1); + + /// The function type that is needed to combine two polyhedral + /// representations. + using IslCombinatorFn = std::function; + using CombinatorFn = std::function; + static CombinatorFn getCombinatorFn(IslCombinatorFn Fn); + + std::string str() const; +}; + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PVBase &PV); + +} // end namespace llvm + +#endif // LLVM_SUPPORT_PVALUE_H Index: include/llvm/Analysis/Passes.h =================================================================== --- include/llvm/Analysis/Passes.h +++ include/llvm/Analysis/Passes.h @@ -96,6 +96,12 @@ // FunctionPass *createMemDerefPrinter(); + //===--------------------------------------------------------------------===// + // + // createPolyhedralValueInfoWrapperPass - This pass offers a polyhedral-based + // value analysis. + // + FunctionPass *createPolyhedralValueInfoWrapperPass(); } #endif Index: include/llvm/Analysis/PolyhedralValueInfo.h =================================================================== --- /dev/null +++ include/llvm/Analysis/PolyhedralValueInfo.h @@ -0,0 +1,437 @@ +//===--- PolyhedralValueInfo.h -- Polyhedral value analysis -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Analysis to create polyhedral abstractions for values, instructions and +// iteration domains. These abstractions are parametric, piece-wise affine +// functions with loop-iteration granularity. +// +// Parts of the code and ideas have been ported from the Polly [0] project. +// +// [0] http://polly.llvm.org/ +// +//===----------------------------------------------------------------------===// + +#ifndef POLYHEDRAL_VALUE_INFO_H +#define POLYHEDRAL_VALUE_INFO_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/PValue.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class PolyhedralValueInfo; +class PolyhedralExpressionBuilder; + +/// Polyhedral representation of a value (or basic block) in a scope. The values +/// are expressed with regards to loop iterations. The scope defines which loop +/// iterations are represented explicitly and which are kept parametric. +/// +/// For the value of j in following two dimensional loop nest there are three +/// different PEXP values depending on the scope. +/// +/// for (i = 0; i < N; j++) +/// for (j = i; j < 2*i; j++) +/// S(i, j); +/// +/// Scope | Polyhedral representation of j | Description +/// ---------------------------------------------------------------------------- +/// j-loop | [j] -> { [] -> [(j)] } | Unconstrained parametric value +/// | | in one iteration of the j-loop +/// i-loop | [i] -> { [l1] -> [(i + l1)] } | Parametric value of i plus the +/// | | current loop iteration (j) of +/// | | the innermost loop +/// max/none | [] -> { [l0, l1] -> [(l0 + l1)]} | Sum of the current iterations +/// | | in the i-loop and j-loop +/// /\ /\ /\ +/// || || || +/// parameters || value +/// loop iterations +/// +/// The domain of S in the above example also depends on the scope and can be +/// one of the following: +/// +/// Scope | Polyhedral representation of the domain of S +/// ---------------------------------------------------------------------------- +/// j-loop | [] -> { [] -> [(1)] } +/// i-loop | [i] -> { [l0] -> [(1)] : 0 <= i0 < i } +/// max/none | [N] -> { [l0, l1] -> [(1)] : 0 <= i1 < i0 and i0 < N } +/// +/// /\ /\ /\ /\ +/// || || || || +/// parameters || (fixed) value || +/// loop iterations constraints +/// +/// Note that iterations are always "normalized", thus they are expressed as a +/// range from 0 to the number of iterations. The value part of the polyhedral +/// representation will compensate for non-zero initial values or strides not +/// equal to one. +/// +/// If a scope loop is given, the PEXP will not contain information about any +/// expressions outside that loop. Otherwise, the PEXP will represent all +/// expressions that influence the value in question until the representation +/// would become non-affine. Instead of a non-affine (thus invalid) result the +/// non-affine part is represented as a parameter. +/// +/// TODO: Describe invalid and known domain. +/// +class PEXP { +public: + /// The possible kinds for a polyhedral expression. + enum ExpressionKind { + EK_NONE, ///<= An unspecified, not yet initialized, value. + EK_INTEGER, ///<= A (piece-wise defined) integer value. + EK_UNKNOWN_VALUE, ///<= An unknown (parametric) or changing (loop iv) value. + EK_DOMAIN, ///<= A domain value that evaluates to one if the basic + /// block is executed. + EK_NON_AFFINE, ///<= A non-affine, thus invalid, value. + }; + + /// Create a new, uninitialized polyhedral expression for @p Val. + PEXP(Value *Val, Loop *Scope) : Kind(EK_NONE), Val(Val), Scope(Scope) { + assert(Val); + } + + /// Return the value this PEXP represents. + Value *getValue() const { return Val; } + + /// Return the scope in which this PEXP is expressed. + Loop *getScope() const { return Scope; } + + /// Assign the combination of @p PE0 and @p PE1 using @p Combinator. + PEXP *assign(const PEXP *PE0, const PEXP *PE1, + PVAff::CombinatorFn Combinator); + PEXP *assign(const PEXP *PE0, const PEXP *PE1, + PVAff::IslCombinatorFn Combinator); + + /// Return the polyhedral piece-wise affine function. + const PVAff &getPWA() const { return PWA; } + PVAff &getPWA() { return PWA; } + + /// Return the domain for which this PEXP is defined. + PVSet getDomain() const { return getPWA().getDomain(); } + + /// Return the invalid domain which this PEXP is undefined. + const PVSet &getInvalidDomain() const { return InvalidDomain; } + + /// Return the known constraints about this PEXP. + const PVSet &getKnownDomain() const { return KnownDomain; } + + /// Return the expression kind of this PEXP. + ExpressionKind getKind() const { return this->Kind; } + + /// Return true if this polyhedral representation has been initialized. + bool isInitialized() const { return Kind != EK_NONE; } + + /// Print this polyhedral representation to @p OS. + void print(raw_ostream &OS) const; + + /// Dump this polyhedral representation to the dbgs() stream. + void dump() const; + +private: + /// Assignment and move-assignment operator. + PEXP &operator=(PEXP &&PE); + PEXP &operator=(const PEXP &PE); + + /// The kind of this polyhedral expression. + ExpressionKind Kind; + + /// The llvm value that is represented by this polyhedral expression. + Value *const Val; + + /// The scope of this polyhedral expression. + Loop *const Scope; + + /// The value represented as (p)iece-(w)ise (a)ffine function. + PVAff PWA; + + /// The domain under which the representation is invalid. + PVSet InvalidDomain; + + /// The domain containing known constraints. + PVSet KnownDomain; + + /// Combine the invalid and known domain of @p PE with this PEXP. + /// + /// @param PE The polyhedral representation to combine. + PEXP *combine(const PEXP *PE); + + /// Combine the polyhedral representation @p PE restricted to the domain + /// @p Domain into this one using @p Combinator as a combinator function. + /// + /// @param PE The polyhedral representation to combine. + /// @param Combinator The combinator function. + /// @param Domain The domain for which @p PEXP will be combined. + PEXP *combine(const PEXP *PE, PVAff::CombinatorFn Combinator, + const PVSet *Domain = nullptr); + PEXP *combine(const PEXP *PE, PVAff::IslCombinatorFn Combinator, + const PVSet *Domain = nullptr); + + /// Include the constraints in @p ID in the invalid domain. + void addInvalidDomain(const PVSet &ID); + + /// Include the constraints in @p KD in the known domain. + void addKnownDomain(const PVSet &KD); + + /// Assign @p Domain as the domain of this PEXP and change the kind to + /// EK_DOMAIN. + PEXP *setDomain(const PVSet &Domain, bool Overwrite = false); + + /// Set the expression kind to @p Kind. + void setKind(ExpressionKind Kind) { this->Kind = Kind; } + + /// Add the overflow behaviour to the invalid (or known) domain depending on + /// the nsw tag of the underlying computation. + void adjustInvalidAndKnownDomain(); + + /// Invalidate this PEXP. + PEXP *invalidate(); + + friend class PolyhedralValueInfo; + friend class PolyhedralExpressionBuilder; +}; + +/// A cache that maps values/basic blocks and scopes to the computed polyhedral +/// representation (PEXP). The results for maximal scopes (nullptr) can be used +/// in an inter-procedural setting. +class PolyhedralValueInfoCache final { + + /// Mapping from basic blocks to their domain expressed as a PEXP. + using DomainMapKey = std::pair; + DenseMap DomainMap; + + /// Mapping from values to their polyhedral representation. + using ValueMapKey = std::pair; + DenseMap ValueMap; + + /// Mapping from parameter values to their unique id. + DenseMap ParameterMap; + + /// Return or create and cache a PEXP for @p BB in @p Scope. + PEXP *getOrCreateDomain(BasicBlock &BB, Loop *Scope) { + auto *&PE = DomainMap[{&BB, Scope}]; + if (!PE) + PE = new PEXP(&BB, Scope); + + // Verify the internal state + assert(PE == lookup(BB, Scope)); + return PE; + } + + /// Return or create and cache a PEXP for @p V in @p Scope. + PEXP *getOrCreatePEXP(Value &V, Loop *Scope) { + auto *&PE = ValueMap[{&V, Scope}]; + if (!PE) + PE = new PEXP(&V, Scope); + + // Verify the internal state + assert(PE == lookup(V, Scope)); + return PE; + } + + /// Return the unique parameter id for @p V. + PVId getParameterId(Value &V, const PVCtx &Ctx); + + friend class PolyhedralExpressionBuilder; + +public: + ~PolyhedralValueInfoCache() { releaseMemory(); } + + /// Return the cached polyhedral representation of @p V in @p Scope, if any. + PEXP *lookup(Value &V, Loop *Scope) { return ValueMap.lookup({&V, Scope}); } + + /// Return the cached polyhedral representation of @p BB in @p Scope, if any. + PEXP *lookup(BasicBlock &BB, Loop *Scope) { + return DomainMap.lookup({&BB, Scope}); + } + + /// Forget the value for @p BB in @p Scope. Returns true if there was one. + bool forget(BasicBlock &BB, Loop *Scope) { + return DomainMap.erase({&BB, Scope}); + } + + /// Forget the value for @p V in @p Scope. Returns true if there was one. + bool forget(Value &V, Loop *Scope) { + return ValueMap.erase({&V, Scope}); + } + + /// Iterators for polyhedral representation of values. + ///{ + using iterator = decltype(ValueMap)::iterator; + using const_iterator = decltype(ValueMap)::const_iterator; + + iterator begin() { return ValueMap.begin(); } + iterator end() { return ValueMap.end(); } + const_iterator begin() const { return ValueMap.begin(); } + const_iterator end() const { return ValueMap.end(); } + + iterator_range values() { return make_range(begin(), end()); } + iterator_range values() const { + return make_range(begin(), end()); + } + ///} + + /// Iterators for polyhedral domains of basic block. + ///{ + using domain_iterator = decltype(DomainMap)::iterator; + using const_domain_iterator = decltype(DomainMap)::const_iterator; + + domain_iterator domain_begin() { return DomainMap.begin(); } + domain_iterator domain_end() { return DomainMap.end(); } + const_domain_iterator domain_begin() const { return DomainMap.begin(); } + const_domain_iterator domain_end() const { return DomainMap.end(); } + + iterator_range domains() { + return make_range(domain_begin(), domain_end()); + } + iterator_range domains() const { + return make_range(domain_begin(), domain_end()); + } + ///} + + /// Clear all cached information. + void releaseMemory(); +}; + +/// Analysis to create polyhedral abstractions for values, instructions and +/// iteration domains. +class PolyhedralValueInfo { + /// The (shared) context in which the polyhedral values are build. + PVCtx Ctx; + + /// The loop information for the function we are currently analyzing. + LoopInfo &LI; + + /// The polyhedral expression builder. + PolyhedralExpressionBuilder *PEBuilder; + + friend class PolyhedralExpressionBuilder; + +public: + /// Constructor + PolyhedralValueInfo(PVCtx Ctx, LoopInfo &LI); + + ~PolyhedralValueInfo(); + + /// Return the polyhedral expression of @p V in @p Scope. + const PEXP *getPEXP(Value *V, Loop *Scope = nullptr); + + /// Return the domain of @p BB as polyhedral expression in @p Scope. + const PEXP *getDomainFor(BasicBlock *BB, Loop *Scope = nullptr); + + /// Return the internal context used. + const PVCtx &getCtx() const { return Ctx; } + + /// Return the unique parameter id for @p V. + PVId getParameterId(Value &V); + + /// Return true if @p PE represents an unknown (parametric) value. + bool isUnknown(const PEXP *PE) const; + + /// Return true if @p PE represents an integer value. + /// + /// As PEXP allow for piecewise representation, integer does not necessarily + /// mean constant. An example would be + /// %int = phi i32 [5, %b0], [7, %b1] + /// which is an integer value but not a constant one. + bool isInteger(const PEXP *PE) const; + + /// Return true if @p PE represents a constant value. + bool isConstant(const PEXP *PE) const; + + /// Return true if @p PE represents an affine value. + bool isAffine(const PEXP *PE) const; + + /// Return true if @p PE represents a non-affine value. + bool isNonAffine(const PEXP *PE) const; + + /// Return true if @p PE represents a value that is fixed for one iteration of + /// @p Scope. Thus @p PE is not parametric in an instruction in @p Scope. + bool hasScope(const PEXP *PE, Loop *Scope) const; + + /// Return true if @p PE represents a value that is fixed for one function + /// invocation, thus @p PE is not parametric in any llvm::Instruction. + bool hasFunctionScope(const PEXP *PE) const; + + /// Return the unknown ids referenced by @p PE in @p Values. + void getParameters(const PEXP *PE, SmallVectorImpl &Values) const; + + /// Return the unknown values referenced by @p PE in @p Values. + void getParameters(const PEXP *PE, SmallVectorImpl &Values) const; + + /// Return true if the @p Pred relation between @p LHS and @p RHS is known to + /// hold at @p IP with regards to @p Scope. + /// + /// TODO: Specify constrains on @p IP wrt. @p LHS and @p RHS. + bool isKnownToHold(Value *LHS, Value *RHS, ICmpInst::Predicate Pred, + Instruction *IP = nullptr, Loop *Scope = nullptr); + + /// Print some statistics to @p OS. + void print(raw_ostream &OS) const; +}; + +/// Wrapper pass for PolyhedralValueInfo on a per-function basis. +class PolyhedralValueInfoWrapperPass : public FunctionPass { + /// The (shared) context in which the polyhedral values are build. + PVCtx Ctx; + + PolyhedralValueInfo *PI; + Function *F; + +public: + static char ID; + PolyhedralValueInfoWrapperPass() + : FunctionPass(ID), PI(nullptr), F(nullptr) {} + + /// Return the PolyhedralValueInfo object for the current function. + PolyhedralValueInfo &getPolyhedralValueInfo() { + assert(PI); + return *PI; + } + + /// @name Pass interface + //@{ + virtual void getAnalysisUsage(AnalysisUsage &AU) const override; + virtual void releaseMemory() override; + virtual bool runOnFunction(Function &F) override; + //@} + + virtual void print(raw_ostream &OS, const Module *) const override; + void dump() const; +}; + +/// Analysis wrapper for the PolyhedralValueInfo in the new pass manager. +class PolyhedralValueInfoAnalysis + : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + + /// The (shared) context in which the polyhedral values are build. + PVCtx Ctx; + +public: + /// \brief Provide the result typedef for this analysis pass. + typedef PolyhedralValueInfo Result; + + /// \brief Run the analysis pass over a function and produce BFI. + Result run(Function &F, FunctionAnalysisManager &AM); +}; + +/// Stream operators to pretty print polyhedral expressions (PEXP) +/// +///{ +raw_ostream &operator<<(raw_ostream &OS, PEXP::ExpressionKind Kind); +raw_ostream &operator<<(raw_ostream &OS, const PEXP *PE); +raw_ostream &operator<<(raw_ostream &OS, const PEXP &PE); +///} + +} // namespace llvm +#endif Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -285,6 +285,7 @@ void initializePhysicalRegisterUsageInfoPass(PassRegistry&); void initializePlaceBackedgeSafepointsImplPass(PassRegistry&); void initializePlaceSafepointsPass(PassRegistry&); +void initializePolyhedralValueInfoWrapperPassPass(PassRegistry&); void initializePostDomOnlyPrinterPass(PassRegistry&); void initializePostDomOnlyViewerPass(PassRegistry&); void initializePostDomPrinterPass(PassRegistry&); Index: include/llvm/LinkAllPasses.h =================================================================== --- include/llvm/LinkAllPasses.h +++ include/llvm/LinkAllPasses.h @@ -27,6 +27,7 @@ #include "llvm/Analysis/IntervalPartition.h" #include "llvm/Analysis/Lint.h" #include "llvm/Analysis/Passes.h" +#include "llvm/Analysis/PolyhedralValueInfo.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/RegionPass.h" #include "llvm/Analysis/RegionPrinter.h" @@ -141,6 +142,7 @@ (void) llvm::createPromoteMemoryToRegisterPass(); (void) llvm::createDemoteRegisterToMemoryPass(); (void) llvm::createPruneEHPass(); + (void) llvm::createPolyhedralValueInfoWrapperPass(); (void) llvm::createPostDomOnlyPrinterPass(); (void) llvm::createPostDomPrinterPass(); (void) llvm::createPostDomOnlyViewerPass(); Index: lib/Analysis/Analysis.cpp =================================================================== --- lib/Analysis/Analysis.cpp +++ lib/Analysis/Analysis.cpp @@ -67,6 +67,7 @@ initializeModuleSummaryIndexWrapperPassPass(Registry); initializeObjCARCAAWrapperPassPass(Registry); initializeOptimizationRemarkEmitterWrapperPassPass(Registry); + initializePolyhedralValueInfoWrapperPassPass(Registry); initializePostDominatorTreeWrapperPassPass(Registry); initializeRegionInfoPassPass(Registry); initializeRegionViewerPass(Registry); Index: lib/Analysis/CMakeLists.txt =================================================================== --- lib/Analysis/CMakeLists.txt +++ lib/Analysis/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(isl) + add_llvm_library(LLVMAnalysis AliasAnalysis.cpp AliasAnalysisEvaluator.cpp @@ -64,9 +66,11 @@ OptimizationDiagnosticInfo.cpp OrderedBasicBlock.cpp PHITransAddr.cpp + PolyhedralValueInfo.cpp PostDominators.cpp ProfileSummaryInfo.cpp PtrUseVisitor.cpp + PValue.cpp RegionInfo.cpp RegionPass.cpp RegionPrinter.cpp Index: lib/Analysis/LLVMBuild.txt =================================================================== --- lib/Analysis/LLVMBuild.txt +++ lib/Analysis/LLVMBuild.txt @@ -15,8 +15,12 @@ ; ;===------------------------------------------------------------------------===; +[common] +subdirectories = + isl + [component_0] type = Library name = Analysis parent = Libraries -required_libraries = BinaryFormat Core Object ProfileData Support +required_libraries = BinaryFormat Core Object ProfileData Support isl Index: lib/Analysis/PValue.cpp =================================================================== --- /dev/null +++ lib/Analysis/PValue.cpp @@ -0,0 +1,1195 @@ +//===- PValue.cpp ------ isl C++ interface wrapper ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// TODO Add comments to this implementation! +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/PValue.h" + +#include "isl/aff.h" +#include "isl/id.h" +#include "isl/map.h" +#include "isl/set.h" +#include "isl/constraint.h" +#include "isl/options.h" + +#define DEBUG_TYPE "pvalue" + +using namespace llvm; + +static int DOMAIN_N_BASIC_SEK_TRESHOLD = 10; +static int PWA_N_PIECE_TRESHOLD = 4; + + +static void replace(std::string &str, const std::string &find, + const std::string &replace) { + size_t pos = 0; + while ((pos = str.find(find, pos)) != std::string::npos) { + str.replace(pos, find.length(), replace); + pos += replace.length(); + } +} + +static void makeIslCompatible(std::string &str) { + replace(str, ".", "_"); + replace(str, "\"", "_"); + replace(str, " ", "__"); + replace(str, "=>", "TO"); +} + +/// Create a map to map from a given iteration to a subsequent iteration. +/// +/// This map maps from SetSpace -> SetSpace where the dimensions @p Dim +/// is incremented by one and all other dimensions are equal, e.g., +/// [i0, i1, i2, i3] -> [i0, i1, i2 + 1, i3] +/// +/// if @p Dim is 2 and @p SetSpace has 4 dimensions. +static __isl_give isl_map * +createNextIterationMap(__isl_take isl_space *SetSpace, unsigned Dim, bool All) { + auto *MapSpace = isl_space_map_from_set(SetSpace); + auto *NextIterationMap = isl_map_universe(isl_space_copy(MapSpace)); + for (unsigned u = 0; u < isl_map_dim(NextIterationMap, isl_dim_in); u++) + if (u != Dim) + NextIterationMap = + isl_map_equate(NextIterationMap, isl_dim_in, u, isl_dim_out, u); + isl_constraint *C; + if (All) { + C = isl_constraint_alloc_inequality(isl_local_space_from_space(MapSpace)); + C = isl_constraint_set_constant_si(C, -1); + C = isl_constraint_set_coefficient_si(C, isl_dim_in, Dim, -1); + C = isl_constraint_set_coefficient_si(C, isl_dim_out, Dim, 1); + } else { + C = isl_constraint_alloc_equality(isl_local_space_from_space(MapSpace)); + C = isl_constraint_set_constant_si(C, 1); + C = isl_constraint_set_coefficient_si(C, isl_dim_in, Dim, 1); + C = isl_constraint_set_coefficient_si(C, isl_dim_out, Dim, -1); + } + NextIterationMap = isl_map_add_constraint(NextIterationMap, C); + return NextIterationMap; +} + +std::string PVBase::getIslCompatibleName(const std::string &Prefix, + const std::string &Middle, + const std::string &Suffix) { + std::string S = Prefix + Middle + Suffix; + makeIslCompatible(S); + return S; +} + +std::string PVBase::str() const { + llvm_unreachable("PVBase::str() should be overloaded!"); +} + +/* -------------------- PVCtx ------------------------- */ + +PVCtx::PVCtx() : PVCtx(isl_ctx_alloc()) { + isl_options_set_on_error(getIslCtx(), ISL_ON_ERROR_ABORT); +} + +isl_space *PVCtx::getSpace() const { + return isl_space_set_alloc(getIslCtx(), 0, 0); +} + +std::string PVCtx::str() const { + return "PVCTX"; +} + +/* -------------------- PVId ------------------------- */ + +PVId::PVId(const PVBase &Base, const std::string &Name, void *Payload) + : Obj(isl_id_alloc(Base.getIslCtx(), Name.c_str(), Payload)) {} +PVId::PVId(const PVId &Other) : Obj(isl_id_copy(Other.Obj)) {} +PVId::PVId(PVId &&Other) : Obj(Other.Obj) { Other.Obj = nullptr; } + +PVId::~PVId() { isl_id_free(Obj); } + +PVId &PVId::operator=(const PVId &Other) { + if (this != &Other) { + isl_id_free(Obj); + Obj = isl_id_copy(Other.Obj); + } + return *this; +} + +PVId &PVId::operator=(PVId &&Other) { + if (this != &Other) { + Obj = isl_id_free(Obj); + std::swap(Obj, Other.Obj); + } + return *this; +} + +PVId::operator isl_id *() const { return isl_id_copy(Obj); } + +void *PVId::getPayload() const { + return isl_id_get_user(Obj); +} + +isl_ctx *PVId::getIslCtx() const { return isl_id_get_ctx(Obj); } + +isl_space *PVId::getSpace() const { + return isl_space_set_alloc(getIslCtx(), 0, 0); +} + +std::string PVId::str() const { + const char *cstr = isl_id_get_name(Obj); + if (!cstr) + return "null"; + std::string Result(cstr); + return Result; +} + +/* -------------------- PVSet ------------------------ */ + +PVSet::PVSet(isl_set *S) : Obj(S) {} +PVSet::PVSet(const PVSet &Other) : Obj(isl_set_copy(Other.Obj)) {} + +PVSet PVSet::createParameterRange(const PVId &Id, int LowerBound, + int UpperBound) { + PVSet ParamRange = universe(Id); + ParamRange.Obj = isl_set_add_dims(ParamRange.Obj, isl_dim_param, 1); + ParamRange.Obj = isl_set_set_dim_id(ParamRange.Obj, isl_dim_param, 0, Id); + ParamRange.Obj = + isl_set_lower_bound_si(ParamRange.Obj, isl_dim_param, 0, LowerBound); + ParamRange.Obj = + isl_set_upper_bound_si(ParamRange.Obj, isl_dim_param, 0, UpperBound); + return ParamRange; +} + +PVSet::~PVSet() { isl_set_free(Obj); } + +PVSet &PVSet::operator=(const PVSet &Other) { + if (this != &Other) { + isl_set_free(Obj); + Obj = isl_set_copy(Other.Obj); + } + return *this; +} + +PVSet &PVSet::operator=(PVSet &&Other) { + if (this != &Other) { + Obj = isl_set_free(Obj); + std::swap(Obj, Other.Obj); + } + return *this; +} + +PVSet::operator isl_set *() const { return isl_set_copy(Obj); } + +isl_ctx *PVSet::getIslCtx() const { return isl_set_get_ctx(Obj); } + +isl_space *PVSet::getSpace() const { return isl_set_get_space(Obj); } + +size_t PVSet::getNumDimensions() const { return isl_set_n_dim(Obj); } + +size_t PVSet::getNumParameters() const { return isl_set_n_param(Obj); } + +bool PVSet::isEmpty() const { return isl_set_is_empty(Obj); } +bool PVSet::isUniverse() const { + isl_set *C = isl_set_complement(isl_set_copy(Obj)); + bool U = isl_set_is_empty(C); + isl_set_free(C); + return U; +} +bool PVSet::isBounded() const { return isl_set_is_bounded(Obj); } + +int PVSet::getParameterPosition(const PVId &Id) const { + int Pos = isl_set_find_dim_by_id(Obj, isl_dim_param, Id); + return Pos; +} + +bool PVSet::hasLowerBoundForParam(const PVId &Id) { + int Pos = getParameterPosition(Id); + if (Pos < 0) + return false; + return isl_set_dim_has_any_lower_bound(Obj, isl_dim_param, Pos); +} + +PVSet &PVSet::minForParam(const PVId &Id) { + int Pos = getParameterPosition(Id); + assert(Pos >= 0); + assert(isl_set_is_params(Obj)); + Obj = isl_set_move_dims(Obj, isl_dim_set, 0, isl_dim_param, Pos, 1); + Obj = isl_set_lexmin(Obj); + return *this; +} + +void PVSet::dropUnusedParameters() { + size_t NumParams = getNumParameters(); + for (size_t i = 0; i < NumParams; i++) { + if (isl_set_involves_dims(Obj, isl_dim_param, i, 1)) + continue; + + Obj = isl_set_project_out(Obj, isl_dim_param, i, 1); + i--; + NumParams--; + } +} + +PVSet &PVSet::simplify(const PVSet &S) { + Obj = isl_set_gist(Obj, S); + return *this; +} + +PVSet &PVSet::simplifyParameters(const PVSet &S) { + Obj = isl_set_gist_params(Obj, S); + return *this; +} + +PVSet &PVSet::complement() { + Obj = isl_set_complement(Obj); + Obj = isl_set_coalesce(Obj); + return *this; +} + +static void unifySetDimensions(isl_set *&S0, isl_set *&S1) { + int DimDiff = isl_set_n_dim(S0) - isl_set_n_dim(S1); + if (DimDiff > 0) + S1 = isl_set_add_dims(S1, isl_dim_set, DimDiff); + else if (DimDiff < 0) + S0 = isl_set_add_dims(S0, isl_dim_set, -1 * DimDiff); + // S0 = isl_set_align_params(S0, isl_set_get_space(S1)); + // S1 = isl_set_align_params(S1, isl_set_get_space(S0)); +} + +PVSet &PVSet::unify(const PVSet &S) { + if (!Obj) + Obj = isl_set_copy(S.Obj); + else if (S.Obj) { + isl_set *SObj = S; + unifySetDimensions(Obj, SObj); + Obj = isl_set_union(Obj, SObj); + Obj = isl_set_coalesce(Obj); + } + return *this; +} + +PVSet &PVSet::subtract(const PVSet &S) { + if (!Obj) + Obj = isl_set_copy(S.Obj); + else if (S.Obj) { + isl_set *SObj = S; + unifySetDimensions(Obj, SObj); + Obj = isl_set_subtract(Obj, SObj); + Obj = isl_set_coalesce(Obj); + } + return *this; +} + +PVSet &PVSet::intersect(const PVSet &S) { + if (!Obj) + Obj = isl_set_copy(S.Obj); + else if (S.Obj) { + isl_set *SObj = S; + if (isl_set_is_params(SObj)) { + Obj = isl_set_intersect_params(Obj, SObj); + } else { + unifySetDimensions(Obj, SObj); + Obj = isl_set_intersect(Obj, SObj); + } + Obj = isl_set_coalesce(Obj); + } + return *this; +} + +PVSet PVSet::empty(const PVBase &Base) { + auto *Space = Base.getSpace(); + if (isl_space_is_map(Space)) + Space = isl_space_domain(Space); + return isl_set_empty(Space); +} + +PVSet PVSet::empty(const PVBase &Base, unsigned Dims) { + auto *Space = isl_space_set_alloc(Base.getIslCtx(), 0, Dims); + return isl_set_empty(Space); +} + +PVSet PVSet::universe(const PVBase &Base) { + auto *Space = Base.getSpace(); + if (isl_space_is_map(Space)) + Space = isl_space_domain(Space); + return isl_set_universe(Space); +} + +PVSet PVSet::unify(const PVSet &S0, const PVSet &S1) { + return PVSet(S0).unify(S1); +} + +PVSet PVSet::intersect(const PVSet &S0, const PVSet &S1) { + return PVSet(S0).intersect(S1); +} + +PVSet &PVSet::maxInLastInputDims(unsigned Dims) { + if (Dims == 0) + return *this; + unsigned NumDims = getNumDimensions(); + assert(NumDims >= Dims); + Obj = isl_set_project_out(Obj, isl_dim_set, 0, NumDims - Dims); + Obj = isl_set_lexmax(Obj); + Obj = isl_set_insert_dims(Obj, isl_dim_set, 0, NumDims - Dims); + return *this; +} + +PVSet &PVSet::fixInputDim(unsigned Dim, int64_t Value) { + auto Dims = getNumDimensions(); + if (Dims <= Dim) + Obj = isl_set_add_dims(Obj, isl_dim_set, Dim - Dims + 1); + Obj = isl_set_fix_si(Obj, isl_dim_set, Dim, Value); + Obj = isl_set_coalesce(Obj); + return *this; +} + +PVSet &PVSet::equateInputDim(unsigned Dim, const PVId &Id) { + int Pos = isl_set_find_dim_by_id(Obj, isl_dim_param, Id); + assert(Pos >= 0); + Obj = isl_set_equate(Obj, isl_dim_param, Pos, isl_dim_set, Dim); + return *this; +} + +PVSet &PVSet::addInputDims(unsigned Dims) { + Obj = isl_set_add_dims(Obj, isl_dim_set, Dims); + return *this; +} + +PVSet &PVSet::dropFirstInputDims(unsigned Dims) { + Obj = isl_set_project_out(Obj, isl_dim_set, 0, Dims); + return *this; +} + +PVSet &PVSet::dropLastInputDims(unsigned Dims) { + Obj = isl_set_eliminate_dims(Obj, getNumDimensions() - Dims, Dims); + Obj = isl_set_coalesce(Obj); + return *this; +} + +PVSet &PVSet::dropDimsFrom(unsigned Dim) { + if (Dim < getNumDimensions()) + Obj = isl_set_project_out(Obj, isl_dim_set, Dim, getNumDimensions() - Dim); + return *this; +} + +PVSet &PVSet::setInputLowerBound(unsigned Dim, int64_t Value) { + auto Dims = getNumDimensions(); + if (Dims <= Dim) + Obj = isl_set_add_dims(Obj, isl_dim_set, Dim - Dims + 1); + Obj = isl_set_lower_bound_si(Obj, isl_dim_set, Dim, Value); + Obj = isl_set_coalesce(Obj); + return *this; +} + +PVSet &PVSet::preimage(const PVAff &PWA) { + Obj = isl_set_preimage_pw_multi_aff(Obj, isl_pw_multi_aff_from_pw_aff(PWA)); + return *this; +} + +PVSet &PVSet::projectParameter(const PVId &Id) { + int Pos = isl_set_find_dim_by_id(Obj, isl_dim_param, Id); + assert(Pos >= 0); + Obj = isl_set_project_out(Obj, isl_dim_param, Pos, 1); + return *this; +} + +PVSet &PVSet::getNextIteration(unsigned Dim) { + isl_map *NIMap = createNextIterationMap(getSpace(), Dim, false); + Obj = isl_set_apply(Obj, NIMap); + return *this; +} + +PVSet &PVSet::getNextIterations(unsigned Dim) { + isl_map *NIMap = createNextIterationMap(getSpace(), Dim, true); + Obj = isl_set_apply(Obj, NIMap); + return *this; +} + +PVId PVSet::getParameter(unsigned No) const { + return PVId(isl_set_get_dim_id(Obj, isl_dim_param, No)); +} + +void PVSet::getParameters(SmallVectorImpl &Parameters) const { + size_t NumParams = getNumParameters(); + Parameters.reserve(Parameters.size() + NumParams); + for (size_t i = 0; i < NumParams; i++) { + if (isl_set_involves_dims(Obj, isl_dim_param, i, 1)) + Parameters.push_back(getParameter(i)); + } +} + +void PVSet::getParameters(SmallVectorImpl &Parameters) const { + size_t NumParams = getNumParameters(); + Parameters.reserve(Parameters.size() + NumParams); + for (size_t i = 0; i < NumParams; i++) + if (isl_set_involves_dims(Obj, isl_dim_param, i, 1)) + Parameters.push_back(getParameter(i).getPayloadAs()); +} + +std::string PVSet::str() const { + char *cstr = isl_set_to_str(Obj); + if (!cstr) + return "null"; + std::string Result(cstr); + ::free(cstr); + return Result; +} + +/* -------------------- PVMap ----------------------- */ +static void adjustDimensionsPlain(isl_map *&Map0, isl_map *&Map1) { + int DimIn0 = isl_map_dim(Map0, isl_dim_in); + int DimIn1 = isl_map_dim(Map1, isl_dim_in); + if (DimIn0 == DimIn1) + return; + + auto *&Map = (DimIn0 > DimIn1) ? Map1 : Map0; + Map = isl_map_add_dims(Map, isl_dim_in, std::abs(DimIn0 - DimIn1)); + + assert(isl_map_dim(Map0, isl_dim_in) == isl_map_dim(Map1, isl_dim_in)); + assert(isl_map_dim(Map0, isl_dim_out) == isl_map_dim(Map1, isl_dim_out)); +} + + +PVMap::PVMap(isl_map *M) : Obj(M) {} +PVMap::PVMap(const PVMap &Other) : Obj(isl_map_copy(Other.Obj)) {} +PVMap::PVMap(ArrayRef Affs, const PVId &Id) { + if (Affs.empty()) + return; + + const PVAff &Aff = Affs.front(); + isl_space *Space = isl_space_alloc(Aff.getIslCtx(), 0, + Aff.getNumInputDimensions(), Affs.size()); + isl_pw_multi_aff *MPWA = isl_pw_multi_aff_zero(Space); + for (unsigned i = 0; i < Affs.size(); i++) + MPWA = isl_pw_multi_aff_set_pw_aff(MPWA, i, Affs[i]); + + Obj = isl_map_from_pw_multi_aff(MPWA); + assert(!isl_map_has_tuple_id(Obj, isl_dim_out)); + Obj = isl_map_coalesce(Obj); + + Obj = isl_map_set_tuple_id(Obj, isl_dim_out, Id); + assert(isl_map_has_tuple_id(Obj, isl_dim_out)); +} + +PVMap::PVMap(const PVAff &Aff, long Factor) { + isl_val *FactorVal = isl_val_int_from_si(getIslCtx(), Factor); + Obj = isl_map_from_pw_aff(isl_pw_aff_scale_val(Aff, FactorVal)); +} + +PVMap::PVMap(const PVId &ValId, const PVId &InputId, const PVId &OutputId) { + Obj = isl_map_from_pw_aff(PVAff(ValId)); + if (InputId) + Obj = isl_map_set_tuple_id(Obj, isl_dim_in, InputId); + if (OutputId) + Obj = isl_map_set_tuple_id(Obj, isl_dim_out, OutputId); +} + +PVMap::~PVMap() { isl_map_free(Obj); } + +PVMap &PVMap::operator=(const PVMap &Other) { + if (this != &Other) { + isl_map_free(Obj); + Obj = isl_map_copy(Other.Obj); + } + return *this; +} + +PVMap &PVMap::operator=(PVMap &&Other) { + if (this != &Other) { + Obj = isl_map_free(Obj); + std::swap(Obj, Other.Obj); + } + return *this; +} +PVMap::operator isl_map *() const { return isl_map_copy(Obj); } + +isl_ctx *PVMap::getIslCtx() const { return isl_map_get_ctx(Obj); } + +isl_space *PVMap::getSpace() const { return isl_map_get_space(Obj); } + +bool PVMap::isEmpty() const { return Obj && isl_map_is_empty(Obj); } + +size_t PVMap::getNumInputDimensions() const { return isl_map_dim(Obj, isl_dim_in); } + +size_t PVMap::getNumOutputDimensions() const { return isl_map_dim(Obj, isl_dim_out); } + +size_t PVMap::getNumParameters() const { return isl_map_dim(Obj, isl_dim_param); } + +PVMap::CombinatorFn PVMap::getCombinatorFn(PVMap::IslCombinatorFn Fn) { + return [&](const PVMap &PV0, const PVMap &PV1) -> PVMap { + if (!PV0) + return PV1; + if (!PV1) + return PV0; + isl_map *PWAff0 = PV0; + isl_map *PWAff1 = PV1; + adjustDimensionsPlain(PWAff0, PWAff1); + return PVMap(isl_map_coalesce(Fn(PWAff0, PWAff1))); + }; +} + +PVMap &PVMap::intersect(const PVMap &PM) { + Obj = getCombinatorFn(isl_map_intersect)(Obj, PM); + return *this; +} + +PVMap &PVMap::intersectDomain(const PVSet &Dom) { + auto DomDim = Dom.getNumDimensions(); + auto MapDim = getNumInputDimensions(); + if (DomDim > MapDim) + Obj = isl_map_add_dims(Obj, isl_dim_in, DomDim - MapDim); + + isl_set *Set = Dom; + if (DomDim < MapDim) + Set = isl_set_add_dims(Set, isl_dim_set, MapDim - DomDim); + Obj = isl_map_intersect_domain(Obj, Set); + return *this; +} + +PVMap &PVMap::union_add(const PVMap &PM) { + Obj = getCombinatorFn(isl_map_union)(Obj, PM); + return *this; +} + +int PVMap::getParameterPosition(const PVId &Id) const { + int Pos = isl_map_find_dim_by_id(Obj, isl_dim_param, Id); + return Pos; +} + +void PVMap::eliminateParameter(const PVId &Id) { + int Pos = getParameterPosition(Id); + assert(Pos >= 0); + Obj = isl_map_project_out(Obj, isl_dim_param, Pos, 1); +} + +PVSet PVMap::getParameterSet() const { + return isl_map_params(isl_map_copy(Obj)); +} + +PVId PVMap::getParameter(unsigned No) const { + return PVId(isl_map_get_dim_id(Obj, isl_dim_param, No)); +} + +PVId PVMap::getInputId() const { + return isl_map_get_tuple_id(Obj, isl_dim_in); +} + +PVId PVMap::getOutputId() const { + return isl_map_get_tuple_id(Obj, isl_dim_out); +} + +void PVMap::getParameters(SmallVectorImpl &Parameters) const { + size_t NumParams = getNumParameters(); + Parameters.reserve(Parameters.size() + NumParams); + for (size_t i = 0; i < NumParams; i++) { + if (isl_map_involves_dims(Obj, isl_dim_param, i, 1)) + Parameters.push_back(getParameter(i)); + } +} + +void PVMap::getParameters(SmallVectorImpl &Parameters) const { + size_t NumParams = getNumParameters(); + Parameters.reserve(Parameters.size() + NumParams); + for (size_t i = 0; i < NumParams; i++) + if (isl_map_involves_dims(Obj, isl_dim_param, i, 1)) + Parameters.push_back(getParameter(i).getPayloadAs()); +} + +void PVMap::simplify(const PVAff &Aff) { +} + +PVMap &PVMap::floordiv(int64_t V) { + isl_val *Val = isl_val_int_from_si(getIslCtx(), V); + Obj = isl_map_floordiv_val(Obj, Val); + return *this; +} + +PVMap &PVMap::addToOutputDimension(const PVMap &Other, unsigned Dim) { + assert(Other.getNumOutputDimensions() == getNumOutputDimensions()); + assert(Other.getNumInputDimensions() == getNumInputDimensions()); + + isl_map *OtherMap = Other; + OtherMap = isl_map_align_params(OtherMap, getSpace()); + Obj = isl_map_align_params(Obj, isl_map_get_space(OtherMap)); + isl_map_dump(Obj); + isl_map_dump(OtherMap); + Obj = isl_map_sum(Obj, OtherMap); + return *this; +} + +void PVMap::dropUnusedParameters() { + size_t NumParams = getNumParameters(); + for (size_t i = 0; i < NumParams; i++) { + if (isl_map_involves_dims(Obj, isl_dim_param, i, 1)) + continue; + + Obj = isl_map_project_out(Obj, isl_dim_param, i, 1); + i--; + NumParams--; + } +} + +std::string PVMap::str() const { + char *cstr = isl_map_to_str(Obj); + if (!cstr) + return "null"; + std::string Result(cstr); + ::free(cstr); + return Result; +} + +/* -------------------- PVAff ----------------------- */ + +static void adjustDimensionsPlain(isl_pw_aff *&PWA0, isl_pw_aff *&PWA1) { + int DimIn0 = isl_pw_aff_dim(PWA0, isl_dim_in); + int DimIn1 = isl_pw_aff_dim(PWA1, isl_dim_in); + if (DimIn0 == DimIn1) + return; + + auto *&PWA = (DimIn0 > DimIn1) ? PWA1 : PWA0; + PWA = isl_pw_aff_add_dims(PWA, isl_dim_in, std::abs(DimIn0 - DimIn1)); + + assert(isl_pw_aff_dim(PWA0, isl_dim_in) == isl_pw_aff_dim(PWA1, isl_dim_in)); + assert(isl_pw_aff_dim(PWA0, isl_dim_out) == + isl_pw_aff_dim(PWA1, isl_dim_out)); +} + +PVAff::PVAff(const PVId &Id) { + auto *Space = isl_space_set_alloc(Id.getIslCtx(), 1, 0); + Space = isl_space_set_dim_id(Space, isl_dim_param, 0, Id); + + auto *LS = isl_local_space_from_space(Space); + Obj = isl_pw_aff_var_on_domain(LS, isl_dim_param, 0); +} + +PVAff::PVAff(const PVBase &Base, int64_t ConstVal) + : PVAff(PVSet::universe(Base), ConstVal) {} + +PVAff::PVAff(const PVSet &S) { + isl_set *Set = S; + Set = isl_set_detect_equalities(Set); + Set = isl_set_remove_redundancies(Set); + Set = isl_set_coalesce(Set); + Obj = isl_set_indicator_function(Set); +} + +PVAff::PVAff(const PVSet &S, int64_t ConstVal) { + isl_val *V = isl_val_int_from_si(S.getIslCtx(), ConstVal); + isl_set *Set = S; + Set = isl_set_detect_equalities(Set); + Set = isl_set_remove_redundancies(Set); + Set = isl_set_coalesce(Set); + Obj = isl_pw_aff_val_on_domain(Set, V); +} + +PVAff::PVAff(const PVBase &Base, unsigned CoeffPos, int64_t CoeffVal, + const PVId &Id) { + auto *Space = isl_space_set_alloc(Base.getIslCtx(), 1, CoeffPos + 1); + auto *LSpace = isl_local_space_from_space(Space); + auto *Aff = isl_aff_zero_on_domain(LSpace); + Aff = isl_aff_set_coefficient_si(Aff, isl_dim_param, 0, CoeffVal); + Aff = isl_aff_set_dim_id(Aff, isl_dim_param, 0, Id); + Obj = isl_pw_aff_from_aff(Aff); +} + +PVAff::PVAff(const PVAff &Other) : Obj(isl_pw_aff_copy(Other.Obj)) {} + +PVAff::~PVAff() { isl_pw_aff_free(Obj); } + +PVAff &PVAff::operator=(const PVAff &Other) { + if (this != &Other) { + isl_pw_aff_free(Obj); + Obj = isl_pw_aff_copy(Other.Obj); + } + return *this; +} + +PVAff &PVAff::operator=(PVAff &&Other) { + if (this != &Other) { + Obj = isl_pw_aff_free(Obj); + std::swap(Obj, Other.Obj); + } + return *this; +} + +bool PVAff::operator==(const PVAff &Other) { + if (this == &Other) + return true; + if (!(*this) || !Other) + return false; + return isl_pw_aff_is_equal(Obj, Other.Obj); +} + +PVAff::operator isl_pw_aff *() const { return isl_pw_aff_copy(Obj); } + +isl_ctx *PVAff::getIslCtx() const { return isl_pw_aff_get_ctx(Obj); } + +isl_space *PVAff::getSpace() const { return isl_pw_aff_get_space(Obj); } + +size_t PVAff::getNumInputDimensions() const { return isl_pw_aff_dim(Obj, isl_dim_in); } + +size_t PVAff::getNumOutputDimensions() const { return isl_pw_aff_dim(Obj, isl_dim_out); } + +size_t PVAff::getNumParameters() const { return isl_pw_aff_dim(Obj, isl_dim_param); } + +size_t PVAff::getNumPieces() const { return isl_pw_aff_n_piece(Obj); } + +PVAff PVAff::extractFactor(const PVAff &Aff) const { + SmallVector Parameters; + Aff.getParameters(Parameters); + assert(Parameters.size() == 1 && "TODO deal with more or less parameters"); + + return const_cast(this)->getParameterCoeff(Parameters.front()); +} + +int PVAff::getFactor(const PVAff &Aff) const { + // TODO: This is just a test implementation and needs to be replaced! + PVAff OtherAff = Aff; + OtherAff.intersectDomain(getDomain()); + if (isl_pw_aff_is_equal(Obj, OtherAff)) + return 1; + for (int i = 2; i < 10; i++) { + isl_pw_aff *ScaledAffPWA = + isl_pw_aff_scale_val(OtherAff, isl_val_int_from_si(getIslCtx(), i)); + bool Equal = isl_pw_aff_is_equal(Obj, ScaledAffPWA); + isl_pw_aff_free(ScaledAffPWA); + if (Equal) + return i; + } + llvm_unreachable("TODO"); + return -1; +} + +PVId PVAff::getParameter(unsigned No) const { + return PVId(isl_pw_aff_get_dim_id(Obj, isl_dim_param, No)); +} + +void PVAff::getParameters(SmallVectorImpl &Parameters) const { + size_t NumParams = getNumParameters(); + Parameters.reserve(Parameters.size() + NumParams); + for (size_t i = 0; i < NumParams; i++) + if (isl_pw_aff_involves_dims(Obj, isl_dim_param, i, 1)) + Parameters.push_back(getParameter(i)); +} + +void PVAff::getParameters(SmallVectorImpl &Parameters) const { + size_t NumParams = getNumParameters(); + Parameters.reserve(Parameters.size() + NumParams); + for (size_t i = 0; i < NumParams; i++) + if (isl_pw_aff_involves_dims(Obj, isl_dim_param, i, 1)) + Parameters.push_back(getParameter(i).getPayloadAs()); +} + +int PVAff::getParameterPosition(const PVId &Id) const { + auto *Params = isl_pw_aff_params(isl_pw_aff_copy(Obj)); + int Pos = isl_set_find_dim_by_id(Params, isl_dim_param, Id); + isl_set_free(Params); + return Pos; +} + +bool PVAff::involvesId(const PVId &Id) const { + return getParameterPosition(Id) >= 0; +} + +void PVAff::addInputDims(unsigned Dims) { + Obj = isl_pw_aff_add_dims(Obj, isl_dim_in, Dims); +} + +void PVAff::insertInputDims(unsigned Pos, unsigned Dims) { + Obj = isl_pw_aff_insert_dims(Obj, isl_dim_in, Pos, Dims); +} + +void PVAff::dropInputDim(unsigned Dim) { + Obj = isl_pw_aff_drop_dims(Obj, isl_dim_in, Dim, 1); +} + +struct DropInfo { + isl_pw_aff *NewObj; + unsigned NumDims; + unsigned Dims; +}; + +static isl_stat dropLastInputDimsHelper(isl_set *Dom, isl_aff *Aff, void *User) { + DropInfo *DI = static_cast(User); + Dom = isl_set_project_out(Dom, isl_dim_set, DI->NumDims - DI->Dims, DI->Dims); + Aff = isl_aff_drop_dims(Aff, isl_dim_in, DI->NumDims - DI->Dims, DI->Dims); + isl_pw_aff *PWA = isl_pw_aff_from_aff(Aff); + PWA = isl_pw_aff_intersect_domain(PWA, Dom); + DI->NewObj = isl_pw_aff_union_max(DI->NewObj, PWA); + return isl_stat_ok; +} + +void PVAff::dropLastInputDims(unsigned Dims) { + isl_pw_aff *NewObj = isl_pw_aff_empty(isl_space_drop_dims(getSpace(), isl_dim_in, 0, Dims)); + unsigned NumDims = getNumInputDimensions(); + DropInfo DI = {NewObj, NumDims, Dims}; + isl_pw_aff_foreach_piece(Obj, dropLastInputDimsHelper, &DI); + isl_pw_aff_free(Obj); + Obj = isl_pw_aff_coalesce(DI.NewObj); +} + +void PVAff::dropParameter(const PVId &Id) { + int Pos = getParameterPosition(Id); + if (Pos >= 0) + Obj = isl_pw_aff_drop_dims(Obj, isl_dim_param, Pos, 1); +} + +void PVAff::dropUnusedParameters() { + size_t NumParams = getNumParameters(); + for (size_t i = 0; i < NumParams; i++) { + if (isl_pw_aff_involves_dims(Obj, isl_dim_param, i, 1)) + continue; + + Obj = isl_pw_aff_drop_dims(Obj, isl_dim_param, i, 1); + i--; + NumParams--; + } +} + +bool PVAff::isComplex() const { + bool Complex = isl_pw_aff_n_piece(Obj) > PWA_N_PIECE_TRESHOLD; + if (!Complex) + Complex = isl_set_n_basic_set(getDomain()) > DOMAIN_N_BASIC_SEK_TRESHOLD; + return Complex; +} + +bool PVAff::isConstant() const { + return isl_pw_aff_is_cst(Obj); +} + +PVSet PVAff::getLessThanDomain(const PVAff &Aff) const { + isl_pw_aff *AffPWA = isl_pw_aff_add_dims( + Aff, isl_dim_in, getNumInputDimensions() - Aff.getNumInputDimensions()); + return isl_pw_aff_lt_set(isl_pw_aff_copy(Obj), AffPWA); +} + +PVSet PVAff::getLessEqualDomain(const PVAff &Aff) const { + isl_pw_aff *AffPWA = isl_pw_aff_add_dims( + Aff, isl_dim_in, getNumInputDimensions() - Aff.getNumInputDimensions()); + return isl_pw_aff_le_set(isl_pw_aff_copy(Obj), AffPWA); +} +PVSet PVAff::getGreaterEqualDomain(const PVAff &Aff) const { + isl_pw_aff *AffPWA = isl_pw_aff_add_dims( + Aff, isl_dim_in, getNumInputDimensions() - Aff.getNumInputDimensions()); + return isl_pw_aff_ge_set(isl_pw_aff_copy(Obj), AffPWA); +} + +PVSet PVAff::getDomain() const { + return isl_pw_aff_domain(isl_pw_aff_copy(Obj)); +} + +PVAff &PVAff::fixParamDim(unsigned Dim, int64_t Value) { + auto *Dom = isl_pw_aff_domain(isl_pw_aff_copy(Obj)); + Dom = isl_set_fix_si(Dom, isl_dim_param, Dim, Value); + Obj = isl_pw_aff_intersect_domain(Obj, Dom); + return *this; +} + +PVAff &PVAff::fixInputDim(unsigned Dim, int64_t Value) { + auto *Dom = isl_pw_aff_domain(isl_pw_aff_copy(Obj)); + Dom = isl_set_fix_si(Dom, isl_dim_set, Dim, Value); + Obj = isl_pw_aff_intersect_domain(Obj, Dom); + return *this; +} + +PVAff &PVAff::setInputLowerBound(unsigned Dim, + int64_t Value) { + auto *Dom = isl_pw_aff_domain(isl_pw_aff_copy(Obj)); + Dom = isl_set_lower_bound_si(Dom, isl_dim_set, Dim, Value); + Obj = isl_pw_aff_intersect_domain(Obj, Dom); + return *this; +} + +PVAff &PVAff::intersectDomain(const PVSet &Dom) { + auto DomDim = Dom.getNumDimensions(); + auto PWADim = getNumInputDimensions(); + if (DomDim > PWADim) + Obj = isl_pw_aff_add_dims(Obj, isl_dim_in, DomDim - PWADim); + + isl_set *Set = Dom; + if (DomDim < PWADim) + Set = isl_set_add_dims(Set, isl_dim_set, PWADim - DomDim); + Obj = isl_pw_aff_intersect_domain(Obj, Set); + Obj = isl_pw_aff_coalesce(Obj); + return *this; +} + +PVAff &PVAff::simplify(PVSet &S) { + isl_set *Set; + int DimDiff = S.getNumDimensions() - getNumInputDimensions(); + if (DimDiff > 0) { + S.dropDimsFrom(S.getNumDimensions() - DimDiff); + Set = S; + } + else if (DimDiff < 0) + Set = isl_set_add_dims(S, isl_dim_set, -DimDiff); + else + Set = S; + + isl_set *Dom = isl_pw_aff_domain(isl_pw_aff_copy(Obj)); + isl_set *InvDom = isl_set_subtract(Dom, Set); + isl_set *InvCtx = isl_set_params(InvDom); + isl_set *OkCtx = isl_set_complement(InvCtx); + if (isl_set_is_empty(OkCtx)) { + isl_set_free(OkCtx); + return *this; + } + S.intersect(isl_set_copy(OkCtx)); + OkCtx = isl_set_add_dims(OkCtx, isl_dim_set, getNumInputDimensions()); + Obj = isl_pw_aff_gist(Obj, OkCtx); + dropUnusedParameters(); + return *this; +} + +PVAff &PVAff::floordiv(int64_t V) { + isl_val *Val = isl_val_int_from_si(getIslCtx(), V); + Obj = isl_pw_aff_div(Obj, isl_pw_aff_val_on_domain(getDomain(), Val)); + return *this; +} + +PVAff &PVAff::add(const PVAff &PV) { + Obj = getCombinatorFn(isl_pw_aff_add)(Obj, PV); + return *this; +} + +PVAff &PVAff::sub(const PVAff &PV) { + Obj = getCombinatorFn(isl_pw_aff_sub)(Obj, PV); + return *this; +} + +PVAff &PVAff::multiply(const PVAff &PV) { + Obj = getCombinatorFn(isl_pw_aff_mul)(Obj, PV); + return *this; +} + +PVAff &PVAff::union_add(const PVAff &PV) { + Obj = getCombinatorFn(isl_pw_aff_union_add)(Obj, PV); + return *this; +} + +PVAff &PVAff::select(const PVAff &PV0, const PVAff &PV1) { + isl_pw_aff *PV0Obj = PV0; + isl_pw_aff *PV1Obj = PV1; + adjustDimensionsPlain(Obj, PV0Obj); + adjustDimensionsPlain(PV0Obj, PV1Obj); + adjustDimensionsPlain(Obj, PV1Obj); + Obj = isl_pw_aff_cond(Obj, PV0Obj, PV1Obj); + return *this; +} + +PVSet PVAff::zeroSet() const { + return isl_pw_aff_zero_set(isl_pw_aff_copy(Obj)); +} + +PVSet PVAff::nonZeroSet() const { + return isl_pw_aff_non_zero_set(isl_pw_aff_copy(Obj)); +} + +struct ParameterInfo { + PVAff Coeff; + int Pos; +}; + +static isl_stat getParameterAff(isl_set *Domain, isl_aff *Aff, void *User) { + auto *PI = static_cast(User); + isl_val *CoeffVal = isl_aff_get_coefficient_val(Aff, isl_dim_param, PI->Pos); + isl_aff *CoeffAff = isl_aff_zero_on_domain(isl_aff_get_domain_local_space(Aff)); + CoeffAff = isl_aff_set_constant_val(CoeffAff, CoeffVal); + isl_pw_aff *CoeffPWA = isl_pw_aff_from_aff(CoeffAff); + CoeffPWA = isl_pw_aff_intersect_domain(CoeffPWA, Domain); + PI->Coeff.union_add(CoeffPWA); + isl_aff_free(Aff); + return isl_stat_ok; +} + +PVAff PVAff::getParameterCoeff(const PVId &Id) { + int Pos = getParameterPosition(Id); + if (Pos < 0) + return PVAff(); + + ParameterInfo PI = {PVAff(), Pos}; + isl_stat Success = isl_pw_aff_foreach_piece(Obj, getParameterAff, &PI); + (void) Success; + assert(Success == isl_stat_ok); + + return PI.Coeff; +} + +struct EvolutionInfo { + PVAff PWA; + int LD; + int Pos; + long Val; +}; + +static isl_stat adjustBackedgeVal(isl_set *Domain, isl_aff *Aff, void *User) { + auto *EI = static_cast(User); + auto *Val = isl_aff_get_constant_val(Aff); + if (isl_val_is_zero(Val) || isl_val_get_den_si(Val) != 1) { + isl_val_free(Val); + isl_set_free(Domain); + isl_aff_free(Aff); + return isl_stat_error; + } + long ValL = isl_val_get_num_si(Val); + if (EI->Val != LONG_MAX && EI->Val != ValL) { + isl_val_free(Val); + isl_set_free(Domain); + isl_aff_free(Aff); + return isl_stat_error; + } + EI->Val = ValL; + + // Aff = isl_aff_set_constant_val(Aff, isl_val_neg(isl_val_copy(Val))); + Aff = isl_aff_set_constant_si(Aff, 0); + Aff = isl_aff_add_dims(Aff, isl_dim_in, EI->LD + 1); + Aff = isl_aff_set_coefficient_val(Aff, isl_dim_in, EI->LD, Val); + + auto *PosVal = isl_aff_get_coefficient_val(Aff, isl_dim_param, EI->Pos); + if (!isl_val_is_one(PosVal)) { + isl_val_free(Val); + isl_set_free(Domain); + isl_aff_free(Aff); + return isl_stat_error; + } + Aff = isl_aff_set_coefficient_si(Aff, isl_dim_param, EI->Pos, 0); + + Domain = isl_set_drop_constraints_involving_dims(Domain, isl_dim_param, + EI->Pos, 1); + + PVAff Increment = isl_pw_aff_from_aff(Aff); + Increment.intersectDomain(Domain); + EI->PWA.union_add(Increment); + return isl_stat_ok; +} + +PVAff PVAff::perPiecePHIEvolution(const PVId &Id, int LD) const { + int Pos = getParameterPosition(Id); + if (Pos < 0) + return PVAff(); + + EvolutionInfo EI = {PVAff(), LD, Pos, LONG_MAX}; + isl_stat Success = isl_pw_aff_foreach_piece(Obj, adjustBackedgeVal, &EI); + if (Success != isl_stat_ok) + return PVAff(); + + return EI.PWA; +} + +PVAff PVAff::getExpPWA(const PVAff &PWA) { + assert(isl_pw_aff_is_cst(PWA)); + + auto *ExpPWA = isl_pw_aff_empty(isl_pw_aff_get_space(PWA)); + auto ExpPiece = [](isl_set *Dom, isl_aff *Aff, void *User) { + auto **ExpPWA = static_cast(User); + assert(isl_aff_is_cst(Aff)); + + auto *Val = isl_aff_get_constant_val(Aff); + isl_aff_free(Aff); + + Val = isl_val_2exp(Val); + auto *ExpPiecePWA = isl_pw_aff_val_on_domain(Dom, Val); + *ExpPWA = isl_pw_aff_union_add(*ExpPWA, ExpPiecePWA); + return isl_stat_ok; + }; + + auto Success = isl_pw_aff_foreach_piece(PWA, ExpPiece, &ExpPWA); + (void)Success; + assert(Success == isl_stat_ok); + + ExpPWA = isl_pw_aff_add_dims(ExpPWA, isl_dim_in, 0); + return ExpPWA; +} + +PVAff PVAff::createAdd(const PVAff &LHS, const PVAff &RHS) { + return PVAff(LHS).add(RHS); +} + +PVAff PVAff::createUnionAdd(const PVAff &LHS, const PVAff &RHS) { + return getCombinatorFn(isl_pw_aff_union_add)(LHS, RHS); +} + +PVAff PVAff::createSub(const PVAff &LHS, const PVAff &RHS) { + return PVAff(LHS).sub(RHS); +} + +PVAff PVAff::createMultiply(const PVAff &LHS, const PVAff &RHS) { + return PVAff(LHS).multiply(RHS); +} + +PVAff PVAff::createSDiv(const PVAff &LHS, const PVAff &RHS) { + return getCombinatorFn(isl_pw_aff_tdiv_q)(LHS, RHS); +} + +PVAff PVAff::createShiftLeft(const PVAff &LHS, const PVAff &RHS) { + auto ExpRHS = getExpPWA(RHS); + return ExpRHS.multiply(LHS); +} + +PVAff PVAff::createSelect(const PVAff &CondPV, const PVAff &TruePV, + const PVAff &FalsePV) { + return PVAff(CondPV).select(TruePV, FalsePV); +} + +PVAff::CombinatorFn PVAff::getCombinatorFn(PVAff::IslCombinatorFn Fn) { + return [&](const PVAff &PV0, const PVAff &PV1) -> PVAff { + if (!PV0) + return PV1; + if (!PV1) + return PV0; + isl_pw_aff *PWAff0 = PV0; + isl_pw_aff *PWAff1 = PV1; + adjustDimensionsPlain(PWAff0, PWAff1); + return PVAff(isl_pw_aff_coalesce(Fn(PWAff0, PWAff1))); + }; +} + +/// Create the conditions under which @p L @p Pred @p R is true. +PVSet PVAff::buildConditionSet(ICmpInst::Predicate Pred, const PVAff &PV0, + const PVAff &PV1) { + isl_pw_aff *L = PV0; + isl_pw_aff *R = PV1; + adjustDimensionsPlain(L, R); + + switch (Pred) { + case ICmpInst::ICMP_EQ: + return isl_pw_aff_eq_set(L, R); + case ICmpInst::ICMP_NE: + return isl_pw_aff_ne_set(L, R); + case ICmpInst::ICMP_SLT: + return isl_pw_aff_lt_set(L, R); + case ICmpInst::ICMP_SLE: + return isl_pw_aff_le_set(L, R); + case ICmpInst::ICMP_SGT: + return isl_pw_aff_gt_set(L, R); + case ICmpInst::ICMP_SGE: + return isl_pw_aff_ge_set(L, R); + case ICmpInst::ICMP_ULT: + return isl_pw_aff_lt_set(L, R); + case ICmpInst::ICMP_UGT: + return isl_pw_aff_gt_set(L, R); + case ICmpInst::ICMP_ULE: + return isl_pw_aff_le_set(L, R); + case ICmpInst::ICMP_UGE: + return isl_pw_aff_ge_set(L, R); + default: + llvm_unreachable("Non integer predicate not supported"); + } +} + +std::string PVAff::str() const { + char *cstr = isl_pw_aff_to_str(Obj); + if (!cstr) + return "null"; + std::string Result(cstr); + ::free(cstr); + return Result; +} + +llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS, const PVBase &PV) { + OS << PV.str(); + return OS; +} Index: lib/Analysis/PolyhedralValueInfo.cpp =================================================================== --- /dev/null +++ lib/Analysis/PolyhedralValueInfo.cpp @@ -0,0 +1,1474 @@ +//===-- PolyhedralValueInfo.cpp - Polyhedral value analysis ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/PolyhedralValueInfo.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Operator.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; + +#define DEBUG_TYPE "polyhedral-value-info" + +STATISTIC(NUM_PARAMETERS, "Number of parameters created"); +STATISTIC(NUM_DOMAINS, "Number of domains created"); +STATISTIC(NUM_EXPRESSIONS, "Number of expressions created"); +STATISTIC(COMPLEX_DOMAIN, "Number of domains to complex"); + +raw_ostream &llvm::operator<<(raw_ostream &OS, PEXP::ExpressionKind Kind) { + switch (Kind) { + case PEXP::EK_NONE: + return OS << "NONE"; + case PEXP::EK_INTEGER: + return OS << "INTEGER"; + case PEXP::EK_DOMAIN: + return OS << "DOMAIN"; + case PEXP::EK_UNKNOWN_VALUE: + return OS << "UNKNOWN"; + case PEXP::EK_NON_AFFINE: + return OS << "NON AFFINE"; + default: + llvm_unreachable("Unknown polyhedral expression kind"); + } +} + +PEXP *PEXP::setDomain(const PVSet &Domain, bool Overwrite) { + assert((!PWA || Overwrite) && "PWA already initialized"); + DEBUG(dbgs() << "SetDomain: " << Domain << " for " << Val->getName() << "\n"); + if (Domain.isComplex()) { + DEBUG(dbgs() << "Domain too complex!\n"); + return invalidate(); + } + + setKind(PEXP::EK_DOMAIN); + PWA = PVAff(Domain, 1); + PWA.dropUnusedParameters(); + if (!InvalidDomain) + InvalidDomain = PVSet::empty(PWA); + if (!KnownDomain) + KnownDomain = PVSet::universe(PWA); + else + PWA.simplify(KnownDomain); + return this; +} + +void PEXP::print(raw_ostream &OS) const { + OS << PWA << " [" << getValue()->getName() << "] [" << getKind() + << "] [Scope: " << (getScope() ? getScope()->getName() : "") << "]"; + if (!InvalidDomain.isEmpty()) + OS << " [ID: " << InvalidDomain << "]"; + if (!KnownDomain.isUniverse()) + OS << " [KD: " << KnownDomain << "]"; +} +void PEXP::dump() const { print(dbgs()); } + +raw_ostream &llvm::operator<<(raw_ostream &OS, const PEXP *PE) { + if (PE) + OS << *PE; + else + OS << ""; + return OS; +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, const PEXP &PE) { + PE.print(OS); + return OS; +} + +PEXP &PEXP::operator=(const PEXP &PE) { + Kind = PE.Kind; + PWA = PE.getPWA(); + InvalidDomain = PE.getInvalidDomain(); + KnownDomain = PE.getKnownDomain(); + + return *this; +} + +PEXP &PEXP::operator=(PEXP &&PE) { + std::swap(Kind, PE.Kind); + std::swap(PWA, PE.PWA); + std::swap(InvalidDomain, PE.InvalidDomain); + std::swap(KnownDomain, PE.KnownDomain); + + return *this; +} + +void PEXP::addInvalidDomain(const PVSet &ID) { InvalidDomain.unify(ID); } + +void PEXP::addKnownDomain(const PVSet &KD) { + KnownDomain.intersect(KD); + PWA.simplify(KnownDomain); +} + +PEXP *PEXP::assign(const PEXP *PE0, const PEXP *PE1, + PVAff::IslCombinatorFn Combinator) { + return assign(PE0, PE1, PVAff::getCombinatorFn(Combinator)); +} + +PEXP *PEXP::assign(const PEXP *PE0, const PEXP *PE1, + PVAff::CombinatorFn Combinator) { + DEBUG(dbgs() << "Assign " << this << " [" << PE0 << "][" << PE1 << "]\n"); + assert(!isInitialized() && + "Cannot assign to an initialized polyhedral expression"); + assert(PE0 && PE0->isInitialized() && PE1 && PE1->isInitialized() && + "Cannot assign from an uninitialized polyhedral expression"); + + // Merge the kinds and exit if the result is non-affine. + Kind = std::max(PE0->Kind, PE1->Kind); + if (Kind == PEXP::EK_NON_AFFINE) + return invalidate(); + + auto &PWA0 = PE0->getPWA(); + auto &PWA1 = PE1->getPWA(); + PWA = Combinator(PWA0, PWA1); + + InvalidDomain = + PVSet::unify(PE0->getInvalidDomain(), PE1->getInvalidDomain()); + KnownDomain = PVSet::intersect(PE0->getKnownDomain(), PE1->getKnownDomain()); + + return this; +} + +PEXP *PEXP::combine(const PEXP *PE) { + Kind = std::max(Kind, PE->Kind); + + InvalidDomain.unify(PE->getInvalidDomain()); + KnownDomain.intersect(PE->getKnownDomain()); + return this; +} + +PEXP *PEXP::combine(const PEXP *PE, PVAff::IslCombinatorFn Combinator, + const PVSet *Domain) { + return combine(PE, PVAff::getCombinatorFn(Combinator), Domain); +} + +PEXP *PEXP::combine(const PEXP *PE, PVAff::CombinatorFn Combinator, + const PVSet *Domain) { + assert(isInitialized() && PE && PE->isInitialized() && Combinator && + "Can only combine initialized polyhedral expressions"); + + combine(PE); + if (Kind == PEXP::EK_NON_AFFINE) + return invalidate(); + + PVAff PEPWA = PE->getPWA(); + if (Domain) + PEPWA.intersectDomain(*Domain); + + PWA = PWA ? Combinator(PWA, PEPWA) : PEPWA; + + return this; +} + +PEXP *PEXP::invalidate() { + Kind = PEXP::EK_NON_AFFINE; + PWA = PVAff(); + return this; +} + +void PEXP::adjustInvalidAndKnownDomain() { + auto *ITy = cast(getValue()->getType()); + unsigned BitWidth = ITy->getBitWidth(); + assert(BitWidth > 0 && BitWidth <= 64); + int64_t LowerBound = -1 * (1 << (BitWidth - 1)); + int64_t UpperBound = (1 << (BitWidth - 1)) - 1; + + PVAff LowerPWA(getDomain(), LowerBound); + PVAff UpperPWA(getDomain(), UpperBound); + + auto *OVBinOp = cast(getValue()); + bool HasNSW = OVBinOp->hasNoSignedWrap(); + + const PVAff &PWA = getPWA(); + if (HasNSW) { + PVSet BoundedDomain = PWA.getGreaterEqualDomain(LowerPWA).intersect( + PWA.getLessEqualDomain(UpperPWA)); + KnownDomain.intersect(BoundedDomain); + } else { + PVSet BoundedDomain = LowerPWA.getGreaterEqualDomain(PWA).unify( + UpperPWA.getLessEqualDomain(PWA)); + + InvalidDomain.unify(BoundedDomain); + } +} + +// ------------------------------------------------------------------------- // + +void PolyhedralValueInfoCache::releaseMemory() { + DeleteContainerSeconds(ValueMap); + DeleteContainerSeconds(DomainMap); + ParameterMap.clear(); +} + +PVId PolyhedralValueInfoCache::getParameterId(Value &V, const PVCtx &Ctx) { + PVId &Id = ParameterMap[&V]; + if (Id) + return Id; + + std::string ParameterName; + ParameterName = V.hasName() ? V.getName().str() + : "p" + std::to_string(ParameterMap.size()); + ParameterName = PVBase::getIslCompatibleName("", ParameterName, ""); + DEBUG(dbgs() << "NEW PARAM: " << V << " ::: " << ParameterName << "\n";); + Id = PVId(Ctx, ParameterName, &V); + + return Id; +} + +// ------------------------------------------------------------------------- // + +class llvm::PolyhedralExpressionBuilder + : public InstVisitor { + + Loop *Scope; + + PolyhedralValueInfo &PI; + PolyhedralValueInfoCache PIC; + + PEXP *visit(Constant &I); + PEXP *visit(ConstantInt &I); + PEXP *visitParameter(Value &V); + PEXP *createParameter(PEXP *PE); + + PVAff getZero(const PVAff &RefPWA) { return PVAff(RefPWA.getDomain(), 0); } + + PVAff getOne(const PVAff &RefPWA) { return PVAff(RefPWA.getDomain(), 1); } + + PEXP *getOrCreatePEXP(Value &V) { + Loop *UsedScope = Scope; + if (!Scope || !isa(V)) + UsedScope = nullptr; + + PEXP *PE = PIC.getOrCreatePEXP(V, UsedScope); + assert(PE && PIC.lookup(V, UsedScope)); + assert(PE->getScope() == UsedScope && PE->getValue() == &V); + return PE; + } + + PEXP *getOrCreateDomain(BasicBlock &BB) { + return PIC.getOrCreateDomain(BB, Scope); + } + + unsigned getRelativeLoopDepth(BasicBlock *BB); + +public: + PolyhedralExpressionBuilder(PolyhedralValueInfo &PI) + : Scope(nullptr), PI(PI) {} + + PVSet buildNotEqualDomain(const PEXP *VI, ArrayRef CIs); + PVSet buildEqualDomain(const PEXP *VI, Constant &CI); + + void setScope(Loop *NewScope) { Scope = NewScope; } + + PEXP *getDomain(BasicBlock &BB); + + PVId getParameterId(Value &V) { return PIC.getParameterId(V, PI.getCtx()); } + + void setEdgeCondition(PEXP *PE, BasicBlock &PredBB, BasicBlock &BB); + PEXP *getTerminatorPEXP(BasicBlock &BB); + + PEXP *getDomainOnEdge(BasicBlock &PredBB, BasicBlock &BB); + PEXP *getDomainOnEdge(const PEXP &PredDomPE, BasicBlock &BB); + + PEXP *visitOperand(Value &Op, Instruction &I); + PEXP *visit(Value &V); + PEXP *visit(Instruction &I); + + PEXP *visitBinaryOperator(BinaryOperator &I); + PEXP *visitCallInst(CallInst &I); + PEXP *visitCastInst(CastInst &I); + PEXP *visitFCmpInst(FCmpInst &I); + PEXP *visitGetElementPtrInst(GetElementPtrInst &I); + PEXP *visitICmpInst(ICmpInst &I); + PEXP *visitInvokeInst(InvokeInst &I); + PEXP *visitLoadInst(LoadInst &I); + PEXP *visitSelectInst(SelectInst &I); + PEXP *visitConditionalPHINode(PHINode &I); + PEXP *visitPHINode(PHINode &I); + PEXP *visitAllocaInst(AllocaInst &I); + + PEXP *visitInstruction(Instruction &I); +}; + +void PolyhedralExpressionBuilder::setEdgeCondition(PEXP *PE, BasicBlock &PredBB, + BasicBlock &BB) { + assert(PE && !PE->isInitialized()); + + auto &TI = *PredBB.getTerminator(); + if (TI.getNumSuccessors() == 1) { + assert(&BB == TI.getSuccessor(0)); + PE->setDomain(PVSet::universe(PI.getCtx())); + return; + } + + auto *TermPE = getTerminatorPEXP(PredBB); + if (!TermPE) { + PE->invalidate(); + return; + } + DEBUG(dbgs() << "TERMPE: " << TermPE << "\n"); + + PVSet EdgeCond; + auto *Int64Ty = Type::getInt64Ty(TI.getContext()); + if (isa(TI)) { + if (TI.getSuccessor(0) == &BB) + EdgeCond = buildEqualDomain(TermPE, *ConstantInt::get(Int64Ty, 1)); + if (TI.getSuccessor(1) == &BB) + EdgeCond.unify(buildEqualDomain(TermPE, *ConstantInt::get(Int64Ty, 0))); + } else if (auto *SI = dyn_cast(&TI)) { + bool IsDefaultBlock = (SI->getDefaultDest() == &BB); + SmallVector OtherCaseValues; + for (auto &Case : SI->cases()) { + if (Case.getCaseSuccessor() == &BB) + EdgeCond.unify(buildEqualDomain(TermPE, *Case.getCaseValue())); + else if (IsDefaultBlock) + OtherCaseValues.push_back(Case.getCaseValue()); + } + + if (IsDefaultBlock && !OtherCaseValues.empty()) + EdgeCond.unify(buildNotEqualDomain(TermPE, OtherCaseValues)); + } + + PE->combine(TermPE); + PE->setDomain(EdgeCond); + if (PE->PWA.isComplex()) { + DEBUG(dbgs() << "Too complex edge condition!\n";); + COMPLEX_DOMAIN++; + PE->invalidate(); + } +} + +PEXP *PolyhedralExpressionBuilder::getDomain(BasicBlock &BB) { + PEXP *PE = getOrCreateDomain(BB); + assert(PE); + + if (PE->isInitialized()) + return PE; + + DEBUG(dbgs() << "Get domain of: " << BB.getName() << "\n";); + + auto *L = PI.LI.getLoopFor(&BB); + bool IsLoopHeader = L && L->getHeader() == &BB; + + if (&BB.getParent()->getEntryBlock() == &BB) { + DEBUG(dbgs() << "Universe domain for entry [" << BB.getName() << "]\n"); + NUM_DOMAINS++; + return PE->setDomain(PVSet::universe(PI.getCtx())); + } + + if (Scope && (Scope == L || !Scope->contains(&BB))) { + DEBUG(dbgs() << "Universe domain for outside block for [" << BB.getName() + << "] [" << (Scope ? Scope->getName() : "") << "]\n"); + NUM_DOMAINS++; + return PE->setDomain(PVSet::universe(PI.getCtx())); + } + + if (L) { + if (!IsLoopHeader) { + DEBUG(dbgs() << "recurse for loop header [" << L->getHeader()->getName() + << "] first!\n"); + getDomain(*L->getHeader()); + } else if (auto *PL = L->getParentLoop()) { + DEBUG(dbgs() << "recurse for parent loop header [" + << PL->getHeader()->getName() << "] first!\n"); + getDomain(*PL->getHeader()); + } + + // After the recursion we have to update PE. + PE = getOrCreateDomain(BB); + } + + DEBUG(dbgs() << "-- PE: " << PE << " : " << (void *)PE << " [L: " << L + << "][LH: " << IsLoopHeader << "]\n";); + + // While we created the domain of the loop header we did produce partial + // results for other blocks in the loop. While we could update these results + // we will simply forget them for now and recreate them if necessary. + auto ForgetDomainsInLoop = [&](Loop &L) { + if (!IsLoopHeader) + return; + for (auto *BB : L.blocks()) + if (BB != L.getHeader()) + PIC.forget(*BB, Scope); + }; + + unsigned LD = getRelativeLoopDepth(&BB); + PVSet Domain = PVSet::empty(PI.getCtx(), LD); + for (auto *PredBB : predecessors(&BB)) { + DEBUG(dbgs() << "\t Pred: " << PredBB->getName() << "\n"); + if (IsLoopHeader && L->contains(PredBB)) { + DEBUG(dbgs() << "Skip backedge from " << PredBB->getName() << "\n"); + continue; + } + + PEXP *PredDomainPE = getDomainOnEdge(*PredBB, BB); + DEBUG(dbgs() << "Domain on edge " << PredBB->getName() << " => " + << BB.getName() << ": " << PredDomainPE << "\n"); + if (!PredDomainPE || PredDomainPE->PWA.isComplex()) { + ForgetDomainsInLoop(*L); + return PE->invalidate(); + } + + PE->combine(PredDomainPE); + + PVSet PredDomain = PredDomainPE->getDomain(); + + auto *PredL = PI.LI.getLoopFor(PredBB); + if (PredL && L && PredL != L) { + if (getRelativeLoopDepth(PredBB) == LD) + PredDomain.dropDimsFrom(LD - 1); + else + PredDomain.dropDimsFrom(LD); + } else if (PredL != L) { + PredDomain.dropDimsFrom(LD); + } + DEBUG(dbgs() << "Domain on edge " << PredBB->getName() << " => " + << BB.getName() << ": " << PredDomain << "\n"); + + Domain.unify(PredDomain); + delete PredDomainPE; + if (Domain.isComplex()) { + DEBUG(dbgs() << "Too complex domain on edge!\n";); + COMPLEX_DOMAIN++; + ForgetDomainsInLoop(*L); + return PE->invalidate(); + } + } + + DEBUG(dbgs() << "PE: " << PE << "\n"); + DEBUG(dbgs() << "DOmain: " << Domain << "\n"); + PVSet ParameterRange = PVSet::universe(Domain); + + SmallVector Parameters; + Domain.getParameters(Parameters); + for (const PVId &PId : Parameters) { + auto *Parameter = PId.getPayloadAs(); + if (!Parameter->getType()->isIntegerTy(1)) { + DEBUG(dbgs() << "TODO simplify parameter of non boolean type: " + << *Parameter << "\n"); + continue; + } + + ParameterRange.intersect(PVSet::createParameterRange(PId, 0, 1)); + } + Domain.simplifyParameters(ParameterRange); + + if (!IsLoopHeader) { + PE->setDomain(Domain, true); + NUM_DOMAINS++; + return PE; + } + + PVSet NonExitDom = Domain.setInputLowerBound(LD - 1, 0); + DEBUG(dbgs() << "NonExitDom :" << NonExitDom << "\n"); + PE->setDomain(NonExitDom, true); + SmallVector ExitingBlocks; + L->getExitingBlocks(ExitingBlocks); + for (auto *ExitingBB : ExitingBlocks) { + DEBUG(dbgs() << "ExitingBB: " << ExitingBB->getName() << "\n"); + const PEXP *ExitingBBDomainPE = + ExitingBB == &BB ? PE : getDomain(*ExitingBB); + if (!ExitingBBDomainPE || PI.isNonAffine(ExitingBBDomainPE)) { + DEBUG(dbgs() << "TODO: Fix exiting bb domain hack for loop domains!"); + ForgetDomainsInLoop(*L); + return PE->invalidate(); + } + DEBUG(dbgs() << "NonExitDom :" << NonExitDom << "\n"); + PVSet ExitingBBDom = ExitingBBDomainPE->getDomain(); + DEBUG(dbgs() << "ExitingDom: " << ExitingBBDom << "\n"); + PVSet ExitCond; + for (auto *SuccBB : successors(ExitingBB)) + if (!L->contains(SuccBB)) { + PEXP *ExitingPE = getDomainOnEdge(*ExitingBBDomainPE, *SuccBB); + if (!ExitingPE || ExitingPE->PWA.isComplex()) { + ForgetDomainsInLoop(*L); + return PE->invalidate(); + } + PE->combine(ExitingPE); + ExitCond.unify(ExitingPE->getDomain()); + delete ExitingPE; + } + DEBUG(dbgs() << "ExitCond: " << ExitCond << "\n"); + ExitingBBDom.intersect(ExitCond); + ExitingBBDom.dropDimsFrom(LD); + DEBUG(dbgs() << "ExitingDom: " << ExitingBBDom << "\n"); + if (ExitingBBDom.getNumDimensions() >= LD) + ExitingBBDom.getNextIterations(LD - 1); + DEBUG(dbgs() << "ExitingDom: " << ExitingBBDom << "\n"); + NonExitDom.subtract(ExitingBBDom); + DEBUG(dbgs() << "NonExitDom :" << NonExitDom << "\n"); + if (NonExitDom.isComplex()) { + ForgetDomainsInLoop(*L); + return PE->invalidate(); + } + } + + DEBUG(dbgs() << "NonExitDom :" << NonExitDom << "\nDom :" << Domain << "\n"); + Domain.fixInputDim(LD - 1, 0); + DEBUG(dbgs() << "Dom :" << Domain << "\n"); + Domain.unify(NonExitDom); + DEBUG(dbgs() << "Dom :" << Domain << "\n"); + + PE->setDomain(Domain, true); + + ForgetDomainsInLoop(*L); + + NUM_DOMAINS++; + return PE; +} + +PEXP *PolyhedralExpressionBuilder::getDomainOnEdge(BasicBlock &PredBB, + BasicBlock &BB) { + PEXP *PredDomPE = getDomain(PredBB); + assert(PredDomPE); + if (PI.isNonAffine(PredDomPE)) + return nullptr; + return getDomainOnEdge(*PredDomPE, BB); +} + +PEXP *PolyhedralExpressionBuilder::getDomainOnEdge(const PEXP &PredDomPE, + BasicBlock &BB) { + assert(PredDomPE.getKind() == PEXP::EK_DOMAIN); + auto &PredBB = *cast(PredDomPE.getValue()); + DEBUG(dbgs() << "getDomOnEdge: " << PredBB.getName() << " => " << BB.getName() + << "\n"); + PEXP *EdgeDomain = new PEXP(&BB, Scope); + setEdgeCondition(EdgeDomain, PredBB, BB); + DEBUG(dbgs() << "EdgeCond: " << EdgeDomain << " " + << EdgeDomain->isInitialized() << "\n"); + assert(EdgeDomain->isInitialized()); + + EdgeDomain->combine(&PredDomPE); + + PVSet PredDomain = PredDomPE.getDomain(); + DEBUG(dbgs() << "Pred: " << PredBB.getName() << " BB: " << BB.getName() + << "\n" + << "Pred Dom: " << PredDomain << "\n" + << "EdgeDomain: " << EdgeDomain << "\n"); + + return EdgeDomain->setDomain(EdgeDomain->getDomain().intersect(PredDomain), + true); +} + +PEXP *PolyhedralExpressionBuilder::visitOperand(Value &Op, Instruction &I) { + PEXP *PE = visit(Op); + + Instruction *OpI = dyn_cast(&Op); + if (!OpI) + return PE; + + Loop *OpIL = PI.LI.getLoopFor(OpI->getParent()); + unsigned NumDims = PE->getPWA().getNumInputDimensions(); + unsigned NumLeftLoops = 0; + while (OpIL && NumDims && !OpIL->contains(&I)) { + NumLeftLoops++; + NumDims--; + OpIL = OpIL->getParentLoop(); + } + + if (NumLeftLoops) { + PEXP *OpIDomPE = getDomain(*OpI->getParent()); + if (PI.isNonAffine(OpIDomPE)) + PE->getPWA().dropLastInputDims(NumLeftLoops); + else { + PVSet OpIDom = OpIDomPE->getDomain(); + OpIDom.maxInLastInputDims(NumLeftLoops); + PE->getPWA().intersectDomain(OpIDom); + PE->getPWA().dropLastInputDims(NumLeftLoops); + } + } + + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visit(Value &V) { + + PEXP *PE = PIC.lookup(V, Scope); + if (PE && PE->isInitialized()) { + return PE; + } + + DEBUG(dbgs() << "Visit V: " << V << " [" << Scope << "]\n"); + if (!V.getType()->isIntegerTy() && !V.getType()->isPointerTy()) + PE = visitParameter(V); + else if (auto *I = dyn_cast(&V)) + PE = visit(*I); + else if (auto *CI = dyn_cast(&V)) + PE = visit(*CI); + else if (auto *C = dyn_cast(&V)) + PE = visit(*C); + else + PE = visitParameter(V); + + assert(PE && PE->isInitialized()); + + if (PI.isAffine(PE)) + NUM_EXPRESSIONS++; + + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visit(Constant &I) { + DEBUG(dbgs() << "Visit C: " << I << "\n";); + if (I.isNullValue()) + return visit(*ConstantInt::get(Type::getInt64Ty(I.getContext()), 0)); + return visitParameter(I); +} + +PEXP *PolyhedralExpressionBuilder::visit(ConstantInt &I) { + DEBUG(dbgs() << "Visit CI: " << I << "\n";); + + auto *PE = getOrCreatePEXP(I); + if (PE->isInitialized()) + return PE; + + int64_t ConstVal; + if (auto *CI = dyn_cast(&I)) + ConstVal = CI->isOne() ? 1 : CI->getSExtValue(); + else if (I.isNullValue()) + ConstVal = 0; + else + llvm_unreachable("Unhandled ConstantInt!"); + + PE->PWA = PVAff(PI.getCtx(), ConstVal); + PE->setKind(PEXP::EK_INTEGER); + + return PE; +} + +PEXP *PolyhedralExpressionBuilder::createParameter(PEXP *PE) { + PE->PWA = PVAff(PI.getParameterId(*PE->getValue())); + PE->setKind(PEXP::EK_UNKNOWN_VALUE); + + NUM_PARAMETERS++; + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitParameter(Value &V) { + DEBUG(dbgs() << "PESIT Par: " << V << "\n"); + auto *PE = getOrCreatePEXP(V); + return createParameter(PE); +} + +unsigned PolyhedralExpressionBuilder::getRelativeLoopDepth(BasicBlock *BB) { + Loop *L = PI.LI.getLoopFor(BB); + if (!L) + return 0; + if (!Scope) + return L->getLoopDepth(); + if (!Scope->contains(L)) + return 0; + return L->getLoopDepth() - Scope->getLoopDepth(); +} + +PEXP *PolyhedralExpressionBuilder::visit(Instruction &I) { + if (!I.getType()->isIntegerTy() && !I.getType()->isPointerTy()) + return visitParameter(I); + + DEBUG(dbgs() << "Visit I: " << I << "\n"); + auto *PE = InstVisitor::visit(I); + + unsigned RelLD = getRelativeLoopDepth(I.getParent()); + unsigned NumDims = PE->PWA.getNumInputDimensions(); + DEBUG(dbgs() << "RelLD: " << RelLD << " NumDims " << NumDims << "\n\t => " + << PE << "\n"); + assert(NumDims <= RelLD); + PE->PWA.addInputDims(RelLD - NumDims); + assert(PE->PWA.getNumInputDimensions() == RelLD); + + DEBUG(dbgs() << "Visited I: " << I << "\n\t => " << PE << "\n"); + return PE; +} + +PEXP *PolyhedralExpressionBuilder::getTerminatorPEXP(BasicBlock &BB) { + auto *Term = BB.getTerminator(); + + switch (Term->getOpcode()) { + case Instruction::Br: { + auto *BI = cast(Term); + if (BI->isUnconditional()) + return nullptr; + else + return visitOperand(*BI->getCondition(), *Term); + } + case Instruction::Switch: + return visitOperand(*cast(Term)->getCondition(), *Term); + case Instruction::Ret: + case Instruction::Unreachable: + return nullptr; + case Instruction::IndirectBr: + /// @TODO This can be over-approximated + return nullptr; + case Instruction::Invoke: + case Instruction::Resume: + case Instruction::CleanupRet: + case Instruction::CatchRet: + case Instruction::CatchSwitch: + return nullptr; + default: + return nullptr; + } + + llvm_unreachable("unknown terminator"); + return nullptr; +} + +// ------------------------------------------------------------------------- // + +PEXP *PolyhedralExpressionBuilder::visitICmpInst(ICmpInst &I) { + + auto *PE = getOrCreatePEXP(I); + auto *LPE = visitOperand(*I.getOperand(0), I); + if (PI.isNonAffine(LPE)) + return visitParameter(I); + auto *RPE = visitOperand(*I.getOperand(1), I); + if (PI.isNonAffine(RPE)) + return visitParameter(I); + + DEBUG(dbgs() << "ICMP: " << I << "\n"); + DEBUG(dbgs() << "LPE: " << LPE << "\n"); + DEBUG(dbgs() << "RPE: " << RPE << "\n"); + + auto Pred = I.getPredicate(); + auto IPred = I.getInversePredicate(); + auto TrueDomain = PVAff::buildConditionSet(Pred, LPE->PWA, RPE->PWA); + DEBUG(dbgs() << "TD: " << TrueDomain << "\n"); + if (TrueDomain.isComplex()) { + DEBUG(dbgs() << "Too complex true domain!\n";); + COMPLEX_DOMAIN++; + return visitParameter(I); + } + + auto FalseDomain = PVAff::buildConditionSet(IPred, LPE->PWA, RPE->PWA); + DEBUG(dbgs() << "FD: " << FalseDomain << "\n"); + if (FalseDomain.isComplex()) { + DEBUG(dbgs() << "Too complex false domain!\n";); + COMPLEX_DOMAIN++; + return visitParameter(I); + } + + PE->PWA = PVAff(FalseDomain, 0); + PE->PWA.union_add(PVAff(TrueDomain, 1)); + PE->combine(LPE); + PE->combine(RPE); + PE->Kind = PEXP::EK_INTEGER; + DEBUG(dbgs() << PE << "\n"); + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitFCmpInst(FCmpInst &I) { + return visitParameter(I); +} + +PEXP *PolyhedralExpressionBuilder::visitLoadInst(LoadInst &I) { + return visitParameter(I); +} + +PEXP * +PolyhedralExpressionBuilder::visitGetElementPtrInst(GetElementPtrInst &I) { + auto &DL = I.getModule()->getDataLayout(); + + auto *PtrPE = visitOperand(*I.getPointerOperand(), I); + if (PI.isNonAffine(PtrPE)) + return visitParameter(I); + + auto *PE = getOrCreatePEXP(I); + *PE = *PtrPE; + + auto *Ty = I.getPointerOperandType(); + for (auto &Op : make_range(I.idx_begin(), I.idx_end())) { + auto *PEOp = visitOperand(*Op, I); + if (PI.isNonAffine(PEOp)) + return visitParameter(I); + + if (Ty->isStructTy()) { + // TODO: Struct + DEBUG(dbgs() << "TODO: Struct ty " << *Ty << " for " << I << "\n"); + return visitParameter(I); + } + + uint64_t Size = 0; + if (Ty->isPointerTy()) { + Ty = Ty->getPointerElementType(); + Size = DL.getTypeAllocSize(Ty); + } else if (Ty->isArrayTy()) { + Ty = Ty->getArrayElementType(); + Size = DL.getTypeAllocSize(Ty); + } else { + DEBUG(dbgs() << "TODO: Unknown ty " << *Ty << " for " << I << "\n"); + return visitParameter(I); + } + DEBUG(dbgs() << "Ty: " << *Ty << " Size: " << Size << "\n"); + DEBUG(dbgs() << "GepPE: " << PE << "\n"); + + PE->combine(PEOp); + + PVAff ScaledPWA(PEOp->getDomain(), Size); + ScaledPWA.multiply(PEOp->getPWA()); + PE->PWA.add(ScaledPWA); + DEBUG(dbgs() << "GepPE: " << PE << "\n"); + } + + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitCastInst(CastInst &I) { + PEXP *PE = getOrCreatePEXP(I); + *PE = *visitOperand(*I.getOperand(0), I); + + switch (I.getOpcode()) { + case Instruction::Trunc: { + // Handle changed values. + unsigned TypeWidth = + I.getModule()->getDataLayout().getTypeSizeInBits(I.getType()); + if (TypeWidth >= 64) + return visitParameter(I); + uint64_t ExpVal = ((uint64_t)1) << TypeWidth; + PE->addInvalidDomain( + PE->getPWA().getGreaterEqualDomain(PVAff(PE->getPWA(), ExpVal))); + PE->addInvalidDomain( + PE->getPWA().getLessEqualDomain(PVAff(PE->getPWA(), -ExpVal))); + break; + } + case Instruction::ZExt: + // Handle negative values. + PE->addInvalidDomain( + PE->getPWA().getLessThanDomain(PVAff(PE->getPWA(), 0))); + break; + case Instruction::SExt: + case Instruction::FPToSI: + case Instruction::FPToUI: + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::BitCast: + case Instruction::AddrSpaceCast: + // No-op + break; + default: + llvm_unreachable("Unhandled cast operation!\n"); + } + + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitSelectInst(SelectInst &I) { + auto *CondPE = visitOperand(*I.getCondition(), I); + if (PI.isNonAffine(CondPE)) + return visitParameter(I); + + auto *OpTrue = visitOperand(*I.getTrueValue(), I); + if (PI.isNonAffine(OpTrue)) + return visitParameter(I); + + auto *OpFalse = visitOperand(*I.getFalseValue(), I); + if (PI.isNonAffine(OpFalse)) + return visitParameter(I); + + auto CondZero = CondPE->getPWA().zeroSet(); + auto CondOne = CondPE->getPWA().nonZeroSet(); + + auto *PE = getOrCreatePEXP(I); + if (!PI.isNonAffine(OpTrue)) + PE->combine(OpTrue); + + if (!PI.isNonAffine(OpFalse)) + PE->combine(OpFalse); + + if (PI.isNonAffine(OpTrue)) { + PE->InvalidDomain.unify(CondOne); + PE->PWA = OpFalse->getPWA(); + PE->setKind(OpFalse->getKind()); + return PE; + } + + if (PI.isNonAffine(OpFalse)) { + PE->InvalidDomain.unify(CondZero); + PE->PWA = OpTrue->getPWA(); + PE->setKind(OpTrue->getKind()); + return PE; + } + + PE->PWA = PVAff::createSelect(CondPE->getPWA(), OpTrue->getPWA(), + OpFalse->getPWA()); + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitInvokeInst(InvokeInst &I) { + return visitParameter(I); +} + +PEXP *PolyhedralExpressionBuilder::visitCallInst(CallInst &I) { + return visitParameter(I); +} + +PEXP *PolyhedralExpressionBuilder::visitConditionalPHINode(PHINode &I) { + bool InvalidOtherwise = false; + + unsigned RelLD = getRelativeLoopDepth(I.getParent()); + DEBUG(dbgs() << "\nCondPHI: " << I << " [RelLD: " << RelLD << "]\n"); + auto *PE = getOrCreatePEXP(I); + PE->setKind(PEXP::EK_NONE); + + for (unsigned u = 0, e = I.getNumIncomingValues(); u < e; u++) { + PEXP *PredEdgePE = getDomainOnEdge(*I.getIncomingBlock(u), *I.getParent()); + if (!PredEdgePE) { + DEBUG(dbgs() << "PHI op " << u << " (" << *I.getIncomingValue(u) + << ") no domain on edge\n"); + InvalidOtherwise = true; + continue; + } + + auto *PredOpPE = visit(*I.getIncomingValue(u)); + assert(!PI.isNonAffine(PredOpPE)); + + DEBUG(dbgs() << "Op: " << PredOpPE << " " << u + << "\n On edge: " << PredEdgePE << "\n";); + PE->combine(PredEdgePE); + PVSet PredDomain = PredEdgePE->getDomain(); + unsigned NumDims = PredDomain.getNumDimensions(); + assert(RelLD <= NumDims); + if (RelLD < NumDims) + PredDomain.maxInLastInputDims(NumDims - RelLD); + DEBUG(dbgs() << "PredDom: " << PredDomain << "\n"); + PE->combine(PredOpPE, PVAff::createUnionAdd, &PredDomain); + delete PredEdgePE; + } + + if (!PE->isInitialized()) + return visitParameter(I); + + DEBUG(dbgs() << "\nCONDITIONAL PHI: " << I << "\n" << PE << "\n";); + assert(PE->PWA.getNumInputDimensions() >= RelLD); + + PE->PWA.dropLastInputDims(PE->PWA.getNumInputDimensions() - RelLD); + if (InvalidOtherwise) { + assert(PE->PWA); + PVSet Dom = PE->getDomain(); + PE->InvalidDomain.unify(Dom.complement()); + } + + DEBUG(dbgs() << "\nCONDITIONAL PHI: " << I << "\n" << PE << "\n";); + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitPHINode(PHINode &I) { + auto &BB = *I.getParent(); + Loop *L = PI.LI.getLoopFor(&BB); + bool IsLoopHeader = L && L->getHeader() == &BB; + + if (!IsLoopHeader) + return visitConditionalPHINode(I); + + PEXP *ParamPE = PIC.getOrCreatePEXP(I, L); + assert(ParamPE); + + PVId Id = PI.getParameterId(I); + if (!ParamPE->isInitialized()) { + ParamPE->PWA = PVAff(Id); + ParamPE->Kind = PEXP::EK_UNKNOWN_VALUE; + } + if (Scope == L) { + DEBUG(dbgs() << "PHI loop is scope. Parametric value is sufficent!\n"); + return ParamPE; + } + + PEXP *PE = getOrCreatePEXP(I); + assert(PE && !PE->isInitialized()); + if (Scope && !Scope->contains(&I)) { + DEBUG(dbgs() << "PHI not in scope. Parametric value is sufficent!\n"); + *PE = *ParamPE; + return PE; + } + + unsigned LoopDim = getRelativeLoopDepth(L->getHeader()); + + auto OldScope = Scope; + setScope(L); + + bool ConstantStride = true; + PVAff BackEdgeOp; + unsigned NumLatches = L->getNumBackEdges(); + for (unsigned u = 0, e = I.getNumIncomingValues(); u != e; u++) { + auto *OpBB = I.getIncomingBlock(u); + if (!L->contains(OpBB)) + continue; + + auto *OpVal = I.getIncomingValue(u); + auto *OpPE = visit(*OpVal); + PVAff OpAff = OpPE->getPWA(); + OpAff.dropParameter(Id); + if (!OpAff.isConstant()) { + DEBUG(dbgs() << "PHI has non constant stride: " << OpPE << "\n\tfor " + << *OpVal << "\n"); + if (OpPE->getPWA() != OpAff) { + DEBUG(dbgs() << " Operand involves PHI! Invalid!\n"); + setScope(OldScope); + return visitParameter(I); + } + + ConstantStride = false; + if (OldScope != Scope) { + setScope(OldScope); + OpPE = visit(*OpVal); + setScope(L); + } + } + + OpAff = OpPE->getPWA(); + if (NumLatches > 1 || !ConstantStride) { + PEXP *EdgeDomPE = getDomainOnEdge(*OpBB, BB); + if (!EdgeDomPE) { + DEBUG(dbgs() << "PHI back edge has unknown domain!\n"); + setScope(OldScope); + return visitParameter(I); + } + + PVSet EdgeDom = EdgeDomPE->getDomain(); + DEBUG(dbgs() << "EdgeDom: " << EdgeDom << " [LD: " << LoopDim << "]\n"); + EdgeDom.setInputLowerBound(LoopDim - 1, 1); + DEBUG(dbgs() << "EdgeDom: " << EdgeDom << " [LD: " << LoopDim << "]\n"); + OpAff.intersectDomain(EdgeDom); + delete EdgeDomPE; + } + + DEBUG(dbgs() << "Back edge Op: " << OpAff << "\n"); + BackEdgeOp.union_add(OpAff); + } + + DEBUG(dbgs() << "Final Back edge Op: " << BackEdgeOp << "\n"); + if (ConstantStride) { + BackEdgeOp = BackEdgeOp.perPiecePHIEvolution(Id, LoopDim - 1); + if (!BackEdgeOp) { + DEBUG(dbgs() << "TODO: non constant back edge operand value!\n"); + setScope(OldScope); + return visitParameter(I); + } + } + + DEBUG(dbgs() << "BackEdgeOp: " << BackEdgeOp << "\n"); + + PE->PWA = PVAff(); + PE->PWA.union_add(BackEdgeOp); + PE->PWA.dropParameter(Id); + setScope(OldScope); + + for (unsigned u = 0, e = I.getNumIncomingValues(); u != e; u++) { + auto *OpBB = I.getIncomingBlock(u); + if (L->contains(OpBB)) + continue; + + auto *OpVal = I.getIncomingValue(u); + auto *OpPE = visit(*OpVal); + if (PI.isNonAffine(OpPE)) { + return visitParameter(I); + } + + PVAff OpAff = OpPE->getPWA(); + assert(e > NumLatches); + if (e - NumLatches > 1 || !ConstantStride) { + PEXP *EdgeDomPE = getDomainOnEdge(*OpBB, BB); + if (!EdgeDomPE) { + return visitParameter(I); + } + + PVSet EdgeDom = EdgeDomPE->getDomain(); + EdgeDom.fixInputDim(LoopDim - 1, 0); + OpAff.intersectDomain(EdgeDom); + delete EdgeDomPE; + } + + DEBUG(dbgs() << "Init Op: " << OpAff << "\n"); + PE->PWA.union_add(OpAff); + PE->combine(OpPE); + } + + PE->Kind = PEXP::EK_UNKNOWN_VALUE; + DEBUG(dbgs() << "PHI: " << PE->PWA << "\n"); + + return PE; +} + +PEXP *PolyhedralExpressionBuilder::visitBinaryOperator(BinaryOperator &I) { + + Value *Op0 = I.getOperand(0); + auto *PEOp0 = visitOperand(*Op0, I); + if (PI.isNonAffine(PEOp0)) + return visitParameter(I); + + Value *Op1 = I.getOperand(1); + auto *PEOp1 = visitOperand(*Op1, I); + if (PI.isNonAffine(PEOp1)) + return visitParameter(I); + + auto *PE = getOrCreatePEXP(I); + switch (I.getOpcode()) { + case Instruction::Add: + PE->assign(PEOp0, PEOp1, PVAff::createAdd); + PE->adjustInvalidAndKnownDomain(); + return PE; + case Instruction::Sub: + PE->assign(PEOp0, PEOp1, PVAff::createSub); + PE->adjustInvalidAndKnownDomain(); + return PE; + + case Instruction::Mul: + if (PEOp0->Kind != PEXP::EK_INTEGER && PEOp1->Kind != PEXP::EK_INTEGER) + return visitParameter(I); + PE->assign(PEOp0, PEOp1, PVAff::createMultiply); + PE->adjustInvalidAndKnownDomain(); + return PE; + + case Instruction::SRem: +// TODO: This is not yet compatible with the PHI handling which assumes +// monotonicity! +#if 0 + if (PEOp1->Kind == PEXP::EK_INTEGER) { + auto NZ = PEOp1->PWA.nonZeroSet(); + PEOp1->PWA.intersectDomain(NZ); + return PE->assign(PEOp0, PEOp1, isl_pw_aff_tdiv_r); + } +#endif + return visitParameter(I); + case Instruction::SDiv: + if (PEOp1->Kind == PEXP::EK_INTEGER) { + auto NZ = PEOp1->PWA.nonZeroSet(); + PEOp1->PWA.intersectDomain(NZ); + return PE->assign(PEOp0, PEOp1, PVAff::createSDiv); + } + return visitParameter(I); + case Instruction::Shl: + if (PEOp1->Kind == PEXP::EK_INTEGER) { + return PE->assign(PEOp0, PEOp1, PVAff::createShiftLeft); + } + return visitParameter(I); + case Instruction::UDiv: + case Instruction::AShr: + case Instruction::LShr: + case Instruction::URem: + // TODO + return visitParameter(I); + + // Bit operations + case Instruction::And: + if (I.getType()->isIntegerTy(1)) { + PE->assign(PEOp0, PEOp1, PVAff::createMultiply); + PE->PWA.select(getOne(PE->PWA), getZero(PE->PWA)); + return PE; + } + return visitParameter(I); + case Instruction::Or: + if (I.getType()->isIntegerTy(1)) { + PE->assign(PEOp0, PEOp1, PVAff::createAdd); + PE->PWA.select(getOne(PE->PWA), getZero(PE->PWA)); + return PE; + } + return visitParameter(I); + case Instruction::Xor: + if (I.getType()->isIntegerTy(1)) { + auto OneBitXOR = [this](const PVAff &PWA0, const PVAff &PWA1) { + auto OnePWA = getOne(PWA0); + auto ZeroPWA = getZero(PWA0); + auto Mul = PVAff::createMultiply(PWA0, PWA1); + auto MulZero = Mul.zeroSet(); + auto Add = PVAff::createAdd(PWA0, PWA1); + auto AddNonZero = Add.nonZeroSet(); + auto TrueSet = MulZero.intersect(AddNonZero); + return PVAff(TrueSet).select(OnePWA, ZeroPWA); + }; + return PE->assign(PEOp0, PEOp1, OneBitXOR); + } + if (auto *COp0 = dyn_cast(Op0)) + if (COp0->isMinusOne()) { + PE->assign(PEOp0, PEOp1, PVAff::createMultiply); + PE->combine(PEOp0, PVAff::createAdd); + } + if (auto *COp1 = dyn_cast(Op1)) + if (COp1->isMinusOne()) { + PE->assign(PEOp0, PEOp1, PVAff::createMultiply); + PE->combine(PEOp1, PVAff::createAdd); + } + + // TODO + return visitParameter(I); + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: + case Instruction::FRem: + case Instruction::BinaryOpsEnd: + break; + } + + llvm_unreachable("Invalid Binary Operation"); +} + +PEXP *PolyhedralExpressionBuilder::visitAllocaInst(AllocaInst &I) { + return visitParameter(I); +} + +PEXP *PolyhedralExpressionBuilder::visitInstruction(Instruction &I) { + DEBUG(dbgs() << "UNKNOWN INST " << I << "\n";); + assert(!I.getType()->isVoidTy()); + return visitParameter(I); +} + +PVSet PolyhedralExpressionBuilder::buildNotEqualDomain( + const PEXP *PE, ArrayRef CIs) { + assert(PE->Kind != PEXP::EK_NON_AFFINE && PE->PWA); + + PVSet NotEqualDomain; + for (auto *CI : CIs) { + auto *CPE = visit(*static_cast(CI)); + assert(CPE->Kind == PEXP::EK_INTEGER && CPE->PWA && !CPE->InvalidDomain); + + NotEqualDomain.intersect( + PVAff::buildConditionSet(ICmpInst::ICMP_NE, PE->PWA, CPE->PWA)); + } + + return NotEqualDomain; +} + +PVSet PolyhedralExpressionBuilder::buildEqualDomain(const PEXP *PE, + Constant &CI) { + assert(PE->Kind != PEXP::EK_NON_AFFINE && PE->PWA); + + auto *CPE = visit(static_cast(CI)); + assert(CPE->Kind == PEXP::EK_INTEGER && CPE->PWA && !CPE->InvalidDomain); + + return PVAff::buildConditionSet(ICmpInst::ICMP_EQ, PE->PWA, CPE->PWA); +} + +// ------------------------------------------------------------------------- // + +PolyhedralValueInfo::PolyhedralValueInfo(PVCtx Ctx, LoopInfo &LI) + : Ctx(Ctx), LI(LI), PEBuilder(new PolyhedralExpressionBuilder(*this)) { +} + +PolyhedralValueInfo::~PolyhedralValueInfo() { delete PEBuilder; } + +const PEXP *PolyhedralValueInfo::getDomainFor(BasicBlock *BB, Loop *Scope) { + PEBuilder->setScope(Scope); + return PEBuilder->getDomain(*BB); +} + +const PEXP *PolyhedralValueInfo::getPEXP(Value *V, Loop *Scope) { + PEBuilder->setScope(Scope); + return PEBuilder->visit(*V); +} + +PVId PolyhedralValueInfo::getParameterId(Value &V) { + return PEBuilder->getParameterId(V); +} + +bool PolyhedralValueInfo::isUnknown(const PEXP *PE) const { + return PE->Kind == PEXP::EK_UNKNOWN_VALUE; +} + +bool PolyhedralValueInfo::isInteger(const PEXP *PE) const { + return PE->Kind == PEXP::EK_INTEGER; +} + +bool PolyhedralValueInfo::isConstant(const PEXP *PE) const { + return isInteger(PE) && PE->PWA.getNumPieces() == 1; +} + +bool PolyhedralValueInfo::isAffine(const PEXP *PE) const { + return PE->Kind != PEXP::EK_NON_AFFINE && PE->isInitialized(); +} + +bool PolyhedralValueInfo::isNonAffine(const PEXP *PE) const { + return PE->Kind == PEXP::EK_NON_AFFINE; +} + +bool PolyhedralValueInfo::hasScope(const PEXP *PE, Loop *Scope) const { + SmallVector Values; + getParameters(PE, Values); + for (Value *V : Values) { + auto *I = dyn_cast(V); + if (!I) + continue; + if (!Scope || Scope->contains(I)) + return false; + } + return true; +} + +bool PolyhedralValueInfo::hasFunctionScope(const PEXP *PE) const { + return hasScope(PE, nullptr); +} + +void PolyhedralValueInfo::getParameters(const PEXP *PE, + SmallVectorImpl &Values) const { + const PVAff &PWA = PE->getPWA(); + PWA.getParameters(Values); +} + +void PolyhedralValueInfo::getParameters( + const PEXP *PE, SmallVectorImpl &Values) const { + const PVAff &PWA = PE->getPWA(); + PWA.getParameters(Values); +} + +bool PolyhedralValueInfo::isKnownToHold(Value *LHS, Value *RHS, + ICmpInst::Predicate Pred, + Instruction *IP, Loop *Scope) { + const PEXP *LHSPE = getPEXP(LHS, Scope); + if (isNonAffine(LHSPE)) + return false; + + const PEXP *RHSPE = getPEXP(RHS, Scope); + if (isNonAffine(RHSPE)) + return false; + + const PEXP *IPDomPE = IP ? getDomainFor(IP->getParent(), Scope) : nullptr; + if (IP && (isNonAffine(IPDomPE) || !IPDomPE->getInvalidDomain().isEmpty())) + return false; + + PVSet LHSInvDom = LHSPE->getInvalidDomain(); + PVSet RHSInvDom = RHSPE->getInvalidDomain(); + if (IPDomPE) { + LHSInvDom.intersect(IPDomPE->getDomain()); + RHSInvDom.intersect(IPDomPE->getDomain()); + } + + if (!LHSInvDom.isEmpty() || !RHSInvDom.isEmpty()) + return false; + + PVAff LHSAff = LHSPE->getPWA(); + PVAff RHSAff = RHSPE->getPWA(); + + if (IPDomPE) { + LHSAff.intersectDomain(IPDomPE->getDomain()); + RHSAff.intersectDomain(IPDomPE->getDomain()); + } + + auto FalseDomain = PVAff::buildConditionSet( + ICmpInst::getInversePredicate(Pred), LHSAff, RHSAff); + return FalseDomain.isEmpty(); +} + +void PolyhedralValueInfo::print(raw_ostream &OS) const {} + +// ------------------------------------------------------------------------- // + +char PolyhedralValueInfoWrapperPass::ID = 0; + +void PolyhedralValueInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.setPreservesAll(); +} + +void PolyhedralValueInfoWrapperPass::releaseMemory() { + F = nullptr; + delete PI; + PI = nullptr; +} + +bool PolyhedralValueInfoWrapperPass::runOnFunction(Function &F) { + + auto &LI = getAnalysis().getLoopInfo(); + + delete PI; + PI = new PolyhedralValueInfo(Ctx, LI); + + this->F = &F; + + return false; +} + +void PolyhedralValueInfoWrapperPass::print(raw_ostream &OS, + const Module *) const { + PI->print(OS); + + if (!F) + return; + + PolyhedralValueInfoWrapperPass &PIWP = + *const_cast(this); + LoopInfo &LI = getAnalysis().getLoopInfo(); + + for (auto &BB : *PIWP.F) { + Loop *Scope = LI.getLoopFor(&BB); + do { + PIWP.PI->getDomainFor(&BB, Scope); + for (auto &Inst : BB) + if (!Inst.getType()->isVoidTy()) + PIWP.PI->getPEXP(&Inst, Scope); + if (!Scope) + break; + Scope = Scope->getParentLoop(); + } while (true); + } + + for (auto &BB : *PIWP.F) { + Loop *Scope = LI.getLoopFor(&BB); + do { + OS << "Scope: " << (Scope ? Scope->getName() : "") << "\n"; + const PEXP *PE = PIWP.PI->getDomainFor(&BB, Scope); + OS << "Domain of " << BB.getName() << ":\n"; + OS << "\t => " << PE << "\n"; + for (auto &Inst : BB) { + if (Inst.getType()->isVoidTy()) { + OS << "\tValue of " << Inst << ":\n"; + OS << "\t\t => void type!\n"; + continue; + } + const PEXP *PE = PIWP.PI->getPEXP(&Inst, Scope); + OS << "\tValue of " << Inst << ":\n"; + OS << "\t\t => " << PE << "\n"; + SmallVector Values; + PIWP.PI->getParameters(PE, Values); + if (Values.empty()) + continue; + OS << "\t\t\tParams:\n"; + for (Value *Val : Values) + OS << "\t\t\t - " << *Val << "\n"; + } + + if (!Scope) + break; + Scope = Scope->getParentLoop(); + } while (true); + } +} + +FunctionPass *llvm::createPolyhedralValueInfoWrapperPass() { + return new PolyhedralValueInfoWrapperPass(); +} + +void PolyhedralValueInfoWrapperPass::dump() const { + return print(dbgs(), nullptr); +} + +AnalysisKey PolyhedralValueInfoAnalysis::Key; + +PolyhedralValueInfo +PolyhedralValueInfoAnalysis::run(Function &F, FunctionAnalysisManager &AM) { + auto &LI = AM.getResult(F); + return PolyhedralValueInfo(Ctx, LI); +} + +INITIALIZE_PASS_BEGIN(PolyhedralValueInfoWrapperPass, "polyhedral-value-info", + "Polyhedral value analysis", false, true); +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass); +INITIALIZE_PASS_END(PolyhedralValueInfoWrapperPass, "polyhedral-value-info", + "Polyhedral value analysis", false, true) Index: lib/Analysis/isl/AUTHORS =================================================================== --- /dev/null +++ lib/Analysis/isl/AUTHORS @@ -0,0 +1,45 @@ +isl was written by + + Sven Verdoolaege +2006-2007 Leiden Institute of Advanced Computer Science + Universiteit Leiden + Niels Bohrweg 1 + 2333 CA Leiden + The Netherlands +2008-2009 K.U.Leuven + Departement Computerwetenschappen + Celestijnenlaan 200A + B-3001 Leuven + Belgium +2010-2011 INRIA Saclay - Ile-de-France + Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod + 91893 Orsay + France +2011-2012 consultant for Leiden Institute of Advanced Computer Science +2012-2014 Ecole Normale Superieure + 45 rue d'Ulm, 75230 Paris + France +2014-2015 INRIA Rocquencourt + Domaine de Voluceau - Rocquencourt, B.P. 105 + 78153 Le Chesnay + France +2015 LLPT Labs + +Contributions by + +Mythri Alle +Riyadh Baghdadi +Serge Belyshev +Ray Donnelly +Johannes Doerfert +Tobias Grosser +Alexandre Isoard +Andreas Kloeckner +Michael Kruse +Sebastian Pop +Louis-Noel Pouchet +Uday Kumar Reddy +Andreas Simbuerger +Sven van Haastregt + +The merge sort implementation was written by Jeffrey Stedfast. Index: lib/Analysis/isl/CMakeLists.txt =================================================================== --- /dev/null +++ lib/Analysis/isl/CMakeLists.txt @@ -0,0 +1,288 @@ +# External: Integer Set Library +set(ISL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(ISL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") + +# Determine version of isl +if (EXISTS "${ISL_SOURCE_DIR}/GIT_HEAD_ID") + # The source comes from a 'make dist' archive + file(READ "${ISL_SOURCE_DIR}/GIT_HEAD_ID" GIT_HEAD_ID) + string(STRIP "${GIT_HEAD_ID}" GIT_HEAD_ID) +elseif (EXISTS "${ISL_SOURCE_DIR}/gitversion.h") + # The source directory is preconfigured + file(READ "${ISL_SOURCE_DIR}/gitversion.h" GITVERSION_H) + string(REGEX REPLACE ".*\\\"([^\\\"]*)\\\".*" "\\1" GIT_HEAD_ID "${GITVERSION_H}") +elseif () + # Unknown revision + # TODO: We could look for a .git and get the revision from HEAD + set(GIT_HEAD_ID "UNKNOWN") +endif () + +message(STATUS "ISL version: ${GIT_HEAD_ID}") + +# Enable small integer optimization and imath +set(USE_GMP_FOR_MP OFF) +set(USE_IMATH_FOR_MP ON) +set(USE_SMALL_INT_OPT ON) + +# Determine compiler characteristics +include(CheckCSourceCompiles) + +# Like check_c_source_compiles, but sets the result to either +# 0 (error while compiling) or 1 (compiled successfully) +# Required for compatibility with autotool's AC_CHECK_DECLS +function (check_c_source_compiles_numeric _prog _var) + check_c_source_compiles("${_prog}" "${_var}") + if ("${${_var}}") + set("${_var}" 1 PARENT_SCOPE) + else () + set("${_var}" 0 PARENT_SCOPE) + endif () +endfunction () + +# Check for the existance of a type +function (check_c_type_exists _type _files _variable) + set(_includes "") + foreach (file_name ${_files}) + set(_includes "${_includes}#include<${file_name}>\n") + endforeach() + check_c_source_compiles(" + ${_includes} + ${_type} typeVar; + int main() { + return 0; + } + " ${_variable}) +endfunction () + + +check_c_source_compiles(" + int func(void) __attribute__((__warn_unused_result__)); + int main() { return 0; } + " HAS_ATTRIBUTE_WARN_UNUSED_RESULT) +set(GCC_WARN_UNUSED_RESULT) +if (HAS_ATTRIBUTE_WARN_UNUSED_RESULT) + set(GCC_WARN_UNUSED_RESULT "__attribute__((__warn_unused_result__))") +endif () + +check_c_source_compiles(" + static void foo(void) __attribute__ ((unused)); + int main() { return 0; } + " HAVE___ATTRIBUTE__) + + +check_c_source_compiles_numeric(" + #include + int main() { (void)ffs(0); return 0; } + " HAVE_DECL_FFS) + +check_c_source_compiles_numeric(" + int main() { __builtin_ffs(0); return 0; } + " HAVE_DECL___BUILTIN_FFS) + +check_c_source_compiles_numeric(" + #include + int main() { _BitScanForward(NULL, 0); return 0; } + " HAVE_DECL__BITSCANFORWARD) + +if (NOT HAVE_DECL_FFS AND + NOT HAVE_DECL___BUILTIN_FFS AND + NOT HAVE_DECL__BITSCANFORWARD) + message(FATAL_ERROR "No ffs implementation found") +endif () + + +check_c_source_compiles_numeric(" + #include + int main() { (void)strcasecmp(\"\", \"\"); return 0; } + " HAVE_DECL_STRCASECMP) + +check_c_source_compiles_numeric(" + #include + int main() { _stricmp(\"\", \"\"); return 0; } + " HAVE_DECL__STRICMP) + +if (NOT HAVE_DECL_STRCASECMP AND NOT HAVE_DECL__STRICMP) + message(FATAL_ERROR "No strcasecmp implementation found") +endif () + + +check_c_source_compiles_numeric(" + #include + int main() { (void)strncasecmp(\"\", \"\", 0); return 0; } + " HAVE_DECL_STRNCASECMP) + +check_c_source_compiles_numeric(" + #include + int main() { _strnicmp(\"\", \"\", 0); return 0; } + " HAVE_DECL__STRNICMP) + +if (NOT HAVE_DECL_STRNCASECMP AND NOT HAVE_DECL__STRNICMP) + message(FATAL_ERROR "No strncasecmp implementation found") +endif () + + +check_c_source_compiles_numeric(" + #include + int main() { snprintf((void*)0, 0, \"\"); return 0; } + " HAVE_DECL_SNPRINTF) + +check_c_source_compiles_numeric(" + #include + int main() { _snprintf((void*)0, 0, \"\"); return 0; } + " HAVE_DECL__SNPRINTF) + +if (NOT HAVE_DECL_SNPRINTF AND NOT HAVE_DECL__SNPRINTF) + message(FATAL_ERROR "No snprintf implementation found") +endif () + + +# Write configure result +# configure_file(... COPYONLY) avoids that the time stamp changes if the file is identical +file(WRITE "${ISL_BINARY_DIR}/gitversion.h.tmp" + "#define GIT_HEAD_ID \"${GIT_HEAD_ID}\"") +configure_file("${ISL_BINARY_DIR}/gitversion.h.tmp" + "${ISL_BINARY_DIR}/gitversion.h" COPYONLY) + +configure_file("isl_config.h.cmake" "${ISL_BINARY_DIR}/isl_config.h") + +include_directories(BEFORE + ${ISL_BINARY_DIR}/include + ${ISL_SOURCE_DIR}/imath + ${ISL_SOURCE_DIR} +) + +set (ISL_INTERNAL_FILES + basis_reduction_templ.c + bound.c + cat.c + closure.c + codegen.c + isl_ast_int.c + isl_gmp.c + isl_list_templ.c + isl_map_lexopt_templ.c + isl_multi_apply_set.c + isl_multi_apply_templ.c + isl_multi_apply_union_set.c + isl_multi_cmp.c + isl_multi_coalesce.c + isl_multi_floor.c + isl_multi_gist.c + isl_multi_hash.c + isl_multi_intersect.c + isl_multi_templ.c + isl_power_templ.c + isl_pw_hash.c + isl_pw_templ.c + isl_pw_union_opt.c + isl_test.c + isl_test_imath.c + isl_test_int.c + isl_union_eval.c + isl_union_multi.c + isl_union_neg.c + isl_union_single.c + isl_union_templ.c + isl_val_gmp.c + isl_val_imath.c + mp_get_memory_functions.c + polyhedron_detect_equalities.c + polyhedron_minimize.c + polyhedron_sample.c + polytope_scan.c + print_templ.c +) + +set(LLVM_OPTIONAL_SOURCES ISLInternal ${ISL_INTERNAL_FILES}) + +# ISL files to compile +set (ISL_FILES + basis_reduction_tab.c + isl_aff.c + isl_affine_hull.c + isl_arg.c + isl_ast_build.c + isl_ast_build_expr.c + isl_ast.c + isl_ast_codegen.c + isl_ast_graft.c + isl_band.c + isl_bernstein.c + isl_blk.c + isl_bound.c + isl_coalesce.c + isl_constraint.c + isl_convex_hull.c + isl_ctx.c + isl_deprecated.c + isl_dim_map.c + isl_equalities.c + isl_factorization.c + isl_farkas.c + isl_ffs.c + isl_flow.c + isl_fold.c + isl_hash.c + isl_id.c + isl_id_to_ast_expr.c + isl_id_to_id.c + isl_id_to_pw_aff.c + isl_ilp.c + isl_imath.c + isl_input.c + isl_int_sioimath.c + isl_local.c + isl_local_space.c + isl_lp.c + isl_map.c + isl_map_list.c + isl_map_simplify.c + isl_map_subtract.c + isl_map_to_basic_set.c + isl_mat.c + isl_morph.c + isl_obj.c + isl_options.c + isl_output.c + isl_point.c + isl_polynomial.c + isl_printer.c + isl_range.c + isl_reordering.c + isl_sample.c + isl_scan.c + isl_schedule.c + isl_schedule_band.c + isl_schedule_node.c + isl_schedule_read.c + isl_schedule_tree.c + isl_scheduler.c + isl_seq.c + isl_set_list.c + isl_sort.c + isl_space.c + isl_stream.c + isl_tab.c + isl_tab_pip.c + isl_tarjan.c + isl_transitive_closure.c + isl_union_map.c + isl_val.c + isl_val_sioimath.c + isl_vec.c + isl_version.c + isl_vertices.c + pip.c + print.c + imath/gmp_compat.c + imath/imath.c + imath/imrat.c +) + +add_llvm_library(LLVMisl + ${ISL_FILES} +) + +# ISL requires at least C99 to compile. gcc < 5.0 use -std=gnu89 as default. +#target_enable_c99(ISL) + Index: lib/Analysis/isl/ChangeLog =================================================================== --- /dev/null +++ lib/Analysis/isl/ChangeLog @@ -0,0 +1,183 @@ +version: 0.17.1 +date: Fri May 6 12:02:48 CEST 2016 +changes: + - fix bug in coalescing treatment +--- +version: 0.17 +date: Tue May 3 14:26:43 CEST 2016 +changes: + - optionally combine SCCs incrementally in scheduler + - optionally maximize coincidence in scheduler + - optionally avoid loop coalescing in scheduler + - fix handling of nested integer divisions + - optionally detect min/max expressions during AST generation + - minor AST generator improvements + - simplify stride constraints + - improve support for expansions in schedule trees +--- +version: 0.16.1 +date: Thu Jan 14 18:08:06 CET 2016 +changes: + - fix bug in simplification +--- +version: 0.16 +date: Tue Jan 12 09:56:16 CET 2016 +changes: + - add 32 bit integer optimization for IMath + - minor AST generator improvements + - add isl_union_flow_get_full_{may,must}_dependence + - minor improvements to Python bindings + - minor improvements to set and map printing +--- +version: 0.15 +date: Thu Jun 11 12:45:33 CEST 2015 +changes: + - improve coalescing + - add isl_union_access_info_compute_flow + - add mark nodes in AST + - add isl_union_pw_aff and isl_multi_union_pw_aff + - add schedule trees + - deprecate band forests + - deprecate separation_class AST generation option + - introduce isl_bool and isl_stat types +--- +version: 0.14.1 +date: Thu Apr 9 12:57:23 CEST 2015 +changes: + - fix bug in affine expression normalization + - fix handling of conditional validity constraints +--- +version: 0.14 +date: Sat Oct 25 16:08:47 CEST 2014 +changes: + - support IMath as an optional replacement for GMP + - minor AST generator improvements +--- +version: 0.13 +date: Mon Apr 14 11:08:45 CEST 2014 +changes: + - deprecate isl_int + - improved support for multi piecewise quasi-affine expressions + - allow the user to impose a bound on the number of low-level operations + - add isl_id_to_ast_expr and isl_id_to_pw_aff + - add isl_schedule_constraints + - hide internal structure of isl_vec + - remove support for piplib +--- +version: 0.12.2 +date: Sun Jan 12 12:09:46 CET 2014 +changes: + - MinGW-w64 build fix + - fix simplification bug +--- +version: 0.12.1 +date: Wed Jul 24 12:54:46 CEST 2013 +changes: + - handle malloc returning NULL on zero-size allocation + - fix regression in AST generator +--- +version: 0.12 +date: Sun Jun 23 20:23:05 CEST 2013 +changes: + - add isl_val abstraction +--- +version: 0.11.2 +date: Tue Apr 9 18:45:10 CEST 2013 +changes: + - make code generation output the same on Solaris + - fix some hard to trigger bugs +--- +version: 0.11.1 +date: Mon Dec 10 11:55:30 CET 2012 +changes: + - add LICENSE file to distribution + - make code generation output independent of endianness +--- +version: 0.11 +date: Mon Dec 3 08:17:18 CET 2012 +changes: + - change license from LGPL 2.1 to MIT + - add support for multi piecewise quasi-affine expressions + - add code generation + - various minor bug fixes +--- +version: 0.10 +date: Sun Jun 3 18:00:16 CEST 2012 +changes: + - support for interaction with dependence analysis + - add public API for vectors + - improved support for (piecewise) multi quasi-affine expressions + - various minor bug fixes +--- +version: 0.09 +date: Sat Dec 17 18:19:26 CET 2011 +changes: + - improved argument parsing + - hide internal structure of isl_options + - improved support for parameter sets + - configurable scheduling +--- +version: 0.08 +date: Fri Oct 21 12:36:20 CEST 2011 +changes: + - improved parsing + - drop isl_div abstraction + - rename isl_dim to isl_space + - |- + explicitly differentiate between spaces of maps, + sets and parameter sets + - add support for identifiers + - add support for (piecewise) multi quasi-affine expressions + - preliminary Python bindings +--- +version: 0.07 +date: Tue Jul 12 19:34:51 CEST 2011 +changes: + - hide internal structures of isl_div and isl_constraint + - preliminary scheduling + - add support for local spaces and (piecewise) quasi-affine expressions +--- +version: 0.06 +date: Fri Mar 18 15:59:16 CET 2011 +changes: + - improved parsing + - consistency changes in API + - hide internal structure of isl_ctx +--- +version: 0.05.1 +date: Wed Jan 5 10:21:42 CET 2011 +changes: + - fix simple symmetry detection in parametric integer programming +--- +version: 0.05 +date: Thu Dec 23 17:03:14 CET 2010 +changes: + - rename header files from isl_header.h to isl/header.h + - add higher level interface for dependence analysis + - improved argument parsing + - optionally triangulate domains during Bernstein expansion + - support extended PolyLib format + - hide internal structure of some data types + - improved coalescing + - add simple symmetry detection in parametric integer programming +--- +version: 0.04 +date: Fri Sep 10 12:57:50 CEST 2010 +changes: + - rename isl_pw_qpolynomial_fold_add + - add isl_map_apply_pw_qpolynomial_fold + - support named and nested spaces + - support union sets and maps + - add public API for matrices +--- +version: 0.03 +date: Tue Jun 29 13:16:46 CEST 2010 +changes: + - new printing functions + - support for "may" accesses in dependence analysis + - improved coalescing + - improved transitive closure + - fix several hard to trigger bugs + - improved argument parsing + - support parametric vertex enumeration for barvinok + - optionally use Bernstein expansion to compute bounds Index: lib/Analysis/isl/GIT_HEAD_ID =================================================================== --- /dev/null +++ lib/Analysis/isl/GIT_HEAD_ID @@ -0,0 +1 @@ +isl-0.17.1-84-g72ffe88 Index: lib/Analysis/isl/LICENSE =================================================================== --- /dev/null +++ lib/Analysis/isl/LICENSE @@ -0,0 +1,19 @@ +MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. Index: lib/Analysis/isl/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/Analysis/isl/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/isl/LLVMBuild.txt ----------------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = isl +parent = Libraries +required_libraries = Index: lib/Analysis/isl/Makefile.am =================================================================== --- /dev/null +++ lib/Analysis/isl/Makefile.am @@ -0,0 +1,407 @@ +if HAVE_CLANG + MAYBE_INTERFACE = interface +endif +SUBDIRS = . $(MAYBE_INTERFACE) doc +DIST_SUBDIRS = $(MAYBE_INTERFACE) doc + +ACLOCAL_AMFLAGS = -I m4 +AUTOMAKE_OPTIONS = nostdinc subdir-objects + +lib_LTLIBRARIES = libisl.la +noinst_PROGRAMS = isl_test isl_polyhedron_sample isl_pip \ + isl_polyhedron_minimize isl_polytope_scan \ + isl_polyhedron_detect_equalities isl_cat \ + isl_closure isl_bound isl_codegen isl_test_int +TESTS = isl_test codegen_test.sh pip_test.sh bound_test.sh isl_test_int + +if IMATH_FOR_MP + +MP_SRC = \ + isl_hide_deprecated.h \ + isl_imath.c \ + isl_imath.h \ + isl_int_imath.h \ + imath_wrap/gmp_compat.h \ + imath_wrap/imath.h \ + imath_wrap/imrat.h \ + imath_wrap/wrap.h \ + imath_wrap/gmp_compat.c \ + imath_wrap/imath.c \ + imath_wrap/imrat.c + +noinst_PROGRAMS += isl_test_imath +TESTS += isl_test_imath + +if SMALL_INT_OPT +MP_SRC += isl_int_sioimath.h \ + isl_int_sioimath.c \ + isl_val_sioimath.c +else +MP_SRC += isl_val_imath.c +endif + +DEPRECATED_SRC = +MP_INCLUDE_H = +endif + +if GMP_FOR_MP +if NEED_GET_MEMORY_FUNCTIONS +GET_MEMORY_FUNCTIONS=mp_get_memory_functions.c +endif + +MP_SRC = \ + $(GET_MEMORY_FUNCTIONS) \ + isl_int_gmp.h \ + isl_gmp.c \ + isl_val_gmp.c + +DEPRECATED_SRC = isl_ast_int.c +MP_INCLUDE_H = include/isl/val_gmp.h +endif + +AM_CPPFLAGS = -I. -I$(srcdir) -I$(srcdir)/include -Iinclude/ @MP_CPPFLAGS@ +AM_CFLAGS = @WARNING_FLAGS@ + +libisl_la_SOURCES = \ + $(MP_SRC) \ + $(DEPRECATED_SRC) \ + isl_aff.c \ + isl_aff_private.h \ + isl_affine_hull.c \ + isl_arg.c \ + isl_ast.c \ + isl_ast_private.h \ + isl_ast_build.c \ + isl_ast_build_private.h \ + isl_ast_build_expr.c \ + isl_ast_build_expr.h \ + isl_ast_codegen.c \ + isl_ast_graft.c \ + isl_ast_graft_private.h \ + isl_band.c \ + isl_band_private.h \ + isl_basis_reduction.h \ + basis_reduction_tab.c \ + isl_bernstein.c \ + isl_bernstein.h \ + isl_blk.c \ + isl_blk.h \ + isl_bound.c \ + isl_bound.h \ + isl_coalesce.c \ + isl_constraint.c \ + isl_constraint_private.h \ + isl_convex_hull.c \ + isl_ctx.c \ + isl_ctx_private.h \ + isl_deprecated.c \ + isl_dim_map.h \ + isl_dim_map.c \ + isl_equalities.c \ + isl_equalities.h \ + isl_factorization.c \ + isl_factorization.h \ + isl_farkas.c \ + isl_ffs.c \ + isl_flow.c \ + isl_fold.c \ + isl_hash.c \ + isl_hash_private.h \ + isl_id_to_ast_expr.c \ + isl_id_to_id.c \ + isl_id_to_pw_aff.c \ + isl_ilp.c \ + isl_ilp_private.h \ + isl_input.c \ + isl_int.h \ + isl_local.h \ + isl_local.c \ + isl_local_space_private.h \ + isl_local_space.c \ + isl_lp.c \ + isl_lp_private.h \ + isl_map.c \ + isl_map_list.c \ + isl_map_simplify.c \ + isl_map_subtract.c \ + isl_map_private.h \ + isl_map_to_basic_set.c \ + isl_mat.c \ + isl_mat_private.h \ + isl_morph.c \ + isl_morph.h \ + isl_id.c \ + isl_id_private.h \ + isl_obj.c \ + isl_options.c \ + isl_options_private.h \ + isl_output.c \ + isl_output_private.h \ + isl_point_private.h \ + isl_point.c \ + isl_polynomial_private.h \ + isl_polynomial.c \ + isl_printer_private.h \ + isl_printer.c \ + print.c \ + isl_range.c \ + isl_range.h \ + isl_reordering.c \ + isl_reordering.h \ + isl_sample.h \ + isl_sample.c \ + isl_scan.c \ + isl_scan.h \ + isl_schedule.c \ + isl_schedule_band.c \ + isl_schedule_band.h \ + isl_schedule_node.c \ + isl_schedule_node_private.h \ + isl_schedule_read.c \ + isl_schedule_tree.c \ + isl_schedule_tree.h \ + isl_schedule_private.h \ + isl_scheduler.c \ + isl_set_list.c \ + isl_sort.c \ + isl_sort.h \ + isl_space.c \ + isl_space_private.h \ + isl_stream.c \ + isl_stream_private.h \ + isl_seq.c \ + isl_seq.h \ + isl_tab.c \ + isl_tab.h \ + isl_tab_pip.c \ + isl_tarjan.c \ + isl_tarjan.h \ + isl_transitive_closure.c \ + isl_union_map.c \ + isl_union_map_private.h \ + isl_val.c \ + isl_val_private.h \ + isl_vec_private.h \ + isl_vec.c \ + isl_version.c \ + isl_vertices_private.h \ + isl_vertices.c \ + isl_yaml.h +libisl_la_LIBADD = @MP_LIBS@ +libisl_la_LDFLAGS = -version-info @versioninfo@ \ + @MP_LDFLAGS@ + +isl_test_LDFLAGS = @MP_LDFLAGS@ +isl_test_LDADD = libisl.la @MP_LIBS@ + +isl_test_int_LDFLAGS = @MP_LDFLAGS@ +isl_test_int_LDADD = libisl.la @MP_LIBS@ + +if IMATH_FOR_MP +isl_test_imath_LDFLAGS = @MP_LDFLAGS@ +isl_test_imath_LDADD = libisl.la @MP_LIBS@ +endif + +isl_polyhedron_sample_LDADD = libisl.la +isl_polyhedron_sample_SOURCES = \ + polyhedron_sample.c + +isl_pip_LDFLAGS = @MP_LDFLAGS@ +isl_pip_LDADD = libisl.la @MP_LIBS@ +isl_pip_SOURCES = \ + pip.c + +isl_codegen_LDFLAGS = @MP_LDFLAGS@ +isl_codegen_LDADD = libisl.la @MP_LIBS@ +isl_codegen_SOURCES = \ + codegen.c + +isl_bound_LDFLAGS = @MP_LDFLAGS@ +isl_bound_LDADD = libisl.la @MP_LIBS@ +isl_bound_SOURCES = \ + bound.c + +isl_polyhedron_minimize_LDFLAGS = @MP_LDFLAGS@ +isl_polyhedron_minimize_LDADD = libisl.la @MP_LIBS@ +isl_polyhedron_minimize_SOURCES = \ + polyhedron_minimize.c + +isl_polytope_scan_LDADD = libisl.la +isl_polytope_scan_SOURCES = \ + polytope_scan.c + +isl_polyhedron_detect_equalities_LDADD = libisl.la +isl_polyhedron_detect_equalities_SOURCES = \ + polyhedron_detect_equalities.c + +isl_cat_LDADD = libisl.la +isl_cat_SOURCES = \ + cat.c + +isl_closure_LDADD = libisl.la +isl_closure_SOURCES = \ + closure.c + +nodist_pkginclude_HEADERS = \ + include/isl/stdint.h +pkginclude_HEADERS = \ + $(MP_INCLUDE_H) \ + include/isl/aff.h \ + include/isl/aff_type.h \ + include/isl/arg.h \ + include/isl/ast.h \ + include/isl/ast_type.h \ + include/isl/ast_build.h \ + include/isl/band.h \ + include/isl/constraint.h \ + include/isl/ctx.h \ + include/isl/flow.h \ + include/isl/id.h \ + include/isl/id_to_ast_expr.h \ + include/isl/id_to_id.h \ + include/isl/id_to_pw_aff.h \ + include/isl/ilp.h \ + include/isl/hash.h \ + include/isl/hmap.h \ + include/isl/hmap_templ.c \ + include/isl/list.h \ + include/isl/local_space.h \ + include/isl/lp.h \ + include/isl/mat.h \ + include/isl/map.h \ + include/isl/map_to_basic_set.h \ + include/isl/map_type.h \ + include/isl/maybe.h \ + include/isl/maybe_ast_expr.h \ + include/isl/maybe_basic_set.h \ + include/isl/maybe_id.h \ + include/isl/maybe_pw_aff.h \ + include/isl/maybe_templ.h \ + include/isl/multi.h \ + include/isl/obj.h \ + include/isl/options.h \ + include/isl/point.h \ + include/isl/polynomial.h \ + include/isl/polynomial_type.h \ + include/isl/printer.h \ + include/isl/printer_type.h \ + include/isl/schedule.h \ + include/isl/schedule_node.h \ + include/isl/schedule_type.h \ + include/isl/set.h \ + include/isl/set_type.h \ + include/isl/space.h \ + include/isl/stream.h \ + include/isl/union_map.h \ + include/isl/union_map_type.h \ + include/isl/union_set.h \ + include/isl/union_set_type.h \ + include/isl/val.h \ + include/isl/vec.h \ + include/isl/version.h \ + include/isl/vertices.h +deprecateddir = $(pkgincludedir)/deprecated +deprecated_HEADERS = \ + include/isl/deprecated/int.h \ + include/isl/deprecated/aff_int.h \ + include/isl/deprecated/ast_int.h \ + include/isl/deprecated/constraint_int.h \ + include/isl/deprecated/ilp_int.h \ + include/isl/deprecated/map_int.h \ + include/isl/deprecated/mat_int.h \ + include/isl/deprecated/point_int.h \ + include/isl/deprecated/polynomial_int.h \ + include/isl/deprecated/set_int.h \ + include/isl/deprecated/union_map_int.h \ + include/isl/deprecated/val_int.h \ + include/isl/deprecated/vec_int.h + +BUILT_SOURCES = gitversion.h + +CLEANFILES = \ + gitversion.h + +DISTCLEANFILES = \ + isl-uninstalled.sh \ + isl-uninstalled.pc \ + isl.pc \ + isl.pc.in \ + include/isl/stdint.h + +EXTRA_DIST = \ + LICENSE \ + isl_config_post.h \ + basis_reduction_templ.c \ + isl_list_templ.c \ + isl_list_templ.h \ + isl_map_lexopt_templ.c \ + isl_multi_macro.h \ + isl_multi_templ.c \ + isl_multi_templ.h \ + isl_multi_apply_templ.c \ + isl_multi_apply_set.c \ + isl_multi_apply_union_set.c \ + isl_multi_cmp.c \ + isl_multi_coalesce.c \ + isl_multi_floor.c \ + isl_multi_gist.c \ + isl_multi_hash.c \ + isl_multi_intersect.c \ + print_templ.c \ + isl_power_templ.c \ + isl_pw_macro.h \ + isl_pw_templ.c \ + isl_pw_hash.c \ + isl_pw_union_opt.c \ + isl_union_macro.h \ + isl_union_templ.c \ + isl_union_single.c \ + isl_union_multi.c \ + isl_union_eval.c \ + isl_union_neg.c \ + isl.py \ + doc/CodingStyle \ + doc/SubmittingPatches \ + doc/implementation.tex \ + doc/isl.bib \ + doc/mypod2latex \ + doc/manual.tex \ + doc/reading.tex \ + doc/user.pod \ + imath/gmp_compat.c \ + imath/gmp_compat.h \ + imath/imath.c \ + imath/imath.h \ + imath/imrat.c \ + imath/imrat.h \ + interface/all.h \ + interface/isl.py.top \ + test_inputs + +dist-hook: + echo @GIT_HEAD_VERSION@ > $(distdir)/GIT_HEAD_ID + (cd doc; make manual.pdf) + cp doc/manual.pdf $(distdir)/doc/ + +pkgconfigdir=$(pkgconfig_libdir) +pkgconfig_DATA = $(pkgconfig_libfile) + +gitversion.h: @GIT_HEAD@ + $(AM_V_GEN)echo '#define GIT_HEAD_ID "'@GIT_HEAD_VERSION@'"' > $@ + +install-data-local: $(srcdir)/isl.py + @libisl=`sed -ne "/^library_names=/{s/.*='//;s/'$$//;s/ .*//;p;}" \ + $(builddir)/libisl.la`; \ + case $$libisl in \ + '') echo Cannot find isl library name. GDB bindings not installed.;; \ + *) echo $(INSTALL_DATA) $(srcdir)/isl.py \ + $(DESTDIR)$(libdir)/$$libisl-gdb.py; \ + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"; \ + $(INSTALL_DATA) $(srcdir)/isl.py $(DESTDIR)$(libdir)/$$libisl-gdb.py; esac + +uninstall-local: + @libisl=`sed -ne "/^library_names=/{s/.*='//;s/'$$//;s/ .*//;p;}" \ + $(builddir)/libisl.la`; \ + if test -n "$${libisl}"; then \ + rm -f $(DESTDIR)$(libdir)/$$libisl-gdb.py; \ + fi Index: lib/Analysis/isl/Makefile.in =================================================================== --- /dev/null +++ lib/Analysis/isl/Makefile.in @@ -0,0 +1,2210 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = isl_test$(EXEEXT) isl_polyhedron_sample$(EXEEXT) \ + isl_pip$(EXEEXT) isl_polyhedron_minimize$(EXEEXT) \ + isl_polytope_scan$(EXEEXT) \ + isl_polyhedron_detect_equalities$(EXEEXT) isl_cat$(EXEEXT) \ + isl_closure$(EXEEXT) isl_bound$(EXEEXT) isl_codegen$(EXEEXT) \ + isl_test_int$(EXEEXT) $(am__EXEEXT_1) +TESTS = isl_test$(EXEEXT) codegen_test.sh pip_test.sh bound_test.sh \ + isl_test_int$(EXEEXT) $(am__EXEEXT_1) +@IMATH_FOR_MP_TRUE@am__append_1 = isl_test_imath +@IMATH_FOR_MP_TRUE@am__append_2 = isl_test_imath +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_TRUE@am__append_3 = isl_int_sioimath.h \ +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_TRUE@ isl_int_sioimath.c \ +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_TRUE@ isl_val_sioimath.c + +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_FALSE@am__append_4 = isl_val_imath.c +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_cc_maxopt.m4 \ + $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ + $(top_srcdir)/m4/ax_compiler_vendor.m4 \ + $(top_srcdir)/m4/ax_create_pkgconfig_info.m4 \ + $(top_srcdir)/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/m4/ax_detect_git_head.m4 \ + $(top_srcdir)/m4/ax_detect_gmp.m4 \ + $(top_srcdir)/m4/ax_detect_imath.m4 \ + $(top_srcdir)/m4/ax_gcc_archflag.m4 \ + $(top_srcdir)/m4/ax_gcc_warn_unused_result.m4 \ + $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ + $(top_srcdir)/m4/ax_set_warning_flags.m4 \ + $(top_srcdir)/m4/ax_submodule.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(deprecated_HEADERS) \ + $(am__pkginclude_HEADERS_DIST) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = isl_config.h +CONFIG_CLEAN_FILES = bound_test.sh codegen_test.sh pip_test.sh +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \ + "$(DESTDIR)$(deprecateddir)" "$(DESTDIR)$(pkgincludedir)" \ + "$(DESTDIR)$(pkgincludedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libisl_la_DEPENDENCIES = +am__libisl_la_SOURCES_DIST = mp_get_memory_functions.c isl_int_gmp.h \ + isl_gmp.c isl_val_gmp.c isl_hide_deprecated.h isl_imath.c \ + isl_imath.h isl_int_imath.h imath_wrap/gmp_compat.h \ + imath_wrap/imath.h imath_wrap/imrat.h imath_wrap/wrap.h \ + imath_wrap/gmp_compat.c imath_wrap/imath.c imath_wrap/imrat.c \ + isl_int_sioimath.h isl_int_sioimath.c isl_val_sioimath.c \ + isl_val_imath.c isl_ast_int.c isl_aff.c isl_aff_private.h \ + isl_affine_hull.c isl_arg.c isl_ast.c isl_ast_private.h \ + isl_ast_build.c isl_ast_build_private.h isl_ast_build_expr.c \ + isl_ast_build_expr.h isl_ast_codegen.c isl_ast_graft.c \ + isl_ast_graft_private.h isl_band.c isl_band_private.h \ + isl_basis_reduction.h basis_reduction_tab.c isl_bernstein.c \ + isl_bernstein.h isl_blk.c isl_blk.h isl_bound.c isl_bound.h \ + isl_coalesce.c isl_constraint.c isl_constraint_private.h \ + isl_convex_hull.c isl_ctx.c isl_ctx_private.h isl_deprecated.c \ + isl_dim_map.h isl_dim_map.c isl_equalities.c isl_equalities.h \ + isl_factorization.c isl_factorization.h isl_farkas.c isl_ffs.c \ + isl_flow.c isl_fold.c isl_hash.c isl_hash_private.h \ + isl_id_to_ast_expr.c isl_id_to_id.c isl_id_to_pw_aff.c \ + isl_ilp.c isl_ilp_private.h isl_input.c isl_int.h isl_local.h \ + isl_local.c isl_local_space_private.h isl_local_space.c \ + isl_lp.c isl_lp_private.h isl_map.c isl_map_list.c \ + isl_map_simplify.c isl_map_subtract.c isl_map_private.h \ + isl_map_to_basic_set.c isl_mat.c isl_mat_private.h isl_morph.c \ + isl_morph.h isl_id.c isl_id_private.h isl_obj.c isl_options.c \ + isl_options_private.h isl_output.c isl_output_private.h \ + isl_point_private.h isl_point.c isl_polynomial_private.h \ + isl_polynomial.c isl_printer_private.h isl_printer.c print.c \ + isl_range.c isl_range.h isl_reordering.c isl_reordering.h \ + isl_sample.h isl_sample.c isl_scan.c isl_scan.h isl_schedule.c \ + isl_schedule_band.c isl_schedule_band.h isl_schedule_node.c \ + isl_schedule_node_private.h isl_schedule_read.c \ + isl_schedule_tree.c isl_schedule_tree.h isl_schedule_private.h \ + isl_scheduler.c isl_set_list.c isl_sort.c isl_sort.h \ + isl_space.c isl_space_private.h isl_stream.c \ + isl_stream_private.h isl_seq.c isl_seq.h isl_tab.c isl_tab.h \ + isl_tab_pip.c isl_tarjan.c isl_tarjan.h \ + isl_transitive_closure.c isl_union_map.c \ + isl_union_map_private.h isl_val.c isl_val_private.h \ + isl_vec_private.h isl_vec.c isl_version.c \ + isl_vertices_private.h isl_vertices.c isl_yaml.h +@GMP_FOR_MP_TRUE@@NEED_GET_MEMORY_FUNCTIONS_TRUE@am__objects_1 = mp_get_memory_functions.lo +am__dirstamp = $(am__leading_dot)dirstamp +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_TRUE@am__objects_2 = \ +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_TRUE@ isl_int_sioimath.lo \ +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_TRUE@ isl_val_sioimath.lo +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_FALSE@am__objects_3 = \ +@IMATH_FOR_MP_TRUE@@SMALL_INT_OPT_FALSE@ isl_val_imath.lo +@GMP_FOR_MP_FALSE@@IMATH_FOR_MP_TRUE@am__objects_4 = isl_imath.lo \ +@GMP_FOR_MP_FALSE@@IMATH_FOR_MP_TRUE@ imath_wrap/gmp_compat.lo \ +@GMP_FOR_MP_FALSE@@IMATH_FOR_MP_TRUE@ imath_wrap/imath.lo \ +@GMP_FOR_MP_FALSE@@IMATH_FOR_MP_TRUE@ imath_wrap/imrat.lo \ +@GMP_FOR_MP_FALSE@@IMATH_FOR_MP_TRUE@ $(am__objects_2) \ +@GMP_FOR_MP_FALSE@@IMATH_FOR_MP_TRUE@ $(am__objects_3) +@GMP_FOR_MP_TRUE@am__objects_4 = $(am__objects_1) isl_gmp.lo \ +@GMP_FOR_MP_TRUE@ isl_val_gmp.lo +@GMP_FOR_MP_TRUE@am__objects_5 = isl_ast_int.lo +am_libisl_la_OBJECTS = $(am__objects_4) $(am__objects_5) isl_aff.lo \ + isl_affine_hull.lo isl_arg.lo isl_ast.lo isl_ast_build.lo \ + isl_ast_build_expr.lo isl_ast_codegen.lo isl_ast_graft.lo \ + isl_band.lo basis_reduction_tab.lo isl_bernstein.lo isl_blk.lo \ + isl_bound.lo isl_coalesce.lo isl_constraint.lo \ + isl_convex_hull.lo isl_ctx.lo isl_deprecated.lo isl_dim_map.lo \ + isl_equalities.lo isl_factorization.lo isl_farkas.lo \ + isl_ffs.lo isl_flow.lo isl_fold.lo isl_hash.lo \ + isl_id_to_ast_expr.lo isl_id_to_id.lo isl_id_to_pw_aff.lo \ + isl_ilp.lo isl_input.lo isl_local.lo isl_local_space.lo \ + isl_lp.lo isl_map.lo isl_map_list.lo isl_map_simplify.lo \ + isl_map_subtract.lo isl_map_to_basic_set.lo isl_mat.lo \ + isl_morph.lo isl_id.lo isl_obj.lo isl_options.lo isl_output.lo \ + isl_point.lo isl_polynomial.lo isl_printer.lo print.lo \ + isl_range.lo isl_reordering.lo isl_sample.lo isl_scan.lo \ + isl_schedule.lo isl_schedule_band.lo isl_schedule_node.lo \ + isl_schedule_read.lo isl_schedule_tree.lo isl_scheduler.lo \ + isl_set_list.lo isl_sort.lo isl_space.lo isl_stream.lo \ + isl_seq.lo isl_tab.lo isl_tab_pip.lo isl_tarjan.lo \ + isl_transitive_closure.lo isl_union_map.lo isl_val.lo \ + isl_vec.lo isl_version.lo isl_vertices.lo +libisl_la_OBJECTS = $(am_libisl_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libisl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libisl_la_LDFLAGS) $(LDFLAGS) -o $@ +@IMATH_FOR_MP_TRUE@am__EXEEXT_1 = isl_test_imath$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am_isl_bound_OBJECTS = bound.$(OBJEXT) +isl_bound_OBJECTS = $(am_isl_bound_OBJECTS) +isl_bound_DEPENDENCIES = libisl.la +isl_bound_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(isl_bound_LDFLAGS) $(LDFLAGS) -o $@ +am_isl_cat_OBJECTS = cat.$(OBJEXT) +isl_cat_OBJECTS = $(am_isl_cat_OBJECTS) +isl_cat_DEPENDENCIES = libisl.la +am_isl_closure_OBJECTS = closure.$(OBJEXT) +isl_closure_OBJECTS = $(am_isl_closure_OBJECTS) +isl_closure_DEPENDENCIES = libisl.la +am_isl_codegen_OBJECTS = codegen.$(OBJEXT) +isl_codegen_OBJECTS = $(am_isl_codegen_OBJECTS) +isl_codegen_DEPENDENCIES = libisl.la +isl_codegen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(isl_codegen_LDFLAGS) $(LDFLAGS) -o $@ +am_isl_pip_OBJECTS = pip.$(OBJEXT) +isl_pip_OBJECTS = $(am_isl_pip_OBJECTS) +isl_pip_DEPENDENCIES = libisl.la +isl_pip_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(isl_pip_LDFLAGS) $(LDFLAGS) -o $@ +am_isl_polyhedron_detect_equalities_OBJECTS = \ + polyhedron_detect_equalities.$(OBJEXT) +isl_polyhedron_detect_equalities_OBJECTS = \ + $(am_isl_polyhedron_detect_equalities_OBJECTS) +isl_polyhedron_detect_equalities_DEPENDENCIES = libisl.la +am_isl_polyhedron_minimize_OBJECTS = polyhedron_minimize.$(OBJEXT) +isl_polyhedron_minimize_OBJECTS = \ + $(am_isl_polyhedron_minimize_OBJECTS) +isl_polyhedron_minimize_DEPENDENCIES = libisl.la +isl_polyhedron_minimize_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(isl_polyhedron_minimize_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_isl_polyhedron_sample_OBJECTS = polyhedron_sample.$(OBJEXT) +isl_polyhedron_sample_OBJECTS = $(am_isl_polyhedron_sample_OBJECTS) +isl_polyhedron_sample_DEPENDENCIES = libisl.la +am_isl_polytope_scan_OBJECTS = polytope_scan.$(OBJEXT) +isl_polytope_scan_OBJECTS = $(am_isl_polytope_scan_OBJECTS) +isl_polytope_scan_DEPENDENCIES = libisl.la +isl_test_SOURCES = isl_test.c +isl_test_OBJECTS = isl_test.$(OBJEXT) +isl_test_DEPENDENCIES = libisl.la +isl_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(isl_test_LDFLAGS) $(LDFLAGS) -o $@ +isl_test_imath_SOURCES = isl_test_imath.c +isl_test_imath_OBJECTS = isl_test_imath.$(OBJEXT) +@IMATH_FOR_MP_TRUE@isl_test_imath_DEPENDENCIES = libisl.la +isl_test_imath_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(isl_test_imath_LDFLAGS) $(LDFLAGS) -o \ + $@ +isl_test_int_SOURCES = isl_test_int.c +isl_test_int_OBJECTS = isl_test_int.$(OBJEXT) +isl_test_int_DEPENDENCIES = libisl.la +isl_test_int_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(isl_test_int_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libisl_la_SOURCES) $(isl_bound_SOURCES) $(isl_cat_SOURCES) \ + $(isl_closure_SOURCES) $(isl_codegen_SOURCES) \ + $(isl_pip_SOURCES) $(isl_polyhedron_detect_equalities_SOURCES) \ + $(isl_polyhedron_minimize_SOURCES) \ + $(isl_polyhedron_sample_SOURCES) $(isl_polytope_scan_SOURCES) \ + isl_test.c isl_test_imath.c isl_test_int.c +DIST_SOURCES = $(am__libisl_la_SOURCES_DIST) $(isl_bound_SOURCES) \ + $(isl_cat_SOURCES) $(isl_closure_SOURCES) \ + $(isl_codegen_SOURCES) $(isl_pip_SOURCES) \ + $(isl_polyhedron_detect_equalities_SOURCES) \ + $(isl_polyhedron_minimize_SOURCES) \ + $(isl_polyhedron_sample_SOURCES) $(isl_polytope_scan_SOURCES) \ + isl_test.c isl_test_imath.c isl_test_int.c +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +am__pkginclude_HEADERS_DIST = include/isl/val_gmp.h include/isl/aff.h \ + include/isl/aff_type.h include/isl/arg.h include/isl/ast.h \ + include/isl/ast_type.h include/isl/ast_build.h \ + include/isl/band.h include/isl/constraint.h include/isl/ctx.h \ + include/isl/flow.h include/isl/id.h \ + include/isl/id_to_ast_expr.h include/isl/id_to_id.h \ + include/isl/id_to_pw_aff.h include/isl/ilp.h \ + include/isl/hash.h include/isl/hmap.h include/isl/hmap_templ.c \ + include/isl/list.h include/isl/local_space.h include/isl/lp.h \ + include/isl/mat.h include/isl/map.h \ + include/isl/map_to_basic_set.h include/isl/map_type.h \ + include/isl/maybe.h include/isl/maybe_ast_expr.h \ + include/isl/maybe_basic_set.h include/isl/maybe_id.h \ + include/isl/maybe_pw_aff.h include/isl/maybe_templ.h \ + include/isl/multi.h include/isl/obj.h include/isl/options.h \ + include/isl/point.h include/isl/polynomial.h \ + include/isl/polynomial_type.h include/isl/printer.h \ + include/isl/printer_type.h include/isl/schedule.h \ + include/isl/schedule_node.h include/isl/schedule_type.h \ + include/isl/set.h include/isl/set_type.h include/isl/space.h \ + include/isl/stream.h include/isl/union_map.h \ + include/isl/union_map_type.h include/isl/union_set.h \ + include/isl/union_set_type.h include/isl/val.h \ + include/isl/vec.h include/isl/version.h include/isl/vertices.h +HEADERS = $(deprecated_HEADERS) $(nodist_pkginclude_HEADERS) \ + $(pkginclude_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope check recheck distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)isl_config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/bound_test.sh.in \ + $(srcdir)/codegen_test.sh.in $(srcdir)/isl_config.h.in \ + $(srcdir)/pip_test.sh.in AUTHORS ChangeLog README compile \ + config.guess config.sub depcomp install-sh ltmain.sh missing \ + test-driver +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CLANG_CXXFLAGS = @CLANG_CXXFLAGS@ +CLANG_LDFLAGS = @CLANG_LDFLAGS@ +CLANG_LIBS = @CLANG_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GIT_HEAD = @GIT_HEAD@ +GIT_HEAD_ID = @GIT_HEAD_ID@ +GIT_HEAD_VERSION = @GIT_HEAD_VERSION@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIB_CLANG_EDIT = @LIB_CLANG_EDIT@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MP_CPPFLAGS = @MP_CPPFLAGS@ +MP_LDFLAGS = @MP_LDFLAGS@ +MP_LIBS = @MP_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +POD2HTML = @POD2HTML@ +PRTDIAG = @PRTDIAG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WARNING_FLAGS = @WARNING_FLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +llvm_config_found = @llvm_config_found@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfig_libdir = @pkgconfig_libdir@ +pkgconfig_libfile = @pkgconfig_libfile@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +versioninfo = @versioninfo@ +@HAVE_CLANG_TRUE@MAYBE_INTERFACE = interface +SUBDIRS = . $(MAYBE_INTERFACE) doc +DIST_SUBDIRS = $(MAYBE_INTERFACE) doc +ACLOCAL_AMFLAGS = -I m4 +AUTOMAKE_OPTIONS = nostdinc subdir-objects +lib_LTLIBRARIES = libisl.la +@GMP_FOR_MP_TRUE@MP_SRC = \ +@GMP_FOR_MP_TRUE@ $(GET_MEMORY_FUNCTIONS) \ +@GMP_FOR_MP_TRUE@ isl_int_gmp.h \ +@GMP_FOR_MP_TRUE@ isl_gmp.c \ +@GMP_FOR_MP_TRUE@ isl_val_gmp.c + +@IMATH_FOR_MP_TRUE@MP_SRC = isl_hide_deprecated.h isl_imath.c \ +@IMATH_FOR_MP_TRUE@ isl_imath.h isl_int_imath.h \ +@IMATH_FOR_MP_TRUE@ imath_wrap/gmp_compat.h imath_wrap/imath.h \ +@IMATH_FOR_MP_TRUE@ imath_wrap/imrat.h imath_wrap/wrap.h \ +@IMATH_FOR_MP_TRUE@ imath_wrap/gmp_compat.c imath_wrap/imath.c \ +@IMATH_FOR_MP_TRUE@ imath_wrap/imrat.c $(am__append_3) \ +@IMATH_FOR_MP_TRUE@ $(am__append_4) +@GMP_FOR_MP_TRUE@DEPRECATED_SRC = isl_ast_int.c +@IMATH_FOR_MP_TRUE@DEPRECATED_SRC = +@GMP_FOR_MP_TRUE@MP_INCLUDE_H = include/isl/val_gmp.h +@IMATH_FOR_MP_TRUE@MP_INCLUDE_H = +@GMP_FOR_MP_TRUE@@NEED_GET_MEMORY_FUNCTIONS_TRUE@GET_MEMORY_FUNCTIONS = mp_get_memory_functions.c +AM_CPPFLAGS = -I. -I$(srcdir) -I$(srcdir)/include -Iinclude/ @MP_CPPFLAGS@ +AM_CFLAGS = @WARNING_FLAGS@ +libisl_la_SOURCES = \ + $(MP_SRC) \ + $(DEPRECATED_SRC) \ + isl_aff.c \ + isl_aff_private.h \ + isl_affine_hull.c \ + isl_arg.c \ + isl_ast.c \ + isl_ast_private.h \ + isl_ast_build.c \ + isl_ast_build_private.h \ + isl_ast_build_expr.c \ + isl_ast_build_expr.h \ + isl_ast_codegen.c \ + isl_ast_graft.c \ + isl_ast_graft_private.h \ + isl_band.c \ + isl_band_private.h \ + isl_basis_reduction.h \ + basis_reduction_tab.c \ + isl_bernstein.c \ + isl_bernstein.h \ + isl_blk.c \ + isl_blk.h \ + isl_bound.c \ + isl_bound.h \ + isl_coalesce.c \ + isl_constraint.c \ + isl_constraint_private.h \ + isl_convex_hull.c \ + isl_ctx.c \ + isl_ctx_private.h \ + isl_deprecated.c \ + isl_dim_map.h \ + isl_dim_map.c \ + isl_equalities.c \ + isl_equalities.h \ + isl_factorization.c \ + isl_factorization.h \ + isl_farkas.c \ + isl_ffs.c \ + isl_flow.c \ + isl_fold.c \ + isl_hash.c \ + isl_hash_private.h \ + isl_id_to_ast_expr.c \ + isl_id_to_id.c \ + isl_id_to_pw_aff.c \ + isl_ilp.c \ + isl_ilp_private.h \ + isl_input.c \ + isl_int.h \ + isl_local.h \ + isl_local.c \ + isl_local_space_private.h \ + isl_local_space.c \ + isl_lp.c \ + isl_lp_private.h \ + isl_map.c \ + isl_map_list.c \ + isl_map_simplify.c \ + isl_map_subtract.c \ + isl_map_private.h \ + isl_map_to_basic_set.c \ + isl_mat.c \ + isl_mat_private.h \ + isl_morph.c \ + isl_morph.h \ + isl_id.c \ + isl_id_private.h \ + isl_obj.c \ + isl_options.c \ + isl_options_private.h \ + isl_output.c \ + isl_output_private.h \ + isl_point_private.h \ + isl_point.c \ + isl_polynomial_private.h \ + isl_polynomial.c \ + isl_printer_private.h \ + isl_printer.c \ + print.c \ + isl_range.c \ + isl_range.h \ + isl_reordering.c \ + isl_reordering.h \ + isl_sample.h \ + isl_sample.c \ + isl_scan.c \ + isl_scan.h \ + isl_schedule.c \ + isl_schedule_band.c \ + isl_schedule_band.h \ + isl_schedule_node.c \ + isl_schedule_node_private.h \ + isl_schedule_read.c \ + isl_schedule_tree.c \ + isl_schedule_tree.h \ + isl_schedule_private.h \ + isl_scheduler.c \ + isl_set_list.c \ + isl_sort.c \ + isl_sort.h \ + isl_space.c \ + isl_space_private.h \ + isl_stream.c \ + isl_stream_private.h \ + isl_seq.c \ + isl_seq.h \ + isl_tab.c \ + isl_tab.h \ + isl_tab_pip.c \ + isl_tarjan.c \ + isl_tarjan.h \ + isl_transitive_closure.c \ + isl_union_map.c \ + isl_union_map_private.h \ + isl_val.c \ + isl_val_private.h \ + isl_vec_private.h \ + isl_vec.c \ + isl_version.c \ + isl_vertices_private.h \ + isl_vertices.c \ + isl_yaml.h + +libisl_la_LIBADD = @MP_LIBS@ +libisl_la_LDFLAGS = -version-info @versioninfo@ \ + @MP_LDFLAGS@ + +isl_test_LDFLAGS = @MP_LDFLAGS@ +isl_test_LDADD = libisl.la @MP_LIBS@ +isl_test_int_LDFLAGS = @MP_LDFLAGS@ +isl_test_int_LDADD = libisl.la @MP_LIBS@ +@IMATH_FOR_MP_TRUE@isl_test_imath_LDFLAGS = @MP_LDFLAGS@ +@IMATH_FOR_MP_TRUE@isl_test_imath_LDADD = libisl.la @MP_LIBS@ +isl_polyhedron_sample_LDADD = libisl.la +isl_polyhedron_sample_SOURCES = \ + polyhedron_sample.c + +isl_pip_LDFLAGS = @MP_LDFLAGS@ +isl_pip_LDADD = libisl.la @MP_LIBS@ +isl_pip_SOURCES = \ + pip.c + +isl_codegen_LDFLAGS = @MP_LDFLAGS@ +isl_codegen_LDADD = libisl.la @MP_LIBS@ +isl_codegen_SOURCES = \ + codegen.c + +isl_bound_LDFLAGS = @MP_LDFLAGS@ +isl_bound_LDADD = libisl.la @MP_LIBS@ +isl_bound_SOURCES = \ + bound.c + +isl_polyhedron_minimize_LDFLAGS = @MP_LDFLAGS@ +isl_polyhedron_minimize_LDADD = libisl.la @MP_LIBS@ +isl_polyhedron_minimize_SOURCES = \ + polyhedron_minimize.c + +isl_polytope_scan_LDADD = libisl.la +isl_polytope_scan_SOURCES = \ + polytope_scan.c + +isl_polyhedron_detect_equalities_LDADD = libisl.la +isl_polyhedron_detect_equalities_SOURCES = \ + polyhedron_detect_equalities.c + +isl_cat_LDADD = libisl.la +isl_cat_SOURCES = \ + cat.c + +isl_closure_LDADD = libisl.la +isl_closure_SOURCES = \ + closure.c + +nodist_pkginclude_HEADERS = \ + include/isl/stdint.h + +pkginclude_HEADERS = \ + $(MP_INCLUDE_H) \ + include/isl/aff.h \ + include/isl/aff_type.h \ + include/isl/arg.h \ + include/isl/ast.h \ + include/isl/ast_type.h \ + include/isl/ast_build.h \ + include/isl/band.h \ + include/isl/constraint.h \ + include/isl/ctx.h \ + include/isl/flow.h \ + include/isl/id.h \ + include/isl/id_to_ast_expr.h \ + include/isl/id_to_id.h \ + include/isl/id_to_pw_aff.h \ + include/isl/ilp.h \ + include/isl/hash.h \ + include/isl/hmap.h \ + include/isl/hmap_templ.c \ + include/isl/list.h \ + include/isl/local_space.h \ + include/isl/lp.h \ + include/isl/mat.h \ + include/isl/map.h \ + include/isl/map_to_basic_set.h \ + include/isl/map_type.h \ + include/isl/maybe.h \ + include/isl/maybe_ast_expr.h \ + include/isl/maybe_basic_set.h \ + include/isl/maybe_id.h \ + include/isl/maybe_pw_aff.h \ + include/isl/maybe_templ.h \ + include/isl/multi.h \ + include/isl/obj.h \ + include/isl/options.h \ + include/isl/point.h \ + include/isl/polynomial.h \ + include/isl/polynomial_type.h \ + include/isl/printer.h \ + include/isl/printer_type.h \ + include/isl/schedule.h \ + include/isl/schedule_node.h \ + include/isl/schedule_type.h \ + include/isl/set.h \ + include/isl/set_type.h \ + include/isl/space.h \ + include/isl/stream.h \ + include/isl/union_map.h \ + include/isl/union_map_type.h \ + include/isl/union_set.h \ + include/isl/union_set_type.h \ + include/isl/val.h \ + include/isl/vec.h \ + include/isl/version.h \ + include/isl/vertices.h + +deprecateddir = $(pkgincludedir)/deprecated +deprecated_HEADERS = \ + include/isl/deprecated/int.h \ + include/isl/deprecated/aff_int.h \ + include/isl/deprecated/ast_int.h \ + include/isl/deprecated/constraint_int.h \ + include/isl/deprecated/ilp_int.h \ + include/isl/deprecated/map_int.h \ + include/isl/deprecated/mat_int.h \ + include/isl/deprecated/point_int.h \ + include/isl/deprecated/polynomial_int.h \ + include/isl/deprecated/set_int.h \ + include/isl/deprecated/union_map_int.h \ + include/isl/deprecated/val_int.h \ + include/isl/deprecated/vec_int.h + +BUILT_SOURCES = gitversion.h +CLEANFILES = \ + gitversion.h + +DISTCLEANFILES = \ + isl-uninstalled.sh \ + isl-uninstalled.pc \ + isl.pc \ + isl.pc.in \ + include/isl/stdint.h + +EXTRA_DIST = \ + LICENSE \ + isl_config_post.h \ + basis_reduction_templ.c \ + isl_list_templ.c \ + isl_list_templ.h \ + isl_map_lexopt_templ.c \ + isl_multi_macro.h \ + isl_multi_templ.c \ + isl_multi_templ.h \ + isl_multi_apply_templ.c \ + isl_multi_apply_set.c \ + isl_multi_apply_union_set.c \ + isl_multi_cmp.c \ + isl_multi_coalesce.c \ + isl_multi_floor.c \ + isl_multi_gist.c \ + isl_multi_hash.c \ + isl_multi_intersect.c \ + print_templ.c \ + isl_power_templ.c \ + isl_pw_macro.h \ + isl_pw_templ.c \ + isl_pw_hash.c \ + isl_pw_union_opt.c \ + isl_union_macro.h \ + isl_union_templ.c \ + isl_union_single.c \ + isl_union_multi.c \ + isl_union_eval.c \ + isl_union_neg.c \ + isl.py \ + doc/CodingStyle \ + doc/SubmittingPatches \ + doc/implementation.tex \ + doc/isl.bib \ + doc/mypod2latex \ + doc/manual.tex \ + doc/reading.tex \ + doc/user.pod \ + imath/gmp_compat.c \ + imath/gmp_compat.h \ + imath/imath.c \ + imath/imath.h \ + imath/imrat.c \ + imath/imrat.h \ + interface/all.h \ + interface/isl.py.top \ + test_inputs + +pkgconfigdir = $(pkgconfig_libdir) +pkgconfig_DATA = $(pkgconfig_libfile) +all: $(BUILT_SOURCES) isl_config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +isl_config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/isl_config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status isl_config.h +$(srcdir)/isl_config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f isl_config.h stamp-h1 +bound_test.sh: $(top_builddir)/config.status $(srcdir)/bound_test.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +codegen_test.sh: $(top_builddir)/config.status $(srcdir)/codegen_test.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +pip_test.sh: $(top_builddir)/config.status $(srcdir)/pip_test.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +imath_wrap/$(am__dirstamp): + @$(MKDIR_P) imath_wrap + @: > imath_wrap/$(am__dirstamp) +imath_wrap/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) imath_wrap/$(DEPDIR) + @: > imath_wrap/$(DEPDIR)/$(am__dirstamp) +imath_wrap/gmp_compat.lo: imath_wrap/$(am__dirstamp) \ + imath_wrap/$(DEPDIR)/$(am__dirstamp) +imath_wrap/imath.lo: imath_wrap/$(am__dirstamp) \ + imath_wrap/$(DEPDIR)/$(am__dirstamp) +imath_wrap/imrat.lo: imath_wrap/$(am__dirstamp) \ + imath_wrap/$(DEPDIR)/$(am__dirstamp) + +libisl.la: $(libisl_la_OBJECTS) $(libisl_la_DEPENDENCIES) $(EXTRA_libisl_la_DEPENDENCIES) + $(AM_V_CCLD)$(libisl_la_LINK) -rpath $(libdir) $(libisl_la_OBJECTS) $(libisl_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +isl_bound$(EXEEXT): $(isl_bound_OBJECTS) $(isl_bound_DEPENDENCIES) $(EXTRA_isl_bound_DEPENDENCIES) + @rm -f isl_bound$(EXEEXT) + $(AM_V_CCLD)$(isl_bound_LINK) $(isl_bound_OBJECTS) $(isl_bound_LDADD) $(LIBS) + +isl_cat$(EXEEXT): $(isl_cat_OBJECTS) $(isl_cat_DEPENDENCIES) $(EXTRA_isl_cat_DEPENDENCIES) + @rm -f isl_cat$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(isl_cat_OBJECTS) $(isl_cat_LDADD) $(LIBS) + +isl_closure$(EXEEXT): $(isl_closure_OBJECTS) $(isl_closure_DEPENDENCIES) $(EXTRA_isl_closure_DEPENDENCIES) + @rm -f isl_closure$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(isl_closure_OBJECTS) $(isl_closure_LDADD) $(LIBS) + +isl_codegen$(EXEEXT): $(isl_codegen_OBJECTS) $(isl_codegen_DEPENDENCIES) $(EXTRA_isl_codegen_DEPENDENCIES) + @rm -f isl_codegen$(EXEEXT) + $(AM_V_CCLD)$(isl_codegen_LINK) $(isl_codegen_OBJECTS) $(isl_codegen_LDADD) $(LIBS) + +isl_pip$(EXEEXT): $(isl_pip_OBJECTS) $(isl_pip_DEPENDENCIES) $(EXTRA_isl_pip_DEPENDENCIES) + @rm -f isl_pip$(EXEEXT) + $(AM_V_CCLD)$(isl_pip_LINK) $(isl_pip_OBJECTS) $(isl_pip_LDADD) $(LIBS) + +isl_polyhedron_detect_equalities$(EXEEXT): $(isl_polyhedron_detect_equalities_OBJECTS) $(isl_polyhedron_detect_equalities_DEPENDENCIES) $(EXTRA_isl_polyhedron_detect_equalities_DEPENDENCIES) + @rm -f isl_polyhedron_detect_equalities$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(isl_polyhedron_detect_equalities_OBJECTS) $(isl_polyhedron_detect_equalities_LDADD) $(LIBS) + +isl_polyhedron_minimize$(EXEEXT): $(isl_polyhedron_minimize_OBJECTS) $(isl_polyhedron_minimize_DEPENDENCIES) $(EXTRA_isl_polyhedron_minimize_DEPENDENCIES) + @rm -f isl_polyhedron_minimize$(EXEEXT) + $(AM_V_CCLD)$(isl_polyhedron_minimize_LINK) $(isl_polyhedron_minimize_OBJECTS) $(isl_polyhedron_minimize_LDADD) $(LIBS) + +isl_polyhedron_sample$(EXEEXT): $(isl_polyhedron_sample_OBJECTS) $(isl_polyhedron_sample_DEPENDENCIES) $(EXTRA_isl_polyhedron_sample_DEPENDENCIES) + @rm -f isl_polyhedron_sample$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(isl_polyhedron_sample_OBJECTS) $(isl_polyhedron_sample_LDADD) $(LIBS) + +isl_polytope_scan$(EXEEXT): $(isl_polytope_scan_OBJECTS) $(isl_polytope_scan_DEPENDENCIES) $(EXTRA_isl_polytope_scan_DEPENDENCIES) + @rm -f isl_polytope_scan$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(isl_polytope_scan_OBJECTS) $(isl_polytope_scan_LDADD) $(LIBS) + +isl_test$(EXEEXT): $(isl_test_OBJECTS) $(isl_test_DEPENDENCIES) $(EXTRA_isl_test_DEPENDENCIES) + @rm -f isl_test$(EXEEXT) + $(AM_V_CCLD)$(isl_test_LINK) $(isl_test_OBJECTS) $(isl_test_LDADD) $(LIBS) + +isl_test_imath$(EXEEXT): $(isl_test_imath_OBJECTS) $(isl_test_imath_DEPENDENCIES) $(EXTRA_isl_test_imath_DEPENDENCIES) + @rm -f isl_test_imath$(EXEEXT) + $(AM_V_CCLD)$(isl_test_imath_LINK) $(isl_test_imath_OBJECTS) $(isl_test_imath_LDADD) $(LIBS) + +isl_test_int$(EXEEXT): $(isl_test_int_OBJECTS) $(isl_test_int_DEPENDENCIES) $(EXTRA_isl_test_int_DEPENDENCIES) + @rm -f isl_test_int$(EXEEXT) + $(AM_V_CCLD)$(isl_test_int_LINK) $(isl_test_int_OBJECTS) $(isl_test_int_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f imath_wrap/*.$(OBJEXT) + -rm -f imath_wrap/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basis_reduction_tab.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bound.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/closure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/codegen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_aff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_affine_hull.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_arg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ast.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ast_build.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ast_build_expr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ast_codegen.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ast_graft.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ast_int.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_band.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_bernstein.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_blk.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_bound.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_coalesce.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_constraint.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_convex_hull.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ctx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_deprecated.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_dim_map.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_equalities.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_factorization.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_farkas.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ffs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_flow.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_fold.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_gmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_id_to_ast_expr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_id_to_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_id_to_pw_aff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_ilp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_imath.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_input.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_int_sioimath.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_local.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_local_space.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_lp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_map.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_map_list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_map_simplify.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_map_subtract.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_map_to_basic_set.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_mat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_morph.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_obj.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_options.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_output.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_point.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_polynomial.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_printer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_range.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_reordering.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_sample.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_scan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_schedule.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_schedule_band.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_schedule_node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_schedule_read.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_schedule_tree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_scheduler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_seq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_set_list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_sort.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_space.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_tab.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_tab_pip.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_tarjan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_test_imath.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_test_int.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_transitive_closure.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_union_map.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_val.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_val_gmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_val_imath.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_val_sioimath.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_vec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_version.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isl_vertices.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mp_get_memory_functions.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyhedron_detect_equalities.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyhedron_minimize.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyhedron_sample.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polytope_scan.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@imath_wrap/$(DEPDIR)/gmp_compat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@imath_wrap/$(DEPDIR)/imath.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@imath_wrap/$(DEPDIR)/imrat.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf imath_wrap/.libs imath_wrap/_libs + +distclean-libtool: + -rm -f libtool config.lt +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +install-deprecatedHEADERS: $(deprecated_HEADERS) + @$(NORMAL_INSTALL) + @list='$(deprecated_HEADERS)'; test -n "$(deprecateddir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(deprecateddir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(deprecateddir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(deprecateddir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(deprecateddir)" || exit $$?; \ + done + +uninstall-deprecatedHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(deprecated_HEADERS)'; test -n "$(deprecateddir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(deprecateddir)'; $(am__uninstall_files_from_dir) +install-nodist_pkgincludeHEADERS: $(nodist_pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-nodist_pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +isl_test.log: isl_test$(EXEEXT) + @p='isl_test$(EXEEXT)'; \ + b='isl_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +codegen_test.sh.log: codegen_test.sh + @p='codegen_test.sh'; \ + b='codegen_test.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +pip_test.sh.log: pip_test.sh + @p='pip_test.sh'; \ + b='pip_test.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +bound_test.sh.log: bound_test.sh + @p='bound_test.sh'; \ + b='bound_test.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +isl_test_int.log: isl_test_int$(EXEEXT) + @p='isl_test_int$(EXEEXT)'; \ + b='isl_test_int'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +isl_test_imath.log: isl_test_imath$(EXEEXT) + @p='isl_test_imath$(EXEEXT)'; \ + b='isl_test_imath'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-recursive +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) $(HEADERS) \ + isl_config.h +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(deprecateddir)" "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f imath_wrap/$(DEPDIR)/$(am__dirstamp) + -rm -f imath_wrap/$(am__dirstamp) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf ./$(DEPDIR) imath_wrap/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-libtool distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-data-local install-deprecatedHEADERS \ + install-nodist_pkgincludeHEADERS install-pkgconfigDATA \ + install-pkgincludeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf ./$(DEPDIR) imath_wrap/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-deprecatedHEADERS uninstall-libLTLIBRARIES \ + uninstall-local uninstall-nodist_pkgincludeHEADERS \ + uninstall-pkgconfigDATA uninstall-pkgincludeHEADERS + +.MAKE: $(am__recursive_targets) all check check-am install install-am \ + install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-TESTS check-am clean clean-cscope \ + clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstPROGRAMS cscope cscopelist-am ctags ctags-am dist \ + dist-all dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar \ + dist-tarZ dist-xz dist-zip distcheck distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-data-local install-deprecatedHEADERS install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-man \ + install-nodist_pkgincludeHEADERS install-pdf install-pdf-am \ + install-pkgconfigDATA install-pkgincludeHEADERS install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am \ + uninstall-deprecatedHEADERS uninstall-libLTLIBRARIES \ + uninstall-local uninstall-nodist_pkgincludeHEADERS \ + uninstall-pkgconfigDATA uninstall-pkgincludeHEADERS + +.PRECIOUS: Makefile + + +dist-hook: + echo @GIT_HEAD_VERSION@ > $(distdir)/GIT_HEAD_ID + (cd doc; make manual.pdf) + cp doc/manual.pdf $(distdir)/doc/ + +gitversion.h: @GIT_HEAD@ + $(AM_V_GEN)echo '#define GIT_HEAD_ID "'@GIT_HEAD_VERSION@'"' > $@ + +install-data-local: $(srcdir)/isl.py + @libisl=`sed -ne "/^library_names=/{s/.*='//;s/'$$//;s/ .*//;p;}" \ + $(builddir)/libisl.la`; \ + case $$libisl in \ + '') echo Cannot find isl library name. GDB bindings not installed.;; \ + *) echo $(INSTALL_DATA) $(srcdir)/isl.py \ + $(DESTDIR)$(libdir)/$$libisl-gdb.py; \ + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"; \ + $(INSTALL_DATA) $(srcdir)/isl.py $(DESTDIR)$(libdir)/$$libisl-gdb.py; esac + +uninstall-local: + @libisl=`sed -ne "/^library_names=/{s/.*='//;s/'$$//;s/ .*//;p;}" \ + $(builddir)/libisl.la`; \ + if test -n "$${libisl}"; then \ + rm -f $(DESTDIR)$(libdir)/$$libisl-gdb.py; \ + fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: Index: lib/Analysis/isl/README =================================================================== --- /dev/null +++ lib/Analysis/isl/README @@ -0,0 +1,53 @@ +isl is a thread-safe C library for manipulating sets and relations +of integer points bounded by affine constraints. The descriptions of +the sets and relations may involve both parameters and existentially +quantified variables. All computations are performed in exact integer +arithmetic using GMP. + +isl is released under the MIT license, but depends on the LGPL GMP +library. + +Minimal compilation instructions: + + ./configure + make + make install + +If you are taking the source from the git repository, then you first +need to do + + git clone git://repo.or.cz/isl.git + ./autogen.sh + +For more information, see doc/user.pod or the generated documentation. + +New releases are announced on http://freecode.com/projects/isl + +If you use isl, you can let me know by stacking +https://www.ohloh.net/p/isl on ohloh. + +For bug reports, feature requests and questions, +contact http://groups.google.com/group/isl-development + +Whenever you report a bug, please mention the exact version of isl +that you are using (output of "./isl_cat --version"). If you are unable +to compile isl, then report the git version (output of "git describe") +or the version included in the name of the tarball. + +If you use isl for your research, you are invited do cite +the following paper and/or the paper(s) describing the specific +operations you use. + +@incollection{Verdoolaege2010isl, + author = {Verdoolaege, Sven}, + title = {isl: An Integer Set Library for the Polyhedral Model}, + booktitle = {Mathematical Software - ICMS 2010}, + series = {Lecture Notes in Computer Science}, + editor = {Fukuda, Komei and Hoeven, Joris and Joswig, Michael and + Takayama, Nobuki}, + publisher = {Springer}, + isbn = {978-3-642-15581-9}, + pages = {299-302}, + volume = {6327}, + year = {2010} +} Index: lib/Analysis/isl/aclocal.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/aclocal.m4 @@ -0,0 +1,1171 @@ +# generated automatically by aclocal 1.15 -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.15' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.15], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.15])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/ax_c___attribute__.m4]) +m4_include([m4/ax_cc_maxopt.m4]) +m4_include([m4/ax_check_compiler_flags.m4]) +m4_include([m4/ax_compiler_vendor.m4]) +m4_include([m4/ax_create_pkgconfig_info.m4]) +m4_include([m4/ax_create_stdint_h.m4]) +m4_include([m4/ax_detect_git_head.m4]) +m4_include([m4/ax_detect_gmp.m4]) +m4_include([m4/ax_detect_imath.m4]) +m4_include([m4/ax_gcc_archflag.m4]) +m4_include([m4/ax_gcc_warn_unused_result.m4]) +m4_include([m4/ax_gcc_x86_cpuid.m4]) +m4_include([m4/ax_set_warning_flags.m4]) +m4_include([m4/ax_submodule.m4]) +m4_include([m4/libtool.m4]) +m4_include([m4/ltoptions.m4]) +m4_include([m4/ltsugar.m4]) +m4_include([m4/ltversion.m4]) +m4_include([m4/lt~obsolete.m4]) Index: lib/Analysis/isl/basis_reduction_tab.c =================================================================== --- /dev/null +++ lib/Analysis/isl/basis_reduction_tab.c @@ -0,0 +1,293 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include "isl_tab.h" +#include +#include + +struct tab_lp { + struct isl_ctx *ctx; + struct isl_vec *row; + struct isl_tab *tab; + struct isl_tab_undo **stack; + isl_int *obj; + isl_int opt; + isl_int opt_denom; + isl_int tmp; + isl_int tmp2; + int neq; + unsigned dim; + /* number of constraints in initial product tableau */ + int con_offset; + /* objective function has fixed or no integer value */ + int is_fixed; +}; + +#ifdef USE_GMP_FOR_MP +#define GBR_type mpq_t +#define GBR_init(v) mpq_init(v) +#define GBR_clear(v) mpq_clear(v) +#define GBR_set(a,b) mpq_set(a,b) +#define GBR_set_ui(a,b) mpq_set_ui(a,b,1) +#define GBR_mul(a,b,c) mpq_mul(a,b,c) +#define GBR_lt(a,b) (mpq_cmp(a,b) < 0) +#define GBR_is_zero(a) (mpq_sgn(a) == 0) +#define GBR_numref(a) mpq_numref(a) +#define GBR_denref(a) mpq_denref(a) +#define GBR_floor(a,b) mpz_fdiv_q(a,GBR_numref(b),GBR_denref(b)) +#define GBR_ceil(a,b) mpz_cdiv_q(a,GBR_numref(b),GBR_denref(b)) +#define GBR_set_num_neg(a, b) mpz_neg(GBR_numref(*a), b); +#define GBR_set_den(a, b) mpz_set(GBR_denref(*a), b); +#endif /* USE_GMP_FOR_MP */ + +#ifdef USE_IMATH_FOR_MP +#include + +#define GBR_type mp_rat +#define GBR_init(v) v = mp_rat_alloc() +#define GBR_clear(v) mp_rat_free(v) +#define GBR_set(a,b) mp_rat_copy(b,a) +#define GBR_set_ui(a,b) mp_rat_set_uvalue(a,b,1) +#define GBR_mul(a,b,c) mp_rat_mul(b,c,a) +#define GBR_lt(a,b) (mp_rat_compare(a,b) < 0) +#define GBR_is_zero(a) (mp_rat_compare_zero(a) == 0) +#ifdef USE_SMALL_INT_OPT +#define GBR_numref(a) isl_sioimath_encode_big(mp_rat_numer_ref(a)) +#define GBR_denref(a) isl_sioimath_encode_big(mp_rat_denom_ref(a)) +#define GBR_floor(a, b) isl_sioimath_fdiv_q((a), GBR_numref(b), GBR_denref(b)) +#define GBR_ceil(a, b) isl_sioimath_cdiv_q((a), GBR_numref(b), GBR_denref(b)) +#define GBR_set_num_neg(a, b) \ + do { \ + isl_sioimath_scratchspace_t scratch; \ + impz_neg(mp_rat_numer_ref(*a), \ + isl_sioimath_bigarg_src(*b, &scratch));\ + } while (0) +#define GBR_set_den(a, b) \ + do { \ + isl_sioimath_scratchspace_t scratch; \ + impz_set(mp_rat_denom_ref(*a), \ + isl_sioimath_bigarg_src(*b, &scratch));\ + } while (0) +#else /* USE_SMALL_INT_OPT */ +#define GBR_numref(a) mp_rat_numer_ref(a) +#define GBR_denref(a) mp_rat_denom_ref(a) +#define GBR_floor(a,b) impz_fdiv_q(a,GBR_numref(b),GBR_denref(b)) +#define GBR_ceil(a,b) impz_cdiv_q(a,GBR_numref(b),GBR_denref(b)) +#define GBR_set_num_neg(a, b) impz_neg(GBR_numref(*a), b) +#define GBR_set_den(a, b) impz_set(GBR_denref(*a), b) +#endif /* USE_SMALL_INT_OPT */ +#endif /* USE_IMATH_FOR_MP */ + +static struct tab_lp *init_lp(struct isl_tab *tab); +static void set_lp_obj(struct tab_lp *lp, isl_int *row, int dim); +static int solve_lp(struct tab_lp *lp); +static void get_obj_val(struct tab_lp* lp, GBR_type *F); +static void delete_lp(struct tab_lp *lp); +static int add_lp_row(struct tab_lp *lp, isl_int *row, int dim); +static void get_alpha(struct tab_lp* lp, int row, GBR_type *alpha); +static int del_lp_row(struct tab_lp *lp) WARN_UNUSED; +static int cut_lp_to_hyperplane(struct tab_lp *lp, isl_int *row); + +#define GBR_LP struct tab_lp +#define GBR_lp_init(P) init_lp(P) +#define GBR_lp_set_obj(lp, obj, dim) set_lp_obj(lp, obj, dim) +#define GBR_lp_solve(lp) solve_lp(lp) +#define GBR_lp_get_obj_val(lp, F) get_obj_val(lp, F) +#define GBR_lp_delete(lp) delete_lp(lp) +#define GBR_lp_next_row(lp) lp->neq +#define GBR_lp_add_row(lp, row, dim) add_lp_row(lp, row, dim) +#define GBR_lp_get_alpha(lp, row, alpha) get_alpha(lp, row, alpha) +#define GBR_lp_del_row(lp) del_lp_row(lp) +#define GBR_lp_is_fixed(lp) (lp)->is_fixed +#define GBR_lp_cut(lp, obj) cut_lp_to_hyperplane(lp, obj) +#include "basis_reduction_templ.c" + +/* Set up a tableau for the Cartesian product of bset with itself. + * This could be optimized by first setting up a tableau for bset + * and then performing the Cartesian product on the tableau. + */ +static struct isl_tab *gbr_tab(struct isl_tab *tab, struct isl_vec *row) +{ + unsigned dim; + struct isl_tab *prod; + + if (!tab || !row) + return NULL; + + dim = tab->n_var; + prod = isl_tab_product(tab, tab); + if (isl_tab_extend_cons(prod, 3 * dim + 1) < 0) { + isl_tab_free(prod); + return NULL; + } + return prod; +} + +static struct tab_lp *init_lp(struct isl_tab *tab) +{ + struct tab_lp *lp = NULL; + + if (!tab) + return NULL; + + lp = isl_calloc_type(tab->mat->ctx, struct tab_lp); + if (!lp) + return NULL; + + isl_int_init(lp->opt); + isl_int_init(lp->opt_denom); + isl_int_init(lp->tmp); + isl_int_init(lp->tmp2); + + lp->dim = tab->n_var; + + lp->ctx = tab->mat->ctx; + isl_ctx_ref(lp->ctx); + + lp->stack = isl_alloc_array(lp->ctx, struct isl_tab_undo *, lp->dim); + + lp->row = isl_vec_alloc(lp->ctx, 1 + 2 * lp->dim); + if (!lp->row) + goto error; + lp->tab = gbr_tab(tab, lp->row); + if (!lp->tab) + goto error; + lp->con_offset = lp->tab->n_con; + lp->obj = NULL; + lp->neq = 0; + + return lp; +error: + delete_lp(lp); + return NULL; +} + +static void set_lp_obj(struct tab_lp *lp, isl_int *row, int dim) +{ + lp->obj = row; +} + +static int solve_lp(struct tab_lp *lp) +{ + enum isl_lp_result res; + unsigned flags = 0; + + lp->is_fixed = 0; + + isl_int_set_si(lp->row->el[0], 0); + isl_seq_cpy(lp->row->el + 1, lp->obj, lp->dim); + isl_seq_neg(lp->row->el + 1 + lp->dim, lp->obj, lp->dim); + if (lp->neq) + flags = ISL_TAB_SAVE_DUAL; + res = isl_tab_min(lp->tab, lp->row->el, lp->ctx->one, + &lp->opt, &lp->opt_denom, flags); + isl_int_mul_ui(lp->opt_denom, lp->opt_denom, 2); + if (isl_int_abs_lt(lp->opt, lp->opt_denom)) { + struct isl_vec *sample = isl_tab_get_sample_value(lp->tab); + if (!sample) + return -1; + isl_seq_inner_product(lp->obj, sample->el + 1, lp->dim, &lp->tmp); + isl_seq_inner_product(lp->obj, sample->el + 1 + lp->dim, lp->dim, &lp->tmp2); + isl_int_cdiv_q(lp->tmp, lp->tmp, sample->el[0]); + isl_int_fdiv_q(lp->tmp2, lp->tmp2, sample->el[0]); + if (isl_int_ge(lp->tmp, lp->tmp2)) + lp->is_fixed = 1; + isl_vec_free(sample); + } + isl_int_divexact_ui(lp->opt_denom, lp->opt_denom, 2); + if (res < 0) + return -1; + if (res != isl_lp_ok) + isl_die(lp->ctx, isl_error_internal, + "unexpected missing (bounded) solution", return -1); + return 0; +} + +/* The current objective function has a fixed (or no) integer value. + * Cut the tableau to the hyperplane that fixes this value in + * both halves of the tableau. + * Return 1 if the resulting tableau is empty. + */ +static int cut_lp_to_hyperplane(struct tab_lp *lp, isl_int *row) +{ + enum isl_lp_result res; + + isl_int_set_si(lp->row->el[0], 0); + isl_seq_cpy(lp->row->el + 1, row, lp->dim); + isl_seq_clr(lp->row->el + 1 + lp->dim, lp->dim); + res = isl_tab_min(lp->tab, lp->row->el, lp->ctx->one, + &lp->tmp, NULL, 0); + if (res != isl_lp_ok) + return -1; + + isl_int_neg(lp->row->el[0], lp->tmp); + if (isl_tab_add_eq(lp->tab, lp->row->el) < 0) + return -1; + + isl_seq_cpy(lp->row->el + 1 + lp->dim, row, lp->dim); + isl_seq_clr(lp->row->el + 1, lp->dim); + if (isl_tab_add_eq(lp->tab, lp->row->el) < 0) + return -1; + + lp->con_offset += 2; + + return lp->tab->empty; +} + +static void get_obj_val(struct tab_lp* lp, GBR_type *F) +{ + GBR_set_num_neg(F, lp->opt); + GBR_set_den(F, lp->opt_denom); +} + +static void delete_lp(struct tab_lp *lp) +{ + if (!lp) + return; + + isl_int_clear(lp->opt); + isl_int_clear(lp->opt_denom); + isl_int_clear(lp->tmp); + isl_int_clear(lp->tmp2); + isl_vec_free(lp->row); + free(lp->stack); + isl_tab_free(lp->tab); + isl_ctx_deref(lp->ctx); + free(lp); +} + +static int add_lp_row(struct tab_lp *lp, isl_int *row, int dim) +{ + lp->stack[lp->neq] = isl_tab_snap(lp->tab); + + isl_int_set_si(lp->row->el[0], 0); + isl_seq_cpy(lp->row->el + 1, row, lp->dim); + isl_seq_neg(lp->row->el + 1 + lp->dim, row, lp->dim); + + if (isl_tab_add_valid_eq(lp->tab, lp->row->el) < 0) + return -1; + + return lp->neq++; +} + +static void get_alpha(struct tab_lp* lp, int row, GBR_type *alpha) +{ + row += lp->con_offset; + GBR_set_num_neg(alpha, lp->tab->dual->el[1 + row]); + GBR_set_den(alpha, lp->tab->dual->el[0]); +} + +static int del_lp_row(struct tab_lp *lp) +{ + lp->neq--; + return isl_tab_rollback(lp->tab, lp->stack[lp->neq]); +} Index: lib/Analysis/isl/basis_reduction_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/basis_reduction_templ.c @@ -0,0 +1,357 @@ +/* + * Copyright 2006-2007 Universiteit Leiden + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science, + * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands + * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A, + * B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include +#include "isl_basis_reduction.h" + +static void save_alpha(GBR_LP *lp, int first, int n, GBR_type *alpha) +{ + int i; + + for (i = 0; i < n; ++i) + GBR_lp_get_alpha(lp, first + i, &alpha[i]); +} + +/* Compute a reduced basis for the set represented by the tableau "tab". + * tab->basis, which must be initialized by the calling function to an affine + * unimodular basis, is updated to reflect the reduced basis. + * The first tab->n_zero rows of the basis (ignoring the constant row) + * are assumed to correspond to equalities and are left untouched. + * tab->n_zero is updated to reflect any additional equalities that + * have been detected in the first rows of the new basis. + * The final tab->n_unbounded rows of the basis are assumed to correspond + * to unbounded directions and are also left untouched. + * In particular this means that the remaining rows are assumed to + * correspond to bounded directions. + * + * This function implements the algorithm described in + * "An Implementation of the Generalized Basis Reduction Algorithm + * for Integer Programming" of Cook el al. to compute a reduced basis. + * We use \epsilon = 1/4. + * + * If ctx->opt->gbr_only_first is set, the user is only interested + * in the first direction. In this case we stop the basis reduction when + * the width in the first direction becomes smaller than 2. + */ +struct isl_tab *isl_tab_compute_reduced_basis(struct isl_tab *tab) +{ + unsigned dim; + struct isl_ctx *ctx; + struct isl_mat *B; + int i; + GBR_LP *lp = NULL; + GBR_type F_old, alpha, F_new; + int row; + isl_int tmp; + struct isl_vec *b_tmp; + GBR_type *F = NULL; + GBR_type *alpha_buffer[2] = { NULL, NULL }; + GBR_type *alpha_saved; + GBR_type F_saved; + int use_saved = 0; + isl_int mu[2]; + GBR_type mu_F[2]; + GBR_type two; + GBR_type one; + int empty = 0; + int fixed = 0; + int fixed_saved = 0; + int mu_fixed[2]; + int n_bounded; + int gbr_only_first; + + if (!tab) + return NULL; + + if (tab->empty) + return tab; + + ctx = tab->mat->ctx; + gbr_only_first = ctx->opt->gbr_only_first; + dim = tab->n_var; + B = tab->basis; + if (!B) + return tab; + + n_bounded = dim - tab->n_unbounded; + if (n_bounded <= tab->n_zero + 1) + return tab; + + isl_int_init(tmp); + isl_int_init(mu[0]); + isl_int_init(mu[1]); + + GBR_init(alpha); + GBR_init(F_old); + GBR_init(F_new); + GBR_init(F_saved); + GBR_init(mu_F[0]); + GBR_init(mu_F[1]); + GBR_init(two); + GBR_init(one); + + b_tmp = isl_vec_alloc(ctx, dim); + if (!b_tmp) + goto error; + + F = isl_alloc_array(ctx, GBR_type, n_bounded); + alpha_buffer[0] = isl_alloc_array(ctx, GBR_type, n_bounded); + alpha_buffer[1] = isl_alloc_array(ctx, GBR_type, n_bounded); + alpha_saved = alpha_buffer[0]; + + if (!F || !alpha_buffer[0] || !alpha_buffer[1]) + goto error; + + for (i = 0; i < n_bounded; ++i) { + GBR_init(F[i]); + GBR_init(alpha_buffer[0][i]); + GBR_init(alpha_buffer[1][i]); + } + + GBR_set_ui(two, 2); + GBR_set_ui(one, 1); + + lp = GBR_lp_init(tab); + if (!lp) + goto error; + + i = tab->n_zero; + + GBR_lp_set_obj(lp, B->row[1+i]+1, dim); + ctx->stats->gbr_solved_lps++; + if (GBR_lp_solve(lp) < 0) + goto error; + GBR_lp_get_obj_val(lp, &F[i]); + + if (GBR_lt(F[i], one)) { + if (!GBR_is_zero(F[i])) { + empty = GBR_lp_cut(lp, B->row[1+i]+1); + if (empty) + goto done; + GBR_set_ui(F[i], 0); + } + tab->n_zero++; + } + + do { + if (i+1 == tab->n_zero) { + GBR_lp_set_obj(lp, B->row[1+i+1]+1, dim); + ctx->stats->gbr_solved_lps++; + if (GBR_lp_solve(lp) < 0) + goto error; + GBR_lp_get_obj_val(lp, &F_new); + fixed = GBR_lp_is_fixed(lp); + GBR_set_ui(alpha, 0); + } else + if (use_saved) { + row = GBR_lp_next_row(lp); + GBR_set(F_new, F_saved); + fixed = fixed_saved; + GBR_set(alpha, alpha_saved[i]); + } else { + row = GBR_lp_add_row(lp, B->row[1+i]+1, dim); + GBR_lp_set_obj(lp, B->row[1+i+1]+1, dim); + ctx->stats->gbr_solved_lps++; + if (GBR_lp_solve(lp) < 0) + goto error; + GBR_lp_get_obj_val(lp, &F_new); + fixed = GBR_lp_is_fixed(lp); + + GBR_lp_get_alpha(lp, row, &alpha); + + if (i > 0) + save_alpha(lp, row-i, i, alpha_saved); + + if (GBR_lp_del_row(lp) < 0) + goto error; + } + GBR_set(F[i+1], F_new); + + GBR_floor(mu[0], alpha); + GBR_ceil(mu[1], alpha); + + if (isl_int_eq(mu[0], mu[1])) + isl_int_set(tmp, mu[0]); + else { + int j; + + for (j = 0; j <= 1; ++j) { + isl_int_set(tmp, mu[j]); + isl_seq_combine(b_tmp->el, + ctx->one, B->row[1+i+1]+1, + tmp, B->row[1+i]+1, dim); + GBR_lp_set_obj(lp, b_tmp->el, dim); + ctx->stats->gbr_solved_lps++; + if (GBR_lp_solve(lp) < 0) + goto error; + GBR_lp_get_obj_val(lp, &mu_F[j]); + mu_fixed[j] = GBR_lp_is_fixed(lp); + if (i > 0) + save_alpha(lp, row-i, i, alpha_buffer[j]); + } + + if (GBR_lt(mu_F[0], mu_F[1])) + j = 0; + else + j = 1; + + isl_int_set(tmp, mu[j]); + GBR_set(F_new, mu_F[j]); + fixed = mu_fixed[j]; + alpha_saved = alpha_buffer[j]; + } + isl_seq_combine(B->row[1+i+1]+1, ctx->one, B->row[1+i+1]+1, + tmp, B->row[1+i]+1, dim); + + if (i+1 == tab->n_zero && fixed) { + if (!GBR_is_zero(F[i+1])) { + empty = GBR_lp_cut(lp, B->row[1+i+1]+1); + if (empty) + goto done; + GBR_set_ui(F[i+1], 0); + } + tab->n_zero++; + } + + GBR_set(F_old, F[i]); + + use_saved = 0; + /* mu_F[0] = 4 * F_new; mu_F[1] = 3 * F_old */ + GBR_set_ui(mu_F[0], 4); + GBR_mul(mu_F[0], mu_F[0], F_new); + GBR_set_ui(mu_F[1], 3); + GBR_mul(mu_F[1], mu_F[1], F_old); + if (GBR_lt(mu_F[0], mu_F[1])) { + B = isl_mat_swap_rows(B, 1 + i, 1 + i + 1); + if (i > tab->n_zero) { + use_saved = 1; + GBR_set(F_saved, F_new); + fixed_saved = fixed; + if (GBR_lp_del_row(lp) < 0) + goto error; + --i; + } else { + GBR_set(F[tab->n_zero], F_new); + if (gbr_only_first && GBR_lt(F[tab->n_zero], two)) + break; + + if (fixed) { + if (!GBR_is_zero(F[tab->n_zero])) { + empty = GBR_lp_cut(lp, B->row[1+tab->n_zero]+1); + if (empty) + goto done; + GBR_set_ui(F[tab->n_zero], 0); + } + tab->n_zero++; + } + } + } else { + GBR_lp_add_row(lp, B->row[1+i]+1, dim); + ++i; + } + } while (i < n_bounded - 1); + + if (0) { +done: + if (empty < 0) { +error: + isl_mat_free(B); + B = NULL; + } + } + + GBR_lp_delete(lp); + + if (alpha_buffer[1]) + for (i = 0; i < n_bounded; ++i) { + GBR_clear(F[i]); + GBR_clear(alpha_buffer[0][i]); + GBR_clear(alpha_buffer[1][i]); + } + free(F); + free(alpha_buffer[0]); + free(alpha_buffer[1]); + + isl_vec_free(b_tmp); + + GBR_clear(alpha); + GBR_clear(F_old); + GBR_clear(F_new); + GBR_clear(F_saved); + GBR_clear(mu_F[0]); + GBR_clear(mu_F[1]); + GBR_clear(two); + GBR_clear(one); + + isl_int_clear(tmp); + isl_int_clear(mu[0]); + isl_int_clear(mu[1]); + + tab->basis = B; + + return tab; +} + +/* Compute an affine form of a reduced basis of the given basic + * non-parametric set, which is assumed to be bounded and not + * include any integer divisions. + * The first column and the first row correspond to the constant term. + * + * If the input contains any equalities, we first create an initial + * basis with the equalities first. Otherwise, we start off with + * the identity matrix. + */ +struct isl_mat *isl_basic_set_reduced_basis(struct isl_basic_set *bset) +{ + struct isl_mat *basis; + struct isl_tab *tab; + + if (!bset) + return NULL; + + if (isl_basic_set_dim(bset, isl_dim_div) != 0) + isl_die(bset->ctx, isl_error_invalid, + "no integer division allowed", return NULL); + if (isl_basic_set_dim(bset, isl_dim_param) != 0) + isl_die(bset->ctx, isl_error_invalid, + "no parameters allowed", return NULL); + + tab = isl_tab_from_basic_set(bset, 0); + if (!tab) + return NULL; + + if (bset->n_eq == 0) + tab->basis = isl_mat_identity(bset->ctx, 1 + tab->n_var); + else { + isl_mat *eq; + unsigned nvar = isl_basic_set_total_dim(bset); + eq = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, bset->n_eq, + 1, nvar); + eq = isl_mat_left_hermite(eq, 0, NULL, &tab->basis); + tab->basis = isl_mat_lin_to_aff(tab->basis); + tab->n_zero = bset->n_eq; + isl_mat_free(eq); + } + tab = isl_tab_compute_reduced_basis(tab); + if (!tab) + return NULL; + + basis = isl_mat_copy(tab->basis); + + isl_tab_free(tab); + + return basis; +} Index: lib/Analysis/isl/bound.c =================================================================== --- /dev/null +++ lib/Analysis/isl/bound.c @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct bound_options { + struct isl_options *isl; + unsigned verify; + int print_all; + int continue_on_error; +}; + +ISL_ARGS_START(struct bound_options, bound_options_args) +ISL_ARG_CHILD(struct bound_options, isl, "isl", &isl_options_args, + "isl options") +ISL_ARG_BOOL(struct bound_options, verify, 'T', "verify", 0, NULL) +ISL_ARG_BOOL(struct bound_options, print_all, 'A', "print-all", 0, NULL) +ISL_ARG_BOOL(struct bound_options, continue_on_error, '\0', "continue-on-error", 0, NULL) +ISL_ARGS_END + +ISL_ARG_DEF(bound_options, struct bound_options, bound_options_args) + +static __isl_give isl_set *set_bounds(__isl_take isl_set *set) +{ + unsigned nparam; + int i, r; + isl_point *pt, *pt2; + isl_set *box; + + nparam = isl_set_dim(set, isl_dim_param); + r = nparam >= 8 ? 5 : nparam >= 5 ? 15 : 50; + + pt = isl_set_sample_point(isl_set_copy(set)); + pt2 = isl_point_copy(pt); + + for (i = 0; i < nparam; ++i) { + pt = isl_point_add_ui(pt, isl_dim_param, i, r); + pt2 = isl_point_sub_ui(pt2, isl_dim_param, i, r); + } + + box = isl_set_box_from_points(pt, pt2); + + return isl_set_intersect(set, box); +} + +struct verify_point_bound { + struct bound_options *options; + int stride; + int n; + int exact; + int error; + + isl_pw_qpolynomial_fold *pwf; + isl_pw_qpolynomial_fold *bound; +}; + +static isl_stat verify_point(__isl_take isl_point *pnt, void *user) +{ + int i; + unsigned nvar; + unsigned nparam; + struct verify_point_bound *vpb = (struct verify_point_bound *) user; + isl_int t; + isl_ctx *ctx; + isl_pw_qpolynomial_fold *pwf; + isl_val *bound = NULL; + isl_val *opt = NULL; + isl_set *dom = NULL; + isl_printer *p; + const char *minmax; + int bounded; + int sign; + int ok; + FILE *out = vpb->options->print_all ? stdout : stderr; + + vpb->n--; + + if (1) { + minmax = "ub"; + sign = 1; + } else { + minmax = "lb"; + sign = -1; + } + + ctx = isl_point_get_ctx(pnt); + p = isl_printer_to_file(ctx, out); + + isl_int_init(t); + + pwf = isl_pw_qpolynomial_fold_copy(vpb->pwf); + + nparam = isl_pw_qpolynomial_fold_dim(pwf, isl_dim_param); + for (i = 0; i < nparam; ++i) { + isl_point_get_coordinate(pnt, isl_dim_param, i, &t); + pwf = isl_pw_qpolynomial_fold_fix_dim(pwf, isl_dim_param, i, t); + } + + bound = isl_pw_qpolynomial_fold_eval( + isl_pw_qpolynomial_fold_copy(vpb->bound), + isl_point_copy(pnt)); + + dom = isl_pw_qpolynomial_fold_domain(isl_pw_qpolynomial_fold_copy(pwf)); + bounded = isl_set_is_bounded(dom); + + if (bounded < 0) + goto error; + + if (!bounded) + opt = isl_pw_qpolynomial_fold_eval( + isl_pw_qpolynomial_fold_copy(pwf), + isl_set_sample_point(isl_set_copy(dom))); + else if (sign > 0) + opt = isl_pw_qpolynomial_fold_max(isl_pw_qpolynomial_fold_copy(pwf)); + else + opt = isl_pw_qpolynomial_fold_min(isl_pw_qpolynomial_fold_copy(pwf)); + + nvar = isl_set_dim(dom, isl_dim_set); + if (vpb->exact && bounded) + ok = isl_val_eq(opt, bound); + else if (sign > 0) + ok = isl_val_le(opt, bound); + else + ok = isl_val_le(bound, opt); + if (ok < 0) + goto error; + + if (vpb->options->print_all || !ok) { + p = isl_printer_print_str(p, minmax); + p = isl_printer_print_str(p, "("); + for (i = 0; i < nparam; ++i) { + if (i) + p = isl_printer_print_str(p, ", "); + isl_point_get_coordinate(pnt, isl_dim_param, i, &t); + p = isl_printer_print_isl_int(p, t); + } + p = isl_printer_print_str(p, ") = "); + p = isl_printer_print_val(p, bound); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, bounded ? "opt" : "sample"); + p = isl_printer_print_str(p, " = "); + p = isl_printer_print_val(p, opt); + if (ok) + p = isl_printer_print_str(p, ". OK"); + else + p = isl_printer_print_str(p, ". NOT OK"); + p = isl_printer_end_line(p); + } else if ((vpb->n % vpb->stride) == 0) { + p = isl_printer_print_str(p, "o"); + p = isl_printer_flush(p); + } + + if (0) { +error: + ok = 0; + } + + isl_pw_qpolynomial_fold_free(pwf); + isl_val_free(bound); + isl_val_free(opt); + isl_point_free(pnt); + isl_set_free(dom); + + isl_int_clear(t); + + isl_printer_free(p); + + if (!ok) + vpb->error = 1; + + if (vpb->options->continue_on_error) + ok = 1; + + return (vpb->n >= 1 && ok) ? isl_stat_ok : isl_stat_error; +} + +static int check_solution(__isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_pw_qpolynomial_fold *bound, int exact, + struct bound_options *options) +{ + struct verify_point_bound vpb; + isl_int count, max; + isl_set *dom; + isl_set *context; + int i, r, n; + + dom = isl_pw_qpolynomial_fold_domain(isl_pw_qpolynomial_fold_copy(pwf)); + context = isl_set_params(isl_set_copy(dom)); + context = isl_set_remove_divs(context); + context = set_bounds(context); + + isl_int_init(count); + isl_int_init(max); + + isl_int_set_si(max, 200); + r = isl_set_count_upto(context, max, &count); + assert(r >= 0); + n = isl_int_get_si(count); + + isl_int_clear(max); + isl_int_clear(count); + + vpb.options = options; + vpb.pwf = pwf; + vpb.bound = bound; + vpb.n = n; + vpb.stride = n > 70 ? 1 + (n + 1)/70 : 1; + vpb.error = 0; + vpb.exact = exact; + + if (!options->print_all) { + for (i = 0; i < vpb.n; i += vpb.stride) + printf("."); + printf("\r"); + fflush(stdout); + } + + isl_set_foreach_point(context, verify_point, &vpb); + + isl_set_free(context); + isl_set_free(dom); + isl_pw_qpolynomial_fold_free(pwf); + isl_pw_qpolynomial_fold_free(bound); + + if (!options->print_all) + printf("\n"); + + if (vpb.error) { + fprintf(stderr, "Check failed !\n"); + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + isl_ctx *ctx; + isl_pw_qpolynomial_fold *copy; + isl_pw_qpolynomial_fold *pwf; + isl_stream *s; + struct isl_obj obj; + struct bound_options *options; + int exact; + int r = 0; + + options = bound_options_new_with_defaults(); + assert(options); + argc = bound_options_parse(options, argc, argv, ISL_ARG_ALL); + + ctx = isl_ctx_alloc_with_options(&bound_options_args, options); + + s = isl_stream_new_file(ctx, stdin); + obj = isl_stream_read_obj(s); + if (obj.type == isl_obj_pw_qpolynomial) + pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial(isl_fold_max, + obj.v); + else if (obj.type == isl_obj_pw_qpolynomial_fold) + pwf = obj.v; + else { + obj.type->free(obj.v); + isl_die(ctx, isl_error_invalid, "invalid input", goto error); + } + + if (options->verify) + copy = isl_pw_qpolynomial_fold_copy(pwf); + + pwf = isl_pw_qpolynomial_fold_bound(pwf, &exact); + pwf = isl_pw_qpolynomial_fold_coalesce(pwf); + + if (options->verify) { + r = check_solution(copy, pwf, exact, options); + } else { + if (!exact) + printf("# NOT exact\n"); + isl_pw_qpolynomial_fold_print(pwf, stdout, 0); + fprintf(stdout, "\n"); + isl_pw_qpolynomial_fold_free(pwf); + } + +error: + isl_stream_free(s); + + isl_ctx_free(ctx); + + return r; +} Index: lib/Analysis/isl/bound_test.sh.in =================================================================== --- /dev/null +++ lib/Analysis/isl/bound_test.sh.in @@ -0,0 +1,35 @@ +#!/bin/sh + +EXEEXT=@EXEEXT@ + +BOUND_TESTS="\ + basicLinear2.pwqp \ + basicLinear.pwqp \ + basicTestParameterPosNeg.pwqp \ + basicTest.pwqp \ + devos.pwqp \ + equality1.pwqp \ + equality2.pwqp \ + equality3.pwqp \ + equality4.pwqp \ + equality5.pwqp \ + faddeev.pwqp \ + linearExample.pwqp \ + neg.pwqp \ + philippe3vars3pars.pwqp \ + philippe3vars.pwqp \ + philippeNeg.pwqp \ + philippePolynomialCoeff1P.pwqp \ + philippePolynomialCoeff.pwqp \ + philippe.pwqp \ + product.pwqp \ + split.pwqp \ + test3Deg3Var.pwqp \ + toplas.pwqp \ + unexpanded.pwqp" + +for i in $BOUND_TESTS; do + echo $i; + ./isl_bound$EXEEXT -T --bound=bernstein < $srcdir/test_inputs/$i || exit + ./isl_bound$EXEEXT -T --bound=range < $srcdir/test_inputs/$i || exit +done Index: lib/Analysis/isl/cat.c =================================================================== --- /dev/null +++ lib/Analysis/isl/cat.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +struct isl_arg_choice cat_format[] = { + {"isl", ISL_FORMAT_ISL}, + {"omega", ISL_FORMAT_OMEGA}, + {"polylib", ISL_FORMAT_POLYLIB}, + {"ext-polylib", ISL_FORMAT_EXT_POLYLIB}, + {"latex", ISL_FORMAT_LATEX}, + {0} +}; + +struct isl_arg_choice cat_yaml_style[] = { + { "block", ISL_YAML_STYLE_BLOCK }, + { "flow", ISL_YAML_STYLE_FLOW }, + { 0 } +}; + +struct cat_options { + struct isl_options *isl; + unsigned format; + unsigned yaml_style; +}; + +ISL_ARGS_START(struct cat_options, cat_options_args) +ISL_ARG_CHILD(struct cat_options, isl, "isl", &isl_options_args, "isl options") +ISL_ARG_CHOICE(struct cat_options, format, 0, "format", \ + cat_format, ISL_FORMAT_ISL, "output format") +ISL_ARG_CHOICE(struct cat_options, yaml_style, 0, "yaml-style", \ + cat_yaml_style, ISL_YAML_STYLE_BLOCK, "output YAML style") +ISL_ARGS_END + +ISL_ARG_DEF(cat_options, struct cat_options, cat_options_args) + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx; + isl_stream *s; + struct isl_obj obj; + struct cat_options *options; + isl_printer *p; + + options = cat_options_new_with_defaults(); + assert(options); + argc = cat_options_parse(options, argc, argv, ISL_ARG_ALL); + + ctx = isl_ctx_alloc_with_options(&cat_options_args, options); + + s = isl_stream_new_file(ctx, stdin); + obj = isl_stream_read_obj(s); + isl_stream_free(s); + + p = isl_printer_to_file(ctx, stdout); + p = isl_printer_set_output_format(p, options->format); + p = isl_printer_set_yaml_style(p, options->yaml_style); + p = obj.type->print(p, obj.v); + p = isl_printer_end_line(p); + isl_printer_free(p); + + obj.type->free(obj.v); + + isl_ctx_free(ctx); + + return 0; +} Index: lib/Analysis/isl/closure.c =================================================================== --- /dev/null +++ lib/Analysis/isl/closure.c @@ -0,0 +1,39 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx; + struct isl_map *map; + struct isl_options *options; + isl_printer *p; + int exact; + + options = isl_options_new_with_defaults(); + assert(options); + argc = isl_options_parse(options, argc, argv, ISL_ARG_ALL); + + ctx = isl_ctx_alloc_with_options(&isl_options_args, options); + + p = isl_printer_to_file(ctx, stdout); + + map = isl_map_read_from_file(ctx, stdin); + map = isl_map_transitive_closure(map, &exact); + if (!exact) + p = isl_printer_print_str(p, "# NOT exact\n"); + p = isl_printer_print_map(p, map); + p = isl_printer_end_line(p); + map = isl_map_compute_divs(map); + map = isl_map_coalesce(map); + p = isl_printer_print_str(p, "# coalesced\n"); + p = isl_printer_print_map(p, map); + p = isl_printer_end_line(p); + isl_map_free(map); + + isl_printer_free(p); + + isl_ctx_free(ctx); + + return 0; +} Index: lib/Analysis/isl/codegen.c =================================================================== --- /dev/null +++ lib/Analysis/isl/codegen.c @@ -0,0 +1,244 @@ +/* + * Copyright 2012,2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +/* This program prints an AST that scans the domain elements of + * the domain of a given schedule in the order specified by + * the schedule tree or by their image(s) in the schedule map. + * + * The input consists of either a schedule tree or + * a sequence of three sets/relations. + * - a schedule map + * - a context + * - a relation describing AST generation options + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct options { + struct isl_options *isl; + unsigned atomic; + unsigned separate; +}; + +ISL_ARGS_START(struct options, options_args) +ISL_ARG_CHILD(struct options, isl, "isl", &isl_options_args, "isl options") +ISL_ARG_BOOL(struct options, atomic, 0, "atomic", 0, + "globally set the atomic option") +ISL_ARG_BOOL(struct options, separate, 0, "separate", 0, + "globally set the separate option") +ISL_ARGS_END + +ISL_ARG_DEF(cg_options, struct options, options_args) +ISL_ARG_CTX_DEF(cg_options, struct options, options_args) + +/* Return a universal, 1-dimensional set with the given name. + */ +static __isl_give isl_union_set *universe(isl_ctx *ctx, const char *name) +{ + isl_space *space; + + space = isl_space_set_alloc(ctx, 0, 1); + space = isl_space_set_tuple_name(space, isl_dim_set, name); + return isl_union_set_from_set(isl_set_universe(space)); +} + +/* Set the "name" option for the entire schedule domain. + */ +static __isl_give isl_union_map *set_universe(__isl_take isl_union_map *opt, + __isl_keep isl_union_map *schedule, const char *name) +{ + isl_ctx *ctx; + isl_union_set *domain, *target; + isl_union_map *option; + + ctx = isl_union_map_get_ctx(opt); + + domain = isl_union_map_range(isl_union_map_copy(schedule)); + domain = isl_union_set_universe(domain); + target = universe(ctx, name); + option = isl_union_map_from_domain_and_range(domain, target); + opt = isl_union_map_union(opt, option); + + return opt; +} + +/* Update the build options based on the user-specified options. + * + * If the --separate or --atomic options were specified, then + * we clear any separate or atomic options that may already exist in "opt". + */ +static __isl_give isl_ast_build *set_options(__isl_take isl_ast_build *build, + __isl_take isl_union_map *opt, struct options *options, + __isl_keep isl_union_map *schedule) +{ + if (options->separate || options->atomic) { + isl_ctx *ctx; + isl_union_set *target; + + ctx = isl_union_map_get_ctx(schedule); + + target = universe(ctx, "separate"); + opt = isl_union_map_subtract_range(opt, target); + target = universe(ctx, "atomic"); + opt = isl_union_map_subtract_range(opt, target); + } + + if (options->separate) + opt = set_universe(opt, schedule, "separate"); + if (options->atomic) + opt = set_universe(opt, schedule, "atomic"); + + build = isl_ast_build_set_options(build, opt); + + return build; +} + +/* Construct an AST in case the schedule is specified by a union map. + * + * We read the context and the options from "s" and construct the AST. + */ +static __isl_give isl_ast_node *construct_ast_from_union_map( + __isl_take isl_union_map *schedule, __isl_keep isl_stream *s) +{ + isl_set *context; + isl_union_map *options_map; + isl_ast_build *build; + isl_ast_node *tree; + struct options *options; + + options = isl_ctx_peek_cg_options(isl_stream_get_ctx(s)); + + context = isl_stream_read_set(s); + options_map = isl_stream_read_union_map(s); + + build = isl_ast_build_from_context(context); + build = set_options(build, options_map, options, schedule); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + + return tree; +} + +/* If "node" is a band node, then replace the AST build options + * by "options". + */ +static __isl_give isl_schedule_node *node_set_options( + __isl_take isl_schedule_node *node, void *user) +{ + enum isl_ast_loop_type *type = user; + int i, n; + + if (isl_schedule_node_get_type(node) != isl_schedule_node_band) + return node; + + n = isl_schedule_node_band_n_member(node); + for (i = 0; i < n; ++i) + node = isl_schedule_node_band_member_set_ast_loop_type(node, + i, *type); + return node; +} + +/* Replace the AST build options on all band nodes if requested + * by the user. + */ +static __isl_give isl_schedule *schedule_set_options( + __isl_take isl_schedule *schedule, struct options *options) +{ + enum isl_ast_loop_type type; + + if (!options->separate && !options->atomic) + return schedule; + + type = options->separate ? isl_ast_loop_separate : isl_ast_loop_atomic; + schedule = isl_schedule_map_schedule_node_bottom_up(schedule, + &node_set_options, &type); + + return schedule; +} + +/* Construct an AST in case the schedule is specified by a schedule tree. + */ +static __isl_give isl_ast_node *construct_ast_from_schedule( + __isl_take isl_schedule *schedule) +{ + isl_ast_build *build; + isl_ast_node *tree; + struct options *options; + + options = isl_ctx_peek_cg_options(isl_schedule_get_ctx(schedule)); + + build = isl_ast_build_alloc(isl_schedule_get_ctx(schedule)); + schedule = schedule_set_options(schedule, options); + tree = isl_ast_build_node_from_schedule(build, schedule); + isl_ast_build_free(build); + + return tree; +} + +/* Read an object from stdin. + * If it is a (union) map, then assume an input specified by + * schedule map, context and options and construct an AST from + * those elements + * If it is a schedule object, then construct the AST from the schedule. + */ +int main(int argc, char **argv) +{ + isl_ctx *ctx; + isl_stream *s; + isl_ast_node *tree = NULL; + struct options *options; + isl_printer *p; + struct isl_obj obj; + int r = EXIT_SUCCESS; + + options = cg_options_new_with_defaults(); + assert(options); + ctx = isl_ctx_alloc_with_options(&options_args, options); + isl_options_set_ast_build_detect_min_max(ctx, 1); + argc = cg_options_parse(options, argc, argv, ISL_ARG_ALL); + + s = isl_stream_new_file(ctx, stdin); + obj = isl_stream_read_obj(s); + if (obj.v == NULL) { + r = EXIT_FAILURE; + } else if (obj.type == isl_obj_map) { + isl_union_map *umap; + + umap = isl_union_map_from_map(obj.v); + tree = construct_ast_from_union_map(umap, s); + } else if (obj.type == isl_obj_union_map) { + tree = construct_ast_from_union_map(obj.v, s); + } else if (obj.type == isl_obj_schedule) { + tree = construct_ast_from_schedule(obj.v); + } else { + obj.type->free(obj.v); + isl_die(ctx, isl_error_invalid, "unknown input", + r = EXIT_FAILURE); + } + isl_stream_free(s); + + p = isl_printer_to_file(ctx, stdout); + p = isl_printer_set_output_format(p, ISL_FORMAT_C); + p = isl_printer_print_ast_node(p, tree); + isl_printer_free(p); + + isl_ast_node_free(tree); + + isl_ctx_free(ctx); + return r; +} Index: lib/Analysis/isl/codegen_test.sh.in =================================================================== --- /dev/null +++ lib/Analysis/isl/codegen_test.sh.in @@ -0,0 +1,30 @@ +#!/bin/sh + +EXEEXT=@EXEEXT@ +srcdir=@srcdir@ + +failed=0 + +for i in $srcdir/test_inputs/codegen/*.st \ + $srcdir/test_inputs/codegen/cloog/*.st; do + echo $i; + base=`basename $i .st` + test=test-$base.c + dir=`dirname $i` + ref=$dir/$base.c + (./isl_codegen$EXEEXT < $i > $test && + diff -uw $ref $test && rm $test) || failed=1 +done +for i in $srcdir/test_inputs/codegen/*.in \ + $srcdir/test_inputs/codegen/omega/*.in \ + $srcdir/test_inputs/codegen/pldi2012/*.in; do + echo $i; + base=`basename $i .in` + test=test-$base.c + dir=`dirname $i` + ref=$dir/$base.c + (./isl_codegen$EXEEXT < $i > $test && + diff -uw $ref $test && rm $test) || failed=1 +done + +test $failed -eq 0 || exit Index: lib/Analysis/isl/compile =================================================================== --- /dev/null +++ lib/Analysis/isl/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: Index: lib/Analysis/isl/config.guess =================================================================== --- /dev/null +++ lib/Analysis/isl/config.guess @@ -0,0 +1,1420 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-03-23' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: Index: lib/Analysis/isl/config.sub =================================================================== --- /dev/null +++ lib/Analysis/isl/config.sub @@ -0,0 +1,1799 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-09-11' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: Index: lib/Analysis/isl/configure =================================================================== --- /dev/null +++ lib/Analysis/isl/configure @@ -0,0 +1,21899 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for isl 0.17.1. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: isl-development@googlegroups.com about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='isl' +PACKAGE_TARNAME='isl' +PACKAGE_VERSION='0.17.1' +PACKAGE_STRING='isl 0.17.1' +PACKAGE_BUGREPORT='isl-development@googlegroups.com' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +GIT_HEAD_VERSION +GIT_HEAD +GIT_HEAD_ID +pkgconfig_libfile +pkgconfig_libdir +WARNING_FLAGS +HAVE_CLANG_FALSE +HAVE_CLANG_TRUE +LIB_CLANG_EDIT +llvm_config_found +CLANG_LIBS +CLANG_LDFLAGS +CLANG_CXXFLAGS +SMALL_INT_OPT_FALSE +SMALL_INT_OPT_TRUE +GMP_FOR_MP_FALSE +GMP_FOR_MP_TRUE +IMATH_FOR_MP_FALSE +IMATH_FOR_MP_TRUE +NEED_GET_MEMORY_FUNCTIONS_FALSE +NEED_GET_MEMORY_FUNCTIONS_TRUE +MP_LIBS +MP_LDFLAGS +MP_CPPFLAGS +GENERATE_DOC_FALSE +GENERATE_DOC_TRUE +POD2HTML +PDFLATEX +PERL +CXXCPP +CPP +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +RANLIB +ac_ct_AR +AR +DLLTOOL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +EGREP +GREP +SED +LIBTOOL +PRTDIAG +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +ac_ct_CXX +CXXFLAGS +CXX +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +versioninfo +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_dependency_tracking +enable_portable_binary +with_gcc_arch +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +with_sysroot +enable_libtool_lock +with_int +with_gmp +with_gmp_prefix +with_gmp_exec_prefix +with_gmp_builddir +with_clang +with_clang_prefix +with_clang_exec_prefix +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CXX +CXXFLAGS +CCC +CPP +CXXCPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures isl 0.17.1 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/isl] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of isl 0.17.1:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-portable-binary + disable compiler optimizations that would produce + unportable binaries + --enable-shared[=PKGS] build shared libraries [default=yes] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-gcc-arch= use architecture for gcc -march/-mtune, + instead of guessing + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot=DIR Search for dependent libraries within DIR + (or the compiler's sysroot if not specified). + --with-int=gmp|imath|imath-32 + Which package to use to represent multi-precision + integers [default=gmp] + --with-gmp=system|build Which gmp to use [default=system] + --with-gmp-prefix=DIR Prefix of gmp installation + --with-gmp-exec-prefix=DIR + Exec prefix of gmp installation + --with-gmp-builddir=DIR Location of gmp builddir + --with-clang=system|no Which clang to use [default=no] + --with-clang-prefix=DIR Prefix of clang installation + --with-clang-exec-prefix=DIR + Exec prefix of clang installation + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CPP C preprocessor + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +isl configure 0.17.1 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ----------------------------------------------- ## +## Report this to isl-development@googlegroups.com ## +## ----------------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl + +# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES +# --------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_cxx_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ----------------------------------------------- ## +## Report this to isl-development@googlegroups.com ## +## ----------------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_cxx_check_header_mongrel +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by isl $as_me 0.17.1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in . "$srcdir"/.; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +am__api_version='1.15' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='isl' + VERSION='0.17.1' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=0;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + + +versioninfo=17:1:2 + +if test "x$prefix" != "xNONE"; then + prefix_wd=`cd $prefix && pwd` + srcdir_wd=`cd $srcdir && pwd` + wd=`pwd` + if test "x$prefix_wd" = "x$srcdir_wd"; then + as_fn_error $? "Installation in source directory not supported" "$LINENO" 5 + fi + if test "x$prefix_wd" = "x$wd"; then + as_fn_error $? "Installation in build directory not supported" "$LINENO" 5 + fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler vendor" >&5 +$as_echo_n "checking for C compiler vendor... " >&6; } +if ${ax_cv_c_compiler_vendor+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_c_compiler_vendor=unknown + # note: don't check for gcc first since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale:__PATHCC__,__PATHSCALE__ clang:__clang__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#if !($vencpp) + thisisanerror; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_c_compiler_vendor=`echo $ventest | cut -d: -f1`; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_c_compiler_vendor" >&5 +$as_echo "$ax_cv_c_compiler_vendor" >&6; } + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + + + + + +# Check whether --enable-portable-binary was given. +if test "${enable_portable_binary+set}" = set; then : + enableval=$enable_portable_binary; acx_maxopt_portable=$withval +else + acx_maxopt_portable=no +fi + + +# Try to determine "good" native compiler flags if none specified via CFLAGS +if test "$ac_test_CFLAGS" != "set"; then + CFLAGS="" + case $ax_cv_c_compiler_vendor in + dec) CFLAGS="-newc -w0 -O5 -ansi_alias -ansi_args -fp_reorder -tune host" + if test "x$acx_maxopt_portable" = xno; then + CFLAGS="$CFLAGS -arch host" + fi;; + + sun) CFLAGS="-native -fast -xO5 -dalign" + if test "x$acx_maxopt_portable" = xyes; then + CFLAGS="$CFLAGS -xarch=generic" + fi;; + + hp) CFLAGS="+Oall +Optrs_ansi +DSnative" + if test "x$acx_maxopt_portable" = xyes; then + CFLAGS="$CFLAGS +DAportable" + fi;; + + ibm) if test "x$acx_maxopt_portable" = xno; then + xlc_opt="-qarch=auto -qtune=auto" + else + xlc_opt="-qtune=auto" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $xlc_opt" >&5 +$as_echo_n "checking whether C compiler accepts $xlc_opt... " >&6; } +ax_save_FLAGS=$CFLAGS + CFLAGS="$xlc_opt" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval `$as_echo "ax_cv_c_flags_$xlc_opt" | $as_tr_sh`=yes +else + eval `$as_echo "ax_cv_c_flags_$xlc_opt" | $as_tr_sh`=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +eval ax_check_compiler_flags=$`$as_echo "ax_cv_c_flags_$xlc_opt" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + CFLAGS="-O3 -qansialias -w $xlc_opt" +else + CFLAGS="-O3 -qansialias -w" + echo "******************************************************" + echo "* You seem to have the IBM C compiler. It is *" + echo "* recommended for best performance that you use: *" + echo "* *" + echo "* CFLAGS=-O3 -qarch=xxx -qtune=xxx -qansialias -w *" + echo "* ^^^ ^^^ *" + echo "* where xxx is pwr2, pwr3, 604, or whatever kind of *" + echo "* CPU you have. (Set the CFLAGS environment var. *" + echo "* and re-run configure.) For more info, man cc. *" + echo "******************************************************" +fi + + ;; + + intel) CFLAGS="-O3 -ansi_alias" + if test "x$acx_maxopt_portable" = xno; then + icc_archflag=unknown + icc_flags="" + case $host_cpu in + i686*|x86_64*) + # icc accepts gcc assembly syntax, so these should work: + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x86 cpuid 0 output" >&5 +$as_echo_n "checking for x86 cpuid 0 output... " >&6; } +if ${ax_cv_gcc_x86_cpuid_0+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ax_cv_gcc_x86_cpuid_0=unknown +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + int op = 0, eax, ebx, ecx, edx; + FILE *f; + __asm__("cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ax_cv_gcc_x86_cpuid_0=`cat conftest_cpuid`; rm -f conftest_cpuid +else + ax_cv_gcc_x86_cpuid_0=unknown; rm -f conftest_cpuid +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_x86_cpuid_0" >&5 +$as_echo "$ax_cv_gcc_x86_cpuid_0" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x86 cpuid 1 output" >&5 +$as_echo_n "checking for x86 cpuid 1 output... " >&6; } +if ${ax_cv_gcc_x86_cpuid_1+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ax_cv_gcc_x86_cpuid_1=unknown +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + int op = 1, eax, ebx, ecx, edx; + FILE *f; + __asm__("cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ax_cv_gcc_x86_cpuid_1=`cat conftest_cpuid`; rm -f conftest_cpuid +else + ax_cv_gcc_x86_cpuid_1=unknown; rm -f conftest_cpuid +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_x86_cpuid_1" >&5 +$as_echo "$ax_cv_gcc_x86_cpuid_1" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + case $ax_cv_gcc_x86_cpuid_0 in # see AX_GCC_ARCHFLAG + *:756e6547:*:*) # Intel + case $ax_cv_gcc_x86_cpuid_1 in + *6a?:*[234]:*:*|*6[789b]?:*:*:*) icc_flags="-xK";; + *f3[347]:*:*:*|*f41347:*:*:*) icc_flags="-xP -xN -xW -xK";; + *f??:*:*:*) icc_flags="-xN -xW -xK";; + esac ;; + esac ;; + esac + if test "x$icc_flags" != x; then + for flag in $icc_flags; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $flag" >&5 +$as_echo_n "checking whether C compiler accepts $flag... " >&6; } +ax_save_FLAGS=$CFLAGS + CFLAGS="$flag" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval `$as_echo "ax_cv_c_flags_$flag" | $as_tr_sh`=yes +else + eval `$as_echo "ax_cv_c_flags_$flag" | $as_tr_sh`=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +eval ax_check_compiler_flags=$`$as_echo "ax_cv_c_flags_$flag" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + icc_archflag=$flag; break +else + : +fi + + done + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for icc architecture flag" >&5 +$as_echo_n "checking for icc architecture flag... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $icc_archflag" >&5 +$as_echo "$icc_archflag" >&6; } + if test "x$icc_archflag" != xunknown; then + CFLAGS="$CFLAGS $icc_archflag" + fi + fi + ;; + + gnu) + # default optimization flags for gcc on all systems + CFLAGS="-O3 -fomit-frame-pointer" + + # -malign-double for x86 systems + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -malign-double" >&5 +$as_echo_n "checking whether C compiler accepts -malign-double... " >&6; } +if ${ax_cv_c_flags__malign_double+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_save_FLAGS=$CFLAGS + CFLAGS="-malign-double" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_c_flags__malign_double=yes +else + ax_cv_c_flags__malign_double=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +fi + +eval ax_check_compiler_flags=$ax_cv_c_flags__malign_double +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + CFLAGS="$CFLAGS -malign-double" +else + : +fi + + + # -fstrict-aliasing for gcc-2.95+ + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fstrict-aliasing" >&5 +$as_echo_n "checking whether C compiler accepts -fstrict-aliasing... " >&6; } +if ${ax_cv_c_flags__fstrict_aliasing+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_save_FLAGS=$CFLAGS + CFLAGS="-fstrict-aliasing" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_c_flags__fstrict_aliasing=yes +else + ax_cv_c_flags__fstrict_aliasing=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +fi + +eval ax_check_compiler_flags=$ax_cv_c_flags__fstrict_aliasing +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + CFLAGS="$CFLAGS -fstrict-aliasing" +else + : +fi + + + # note that we enable "unsafe" fp optimization with other compilers, too + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -ffast-math" >&5 +$as_echo_n "checking whether C compiler accepts -ffast-math... " >&6; } +if ${ax_cv_c_flags__ffast_math+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_save_FLAGS=$CFLAGS + CFLAGS="-ffast-math" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_c_flags__ffast_math=yes +else + ax_cv_c_flags__ffast_math=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +fi + +eval ax_check_compiler_flags=$ax_cv_c_flags__ffast_math +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + CFLAGS="$CFLAGS -ffast-math" +else + : +fi + + + + + + +# Check whether --with-gcc-arch was given. +if test "${with_gcc_arch+set}" = set; then : + withval=$with_gcc_arch; ax_gcc_arch=$withval +else + ax_gcc_arch=yes +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcc architecture flag" >&5 +$as_echo_n "checking for gcc architecture flag... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } +if ${ax_cv_gcc_archflag+:} false; then : + $as_echo_n "(cached) " >&6 +else + +ax_cv_gcc_archflag="unknown" + +if test "$GCC" = yes; then + +if test "x$ax_gcc_arch" = xyes; then +ax_gcc_arch="" +if test "$cross_compiling" = no; then +case $host_cpu in + i[3456]86*|x86_64*) # use cpuid codes, in part from x86info-1.7 by D. Jones + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x86 cpuid 0 output" >&5 +$as_echo_n "checking for x86 cpuid 0 output... " >&6; } +if ${ax_cv_gcc_x86_cpuid_0+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ax_cv_gcc_x86_cpuid_0=unknown +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + int op = 0, eax, ebx, ecx, edx; + FILE *f; + __asm__("cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ax_cv_gcc_x86_cpuid_0=`cat conftest_cpuid`; rm -f conftest_cpuid +else + ax_cv_gcc_x86_cpuid_0=unknown; rm -f conftest_cpuid +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_x86_cpuid_0" >&5 +$as_echo "$ax_cv_gcc_x86_cpuid_0" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x86 cpuid 1 output" >&5 +$as_echo_n "checking for x86 cpuid 1 output... " >&6; } +if ${ax_cv_gcc_x86_cpuid_1+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ax_cv_gcc_x86_cpuid_1=unknown +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + int op = 1, eax, ebx, ecx, edx; + FILE *f; + __asm__("cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ax_cv_gcc_x86_cpuid_1=`cat conftest_cpuid`; rm -f conftest_cpuid +else + ax_cv_gcc_x86_cpuid_1=unknown; rm -f conftest_cpuid +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_x86_cpuid_1" >&5 +$as_echo "$ax_cv_gcc_x86_cpuid_1" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + case $ax_cv_gcc_x86_cpuid_0 in + *:756e6547:*:*) # Intel + case $ax_cv_gcc_x86_cpuid_1 in + *5[48]?:*:*:*) ax_gcc_arch="pentium-mmx pentium" ;; + *5??:*:*:*) ax_gcc_arch=pentium ;; + *6[3456]?:*:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; + *6a?:*[01]:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; + *6a?:*[234]:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; + *6[9d]?:*:*:*) ax_gcc_arch="pentium-m pentium3 pentiumpro" ;; + *6[78b]?:*:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; + *6??:*:*:*) ax_gcc_arch=pentiumpro ;; + *f3[347]:*:*:*|*f41347:*:*:*) + case $host_cpu in + x86_64*) ax_gcc_arch="nocona pentium4 pentiumpro" ;; + *) ax_gcc_arch="prescott pentium4 pentiumpro" ;; + esac ;; + *f??:*:*:*) ax_gcc_arch="pentium4 pentiumpro";; + esac ;; + *:68747541:*:*) # AMD + case $ax_cv_gcc_x86_cpuid_1 in + *5[67]?:*:*:*) ax_gcc_arch=k6 ;; + *5[8d]?:*:*:*) ax_gcc_arch="k6-2 k6" ;; + *5[9]?:*:*:*) ax_gcc_arch="k6-3 k6" ;; + *60?:*:*:*) ax_gcc_arch=k7 ;; + *6[12]?:*:*:*) ax_gcc_arch="athlon k7" ;; + *6[34]?:*:*:*) ax_gcc_arch="athlon-tbird k7" ;; + *67?:*:*:*) ax_gcc_arch="athlon-4 athlon k7" ;; + *6[68a]?:*:*:*) + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for x86 cpuid 0x80000006 output" >&5 +$as_echo_n "checking for x86 cpuid 0x80000006 output... " >&6; } +if ${ax_cv_gcc_x86_cpuid_0x80000006+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ax_cv_gcc_x86_cpuid_0x80000006=unknown +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + int op = 0x80000006, eax, ebx, ecx, edx; + FILE *f; + __asm__("cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ax_cv_gcc_x86_cpuid_0x80000006=`cat conftest_cpuid`; rm -f conftest_cpuid +else + ax_cv_gcc_x86_cpuid_0x80000006=unknown; rm -f conftest_cpuid +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_x86_cpuid_0x80000006" >&5 +$as_echo "$ax_cv_gcc_x86_cpuid_0x80000006" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + # L2 cache size + case $ax_cv_gcc_x86_cpuid_0x80000006 in + *:*:*[1-9a-f]??????:*) # (L2 = ecx >> 16) >= 256 + ax_gcc_arch="athlon-xp athlon-4 athlon k7" ;; + *) ax_gcc_arch="athlon-4 athlon k7" ;; + esac ;; + *f[4cef8b]?:*:*:*) ax_gcc_arch="athlon64 k8" ;; + *f5?:*:*:*) ax_gcc_arch="opteron k8" ;; + *f7?:*:*:*) ax_gcc_arch="athlon-fx opteron k8" ;; + *f??:*:*:*) ax_gcc_arch="k8" ;; + esac ;; + *:746e6543:*:*) # IDT + case $ax_cv_gcc_x86_cpuid_1 in + *54?:*:*:*) ax_gcc_arch=winchip-c6 ;; + *58?:*:*:*) ax_gcc_arch=winchip2 ;; + *6[78]?:*:*:*) ax_gcc_arch=c3 ;; + *69?:*:*:*) ax_gcc_arch="c3-2 c3" ;; + esac ;; + esac + if test x"$ax_gcc_arch" = x; then # fallback + case $host_cpu in + i586*) ax_gcc_arch=pentium ;; + i686*) ax_gcc_arch=pentiumpro ;; + esac + fi + ;; + + sparc*) + # Extract the first word of "prtdiag", so it can be a program name with args. +set dummy prtdiag; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PRTDIAG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PRTDIAG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PRTDIAG="$PRTDIAG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/platform/`uname -i`/sbin/:/usr/platform/`uname -m`/sbin/" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PRTDIAG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_PRTDIAG" && ac_cv_path_PRTDIAG="prtdiag" + ;; +esac +fi +PRTDIAG=$ac_cv_path_PRTDIAG +if test -n "$PRTDIAG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PRTDIAG" >&5 +$as_echo "$PRTDIAG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + cputype=`(((grep cpu /proc/cpuinfo | cut -d: -f2) ; ($PRTDIAG -v |grep -i sparc) ; grep -i cpu /var/run/dmesg.boot ) | head -n 1) 2> /dev/null` + cputype=`echo "$cputype" | tr -d ' -' |tr $as_cr_LETTERS $as_cr_letters` + case $cputype in + *ultrasparciv*) ax_gcc_arch="ultrasparc4 ultrasparc3 ultrasparc v9" ;; + *ultrasparciii*) ax_gcc_arch="ultrasparc3 ultrasparc v9" ;; + *ultrasparc*) ax_gcc_arch="ultrasparc v9" ;; + *supersparc*|*tms390z5[05]*) ax_gcc_arch="supersparc v8" ;; + *hypersparc*|*rt62[056]*) ax_gcc_arch="hypersparc v8" ;; + *cypress*) ax_gcc_arch=cypress ;; + esac ;; + + alphaev5) ax_gcc_arch=ev5 ;; + alphaev56) ax_gcc_arch=ev56 ;; + alphapca56) ax_gcc_arch="pca56 ev56" ;; + alphapca57) ax_gcc_arch="pca57 pca56 ev56" ;; + alphaev6) ax_gcc_arch=ev6 ;; + alphaev67) ax_gcc_arch=ev67 ;; + alphaev68) ax_gcc_arch="ev68 ev67" ;; + alphaev69) ax_gcc_arch="ev69 ev68 ev67" ;; + alphaev7) ax_gcc_arch="ev7 ev69 ev68 ev67" ;; + alphaev79) ax_gcc_arch="ev79 ev7 ev69 ev68 ev67" ;; + + powerpc*) + cputype=`((grep cpu /proc/cpuinfo | head -n 1 | cut -d: -f2 | cut -d, -f1 | sed 's/ //g') ; /usr/bin/machine ; /bin/machine; grep CPU /var/run/dmesg.boot | head -n 1 | cut -d" " -f2) 2> /dev/null` + cputype=`echo $cputype | sed -e 's/ppc//g;s/ *//g'` + case $cputype in + *750*) ax_gcc_arch="750 G3" ;; + *740[0-9]*) ax_gcc_arch="$cputype 7400 G4" ;; + *74[4-5][0-9]*) ax_gcc_arch="$cputype 7450 G4" ;; + *74[0-9][0-9]*) ax_gcc_arch="$cputype G4" ;; + *970*) ax_gcc_arch="970 G5 power4";; + *POWER4*|*power4*|*gq*) ax_gcc_arch="power4 970";; + *POWER5*|*power5*|*gr*|*gs*) ax_gcc_arch="power5 power4 970";; + 603ev|8240) ax_gcc_arch="$cputype 603e 603";; + *) ax_gcc_arch=$cputype ;; + esac + ax_gcc_arch="$ax_gcc_arch powerpc" + ;; +esac +fi # not cross-compiling +fi # guess arch + +if test "x$ax_gcc_arch" != x -a "x$ax_gcc_arch" != xno; then +for arch in $ax_gcc_arch; do + if test "x$acx_maxopt_portable" = xyes; then # if we require portable code + flags="-mtune=$arch" + # -mcpu=$arch and m$arch generate nonportable code on every arch except + # x86. And some other arches (e.g. Alpha) don't accept -mtune. Grrr. + case $host_cpu in i*86|x86_64*) flags="$flags -mcpu=$arch -m$arch";; esac + else + flags="-march=$arch -mcpu=$arch -m$arch" + fi + for flag in $flags; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $flag" >&5 +$as_echo_n "checking whether C compiler accepts $flag... " >&6; } +ax_save_FLAGS=$CFLAGS + CFLAGS="$flag" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval `$as_echo "ax_cv_c_flags_$flag" | $as_tr_sh`=yes +else + eval `$as_echo "ax_cv_c_flags_$flag" | $as_tr_sh`=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +eval ax_check_compiler_flags=$`$as_echo "ax_cv_c_flags_$flag" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + ax_cv_gcc_archflag=$flag; break +else + : +fi + + done + test "x$ax_cv_gcc_archflag" = xunknown || break +done +fi + +fi # $GCC=yes + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcc architecture flag" >&5 +$as_echo_n "checking for gcc architecture flag... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_archflag" >&5 +$as_echo "$ax_cv_gcc_archflag" >&6; } +if test "x$ax_cv_gcc_archflag" = xunknown; then + : +else + CFLAGS="$CFLAGS $ax_cv_gcc_archflag" +fi + + + # drop to -O1 for gcc 4.2 + $CC --version | + sed -e 's/.* \([0-9][0-9]*\)\.\([0-9][0-9]*\).*/\1 \2/' | + (read major minor + if test $major -eq 4 -a $minor -eq 2; then + exit 0 + fi + exit 1 + ) && CFLAGS="-O1" + ;; + esac + + if test -z "$CFLAGS"; then + echo "" + echo "********************************************************" + echo "* WARNING: Don't know the best CFLAGS for this system *" + echo "* Use ./configure CFLAGS=... to specify your own flags *" + echo "* (otherwise, a default of CFLAGS=-O3 will be used) *" + echo "********************************************************" + echo "" + CFLAGS="-O3" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $CFLAGS" >&5 +$as_echo_n "checking whether C compiler accepts $CFLAGS... " >&6; } +ax_save_FLAGS=$CFLAGS + CFLAGS="$CFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval `$as_echo "ax_cv_c_flags_$CFLAGS" | $as_tr_sh`=yes +else + eval `$as_echo "ax_cv_c_flags_$CFLAGS" | $as_tr_sh`=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_save_FLAGS +eval ax_check_compiler_flags=$`$as_echo "ax_cv_c_flags_$CFLAGS" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_compiler_flags" >&5 +$as_echo "$ax_check_compiler_flags" >&6; } +if test "x$ax_check_compiler_flags" = xyes; then + : +else + + echo "" + echo "********************************************************" + echo "* WARNING: The guessed CFLAGS don't seem to work with *" + echo "* your compiler. *" + echo "* Use ./configure CFLAGS=... to specify your own flags *" + echo "********************************************************" + echo "" + CFLAGS="" + +fi + + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports function __attribute__((__warn_unused_result__))" >&5 +$as_echo_n "checking whether the compiler supports function __attribute__((__warn_unused_result__))... " >&6; } +if ${ax_cv_gcc_warn_unused_result+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +__attribute__((__warn_unused_result__)) + int f(int i) { return i; } +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_gcc_warn_unused_result=yes +else + ax_cv_gcc_warn_unused_result=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_gcc_warn_unused_result" >&5 +$as_echo "$ax_cv_gcc_warn_unused_result" >&6; } + if test "$ax_cv_gcc_warn_unused_result" = yes; then + +$as_echo "#define GCC_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__" >&5 +$as_echo_n "checking for __attribute__... " >&6; } +if ${ax_cv___attribute__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + static void foo(void) __attribute__ ((unused)); + static void + foo(void) { + exit(1); + } + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv___attribute__=yes +else + ax_cv___attribute__=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv___attribute__" >&5 +$as_echo "$ax_cv___attribute__" >&6; } + if test "$ax_cv___attribute__" = "yes"; then + +$as_echo "#define HAVE___ATTRIBUTE__ 1" >>confdefs.h + + fi + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.2' +macro_revision='1.3337' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case "$ECHO" in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test "$GCC" != yes; then + reload_cmds=false + fi + ;; + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 +$as_echo "${with_sysroot}" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[012]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes +fi + + + + + + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + link_all_deplibs=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test "$lt_cv_irix_exported_symbol" = yes; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([A-Za-z]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec_CXX='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + +# Extract the first word of "perl", so it can be a program name with args. +set dummy perl; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PERL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PERL"; then + ac_cv_prog_PERL="$PERL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PERL="perl" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PERL=$ac_cv_prog_PERL +if test -n "$PERL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 +$as_echo "$PERL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "pdflatex", so it can be a program name with args. +set dummy pdflatex; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PDFLATEX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PDFLATEX"; then + ac_cv_prog_PDFLATEX="$PDFLATEX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PDFLATEX="pdflatex" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PDFLATEX=$ac_cv_prog_PDFLATEX +if test -n "$PDFLATEX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PDFLATEX" >&5 +$as_echo "$PDFLATEX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "pod2html", so it can be a program name with args. +set dummy pod2html; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_POD2HTML+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$POD2HTML"; then + ac_cv_prog_POD2HTML="$POD2HTML" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_POD2HTML="pod2html" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +POD2HTML=$ac_cv_prog_POD2HTML +if test -n "$POD2HTML"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $POD2HTML" >&5 +$as_echo "$POD2HTML" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + if test -n "$PERL" -a -n "$PDFLATEX" -a -n "$POD2HTML"; then + GENERATE_DOC_TRUE= + GENERATE_DOC_FALSE='#' +else + GENERATE_DOC_TRUE='#' + GENERATE_DOC_FALSE= +fi + + +# ------ AX CREATE STDINT H ------------------------------------- +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint types" >&5 +$as_echo_n "checking for stdint types... " >&6; } +ac_stdint_h=`echo include/isl/stdint.h` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +if ${ac_cv_header_stdint_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int_least32_t v = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; +else + ac_cv_header_stdint_t="" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then +CFLAGS="-std=c99" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int_least32_t v = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: your GCC compiler has a defunct stdint.h for its default-mode" >&5 +$as_echo "$as_me: WARNING: your GCC compiler has a defunct stdint.h for its default-mode" >&2;} +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" +fi + + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (are you sure you want them in ./stdint.h?)" >&5 +$as_echo "(are you sure you want them in ./stdint.h?)" >&6; } +elif test "$ac_stdint_h" = "inttypes.h" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (are you sure you want them in ./inttypes.h?)" >&5 +$as_echo "(are you sure you want them in ./inttypes.h?)" >&6; } +elif test "_$ac_cv_header_stdint_t" = "_" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (putting them into $ac_stdint_h)$v" >&5 +$as_echo "(putting them into $ac_stdint_h)$v" >&6; } +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint (shortcircuit)" >&5 +$as_echo "$ac_cv_header_stdint (shortcircuit)" >&6; } +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + + +inttype_headers=`echo | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint uintptr_t" >&5 +$as_echo_n "checking for stdint uintptr_t... " >&6; } +if ${ac_cv_header_stdint_x+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 +$as_echo "(..)" >&6; } + for i in stdint.h inttypes.h sys/inttypes.h $inttype_headers + do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "#include <$i> +" +if test "x$ac_cv_type_uintptr_t" = xyes; then : + ac_cv_header_stdint_x=$i +else + continue +fi + + ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "#include<$i> +" +if test "x$ac_cv_type_uint64_t" = xyes; then : + and64="/uint64_t" +else + and64="" +fi + + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)" + break + done + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint uintptr_t" >&5 +$as_echo_n "checking for stdint uintptr_t... " >&6; } + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint_x" >&5 +$as_echo "$ac_cv_header_stdint_x" >&6; } + + +if test "_$ac_cv_header_stdint_x" = "_" ; then + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint uint32_t" >&5 +$as_echo_n "checking for stdint uint32_t... " >&6; } +if ${ac_cv_header_stdint_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 +$as_echo "(..)" >&6; } + for i in inttypes.h sys/inttypes.h stdint.h $inttype_headers + do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + ac_fn_c_check_type "$LINENO" "uint32_t" "ac_cv_type_uint32_t" "#include <$i> +" +if test "x$ac_cv_type_uint32_t" = xyes; then : + ac_cv_header_stdint_o=$i +else + continue +fi + + ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "#include<$i> +" +if test "x$ac_cv_type_uint64_t" = xyes; then : + and64="/uint64_t" +else + and64="" +fi + + ac_cv_stdint_result="(seen uint32_t$and64 in $i)" + break + break; + done + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint uint32_t" >&5 +$as_echo_n "checking for stdint uint32_t... " >&6; } + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint_o" >&5 +$as_echo "$ac_cv_header_stdint_o" >&6; } + +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint u_int32_t" >&5 +$as_echo_n "checking for stdint u_int32_t... " >&6; } +if ${ac_cv_header_stdint_u+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 +$as_echo "(..)" >&6; } + for i in sys/types.h inttypes.h sys/inttypes.h $inttype_headers ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + ac_fn_c_check_type "$LINENO" "u_int32_t" "ac_cv_type_u_int32_t" "#include <$i> +" +if test "x$ac_cv_type_u_int32_t" = xyes; then : + ac_cv_header_stdint_u=$i +else + continue +fi + + ac_fn_c_check_type "$LINENO" "u_int64_t" "ac_cv_type_u_int64_t" "#include<$i> +" +if test "x$ac_cv_type_u_int64_t" = xyes; then : + and64="/u_int64_t" +else + and64="" +fi + + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)" + break + break; + done + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint u_int32_t" >&5 +$as_echo_n "checking for stdint u_int32_t... " >&6; } + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdint_u" >&5 +$as_echo "$ac_cv_header_stdint_u" >&6; } + +fi fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdint datatype model" >&5 +$as_echo_n "checking for stdint datatype model... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: (..)" >&5 +$as_echo "(..)" >&6; } + + # The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of char" >&5 +$as_echo_n "checking size of char... " >&6; } +if ${ac_cv_sizeof_char+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (char))" "ac_cv_sizeof_char" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_char" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (char) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_char=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_char" >&5 +$as_echo "$ac_cv_sizeof_char" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_CHAR $ac_cv_sizeof_char +_ACEOF + + + # The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5 +$as_echo_n "checking size of short... " >&6; } +if ${ac_cv_sizeof_short+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_short" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (short) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_short=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5 +$as_echo "$ac_cv_sizeof_short" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SHORT $ac_cv_sizeof_short +_ACEOF + + + # The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 +$as_echo_n "checking size of int... " >&6; } +if ${ac_cv_sizeof_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_int" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_int=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 +$as_echo "$ac_cv_sizeof_int" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT $ac_cv_sizeof_int +_ACEOF + + + # The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 +$as_echo_n "checking size of long... " >&6; } +if ${ac_cv_sizeof_long+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_long" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (long) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 +$as_echo "$ac_cv_sizeof_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG $ac_cv_sizeof_long +_ACEOF + + + # The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void*" >&5 +$as_echo_n "checking size of void*... " >&6; } +if ${ac_cv_sizeof_voidp+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void*))" "ac_cv_sizeof_voidp" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_voidp" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (void*) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_voidp=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_voidp" >&5 +$as_echo "$ac_cv_sizeof_voidp" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_VOIDP $ac_cv_sizeof_voidp +_ACEOF + + + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking data model" >&5 +$as_echo_n "checking data model... " >&6; } + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_data_model ($ac_cv_long_data_model, $n)" >&5 +$as_echo "$ac_cv_data_model ($ac_cv_long_data_model, $n)" >&6; } + +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for extra inttypes in chosen header" >&5 +$as_echo_n "checking for extra inttypes in chosen header... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ($ac_cv_header_stdint)" >&5 +$as_echo "($ac_cv_header_stdint)" >&6; } +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +ac_fn_c_check_type "$LINENO" "int_least32_t" "ac_cv_type_int_least32_t" "#include <$ac_cv_header_stdint> +" +if test "x$ac_cv_type_int_least32_t" = xyes; then : + +fi + +ac_fn_c_check_type "$LINENO" "int_fast32_t" "ac_cv_type_int_fast32_t" "#include<$ac_cv_header_stdint> +" +if test "x$ac_cv_type_int_fast32_t" = xyes; then : + +fi + +ac_fn_c_check_type "$LINENO" "intmax_t" "ac_cv_type_intmax_t" "#include <$ac_cv_header_stdint> +" +if test "x$ac_cv_type_intmax_t" = xyes; then : + +fi + + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: make use of $ac_cv_header_stdint in $ac_stdint_h $ac_cv_stdint_result" >&5 +$as_echo "make use of $ac_cv_header_stdint in $ac_stdint_h $ac_cv_stdint_result" >&6; } + +# ----------------- DONE inttypes.h checks START header ------------- +ac_config_commands="$ac_config_commands $ac_stdint_h" + + + + +# Check whether --with-int was given. +if test "${with_int+set}" = set; then : + withval=$with_int; +else + with_int=gmp +fi + +case "$with_int" in +gmp|imath|imath-32) + ;; +*) + as_fn_error $? "bad value ${withval} for --with-int (use gmp, imath or imath-32)" "$LINENO" 5 +esac + + + + +case "$with_int" in +gmp) + + +$as_echo "#define USE_GMP_FOR_MP /**/" >>confdefs.h + + + + +# Check whether --with-gmp was given. +if test "${with_gmp+set}" = set; then : + withval=$with_gmp; +fi + +case "system" in +system|build) + +# Check whether --with-gmp_prefix was given. +if test "${with_gmp_prefix+set}" = set; then : + withval=$with_gmp_prefix; +fi + + +# Check whether --with-gmp_exec_prefix was given. +if test "${with_gmp_exec_prefix+set}" = set; then : + withval=$with_gmp_exec_prefix; +fi + +esac + +# Check whether --with-gmp_builddir was given. +if test "${with_gmp_builddir+set}" = set; then : + withval=$with_gmp_builddir; +fi + +if test "x$with_gmp_prefix" != "x" -a "x$with_gmp_exec_prefix" = "x"; then + with_gmp_exec_prefix=$with_gmp_prefix +fi +if test "x$with_gmp_prefix" != "x" -o "x$with_gmp_exec_prefix" != "x"; then + if test "x$with_gmp" != "x" -a "x$with_gmp" != "xyes" -a "x$with_gmp" != "xsystem"; then + as_fn_error $? "Setting $with_gmp_prefix implies use of system gmp" "$LINENO" 5 + fi + with_gmp="system" +fi +if test "x$with_gmp_builddir" != "x"; then + if test "x$with_gmp" != "x" -a "x$with_gmp" != "xyes" -a "x$with_gmp" != "xbuild"; then + as_fn_error $? "Setting $with_gmp_builddir implies use of build gmp" "$LINENO" 5 + fi + with_gmp="build" + gmp_srcdir=`echo @abs_srcdir@ | $with_gmp_builddir/config.status --file=-` + { $as_echo "$as_me:${as_lineno-$LINENO}: gmp sources in $gmp_srcdir" >&5 +$as_echo "$as_me: gmp sources in $gmp_srcdir" >&6;} +fi +if test "x$with_gmp_exec_prefix" != "x"; then + export PKG_CONFIG_PATH="$with_gmp_exec_prefix/lib/pkgconfig${PKG_CONFIG_PATH+:$PKG_CONFIG_PATH}" +fi +case "$with_gmp" in +system|build) + ;; +*) + case "system" in + bundled) + if test -d $srcdir/.git -a \ + -d $srcdir/gmp -a \ + ! -d $srcdir/gmp/.git; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: git repo detected, but submodule gmp not initialized" >&5 +$as_echo "$as_me: WARNING: git repo detected, but submodule gmp not initialized" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You may want to run" >&5 +$as_echo "$as_me: WARNING: You may want to run" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: git submodule init" >&5 +$as_echo "$as_me: WARNING: git submodule init" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: git submodule update" >&5 +$as_echo "$as_me: WARNING: git submodule update" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: sh autogen.sh" >&5 +$as_echo "$as_me: WARNING: sh autogen.sh" >&2;} + fi + if test -f $srcdir/gmp/configure; then + with_gmp="bundled" + else + with_gmp="no" + fi + ;; + *) + with_gmp="system" + ;; + esac + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which gmp to use" >&5 +$as_echo_n "checking which gmp to use... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_gmp" >&5 +$as_echo "$with_gmp" >&6; } + + +case "$with_gmp" in +system) + if test "x$with_gmp_prefix" != "x"; then + isl_configure_args="$isl_configure_args --with-gmp=$with_gmp_prefix" + MP_CPPFLAGS="-I$with_gmp_prefix/include" + MP_LDFLAGS="-L$with_gmp_prefix/lib" + fi + MP_LIBS=-lgmp + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + SAVE_LIBS="$LIBS" + CPPFLAGS="$MP_CPPFLAGS $CPPFLAGS" + LDFLAGS="$MP_LDFLAGS $LDFLAGS" + LIBS="$MP_LIBS $LIBS" + ac_fn_c_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default" +if test "x$ac_cv_header_gmp_h" = xyes; then : + +else + as_fn_error $? "gmp.h header not found" "$LINENO" 5 +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lgmp" >&5 +$as_echo_n "checking for main in -lgmp... " >&6; } +if ${ac_cv_lib_gmp_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgmp $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_gmp_main=yes +else + ac_cv_lib_gmp_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp_main" >&5 +$as_echo "$ac_cv_lib_gmp_main" >&6; } +if test "x$ac_cv_lib_gmp_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBGMP 1 +_ACEOF + + LIBS="-lgmp $LIBS" + +else + as_fn_error $? "gmp library not found" "$LINENO" 5 +fi + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + mpz_t n, d; + if (mpz_divisible_p(n, d)) + mpz_divexact_ui(n, n, 4); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +else + as_fn_error $? "gmp library too old" "$LINENO" 5 +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" + LIBS="$SAVE_LIBS" + ;; +build) + MP_CPPFLAGS="-I$gmp_srcdir -I$with_gmp_builddir" + MP_LIBS="$with_gmp_builddir/libgmp.la" + ;; +esac +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" +SAVE_LIBS="$LIBS" +CPPFLAGS="$MP_CPPFLAGS $CPPFLAGS" +LDFLAGS="$MP_LDFLAGS $LDFLAGS" +LIBS="$MP_LIBS $LIBS" +need_get_memory_functions=false +ac_fn_c_check_decl "$LINENO" "mp_get_memory_functions" "ac_cv_have_decl_mp_get_memory_functions" "#include +" +if test "x$ac_cv_have_decl_mp_get_memory_functions" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_MP_GET_MEMORY_FUNCTIONS $ac_have_decl +_ACEOF +if test $ac_have_decl = 1; then : + +else + + need_get_memory_functions=true + +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" +LIBS="$SAVE_LIBS" + if test x$need_get_memory_functions = xtrue; then + NEED_GET_MEMORY_FUNCTIONS_TRUE= + NEED_GET_MEMORY_FUNCTIONS_FALSE='#' +else + NEED_GET_MEMORY_FUNCTIONS_TRUE='#' + NEED_GET_MEMORY_FUNCTIONS_FALSE= +fi + + + ;; +imath|imath-32) + + +$as_echo "#define USE_IMATH_FOR_MP /**/" >>confdefs.h + + +MP_CPPFLAGS="-I$srcdir/imath_wrap" +MP_LDFLAGS="" +MP_LIBS="" + +SAVE_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$MP_CPPFLAGS $CPPFLAGS" +ac_fn_c_check_header_mongrel "$LINENO" "imath.h" "ac_cv_header_imath_h" "$ac_includes_default" +if test "x$ac_cv_header_imath_h" = xyes; then : + +else + as_fn_error $? "imath.h header not found" "$LINENO" 5 +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "gmp_compat.h" "ac_cv_header_gmp_compat_h" "$ac_includes_default" +if test "x$ac_cv_header_gmp_compat_h" = xyes; then : + +else + as_fn_error $? "gmp_compat.h header not found" "$LINENO" 5 +fi + + +CPPFLAGS="$SAVE_CPPFLAGS" + + if test x = xfalse; then + NEED_GET_MEMORY_FUNCTIONS_TRUE= + NEED_GET_MEMORY_FUNCTIONS_FALSE='#' +else + NEED_GET_MEMORY_FUNCTIONS_TRUE='#' + NEED_GET_MEMORY_FUNCTIONS_FALSE= +fi + + + ;; +esac +if test "x$with_int" = "ximath-32" -a "x$GCC" = "xyes"; then + MP_CPPFLAGS="-std=gnu99 $MP_CPPFLAGS" +fi + + if test x$with_int = ximath -o x$with_int = ximath-32; then + IMATH_FOR_MP_TRUE= + IMATH_FOR_MP_FALSE='#' +else + IMATH_FOR_MP_TRUE='#' + IMATH_FOR_MP_FALSE= +fi + + if test x$with_int = xgmp; then + GMP_FOR_MP_TRUE= + GMP_FOR_MP_FALSE='#' +else + GMP_FOR_MP_TRUE='#' + GMP_FOR_MP_FALSE= +fi + + + if test "x$with_int" == "ximath-32"; then + SMALL_INT_OPT_TRUE= + SMALL_INT_OPT_FALSE='#' +else + SMALL_INT_OPT_TRUE='#' + SMALL_INT_OPT_FALSE= +fi + +if test "x$with_int" == "ximath-32"; then : + + +$as_echo "#define USE_SMALL_INT_OPT /**/" >>confdefs.h + + +fi + +ac_fn_c_check_decl "$LINENO" "ffs" "ac_cv_have_decl_ffs" "#include +" +if test "x$ac_cv_have_decl_ffs" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_FFS $ac_have_decl +_ACEOF + +ac_fn_c_check_decl "$LINENO" "__builtin_ffs" "ac_cv_have_decl___builtin_ffs" "$ac_includes_default" +if test "x$ac_cv_have_decl___builtin_ffs" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL___BUILTIN_FFS $ac_have_decl +_ACEOF + +ac_fn_c_check_decl "$LINENO" "_BitScanForward" "ac_cv_have_decl__BitScanForward" "#include +" +if test "x$ac_cv_have_decl__BitScanForward" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL__BITSCANFORWARD $ac_have_decl +_ACEOF + +if test "x$ac_cv_have_decl_ffs" = xno -a \ + "x$ac_cv_have_decl___builtin_ffs" = xno -a \ + "x$ac_cv_have_decl__BitScanForward" = xno; then + as_fn_error $? "No ffs implementation found" "$LINENO" 5 +fi +ac_fn_c_check_decl "$LINENO" "strcasecmp" "ac_cv_have_decl_strcasecmp" "#include +" +if test "x$ac_cv_have_decl_strcasecmp" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRCASECMP $ac_have_decl +_ACEOF +ac_fn_c_check_decl "$LINENO" "strncasecmp" "ac_cv_have_decl_strncasecmp" "#include +" +if test "x$ac_cv_have_decl_strncasecmp" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRNCASECMP $ac_have_decl +_ACEOF + +ac_fn_c_check_decl "$LINENO" "_stricmp" "ac_cv_have_decl__stricmp" "#include +" +if test "x$ac_cv_have_decl__stricmp" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL__STRICMP $ac_have_decl +_ACEOF +ac_fn_c_check_decl "$LINENO" "_strnicmp" "ac_cv_have_decl__strnicmp" "#include +" +if test "x$ac_cv_have_decl__strnicmp" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL__STRNICMP $ac_have_decl +_ACEOF + +if test "x$ac_cv_have_decl_strcasecmp" = xno -a \ + "x$ac_cv_have_decl__stricmp" = xno; then + as_fn_error $? "No strcasecmp implementation found" "$LINENO" 5 +fi +if test "x$ac_cv_have_decl_strncasecmp" = xno -a \ + "x$ac_cv_have_decl__strnicmp" = xno; then + as_fn_error $? "No strncasecmp implementation found" "$LINENO" 5 +fi +ac_fn_c_check_decl "$LINENO" "snprintf" "ac_cv_have_decl_snprintf" "#include +" +if test "x$ac_cv_have_decl_snprintf" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SNPRINTF $ac_have_decl +_ACEOF +ac_fn_c_check_decl "$LINENO" "_snprintf" "ac_cv_have_decl__snprintf" "#include +" +if test "x$ac_cv_have_decl__snprintf" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL__SNPRINTF $ac_have_decl +_ACEOF + +if test "x$ac_cv_have_decl_snprintf" = xno -a \ + "x$ac_cv_have_decl__snprintf" = xno; then + as_fn_error $? "No snprintf implementation found" "$LINENO" 5 +fi + + + + + + + +# Check whether --with-clang was given. +if test "${with_clang+set}" = set; then : + withval=$with_clang; +fi + +case "system" in +system|no) + +# Check whether --with-clang_prefix was given. +if test "${with_clang_prefix+set}" = set; then : + withval=$with_clang_prefix; +fi + + +# Check whether --with-clang_exec_prefix was given. +if test "${with_clang_exec_prefix+set}" = set; then : + withval=$with_clang_exec_prefix; +fi + +esac + +if test "x$with_clang_prefix" != "x" -a "x$with_clang_exec_prefix" = "x"; then + with_clang_exec_prefix=$with_clang_prefix +fi +if test "x$with_clang_prefix" != "x" -o "x$with_clang_exec_prefix" != "x"; then + if test "x$with_clang" != "x" -a "x$with_clang" != "xyes" -a "x$with_clang" != "xsystem"; then + as_fn_error $? "Setting $with_clang_prefix implies use of system clang" "$LINENO" 5 + fi + with_clang="system" +fi +if test "x$with_clang_builddir" != "x"; then + if test "x$with_clang" != "x" -a "x$with_clang" != "xyes" -a "x$with_clang" != "xbuild"; then + as_fn_error $? "Setting $with_clang_builddir implies use of build clang" "$LINENO" 5 + fi + with_clang="build" + clang_srcdir=`echo @abs_srcdir@ | $with_clang_builddir/config.status --file=-` + { $as_echo "$as_me:${as_lineno-$LINENO}: clang sources in $clang_srcdir" >&5 +$as_echo "$as_me: clang sources in $clang_srcdir" >&6;} +fi +if test "x$with_clang_exec_prefix" != "x"; then + export PKG_CONFIG_PATH="$with_clang_exec_prefix/lib/pkgconfig${PKG_CONFIG_PATH+:$PKG_CONFIG_PATH}" +fi +case "$with_clang" in +system|no) + ;; +*) + case "no" in + bundled) + if test -d $srcdir/.git -a \ + -d $srcdir/clang -a \ + ! -d $srcdir/clang/.git; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: git repo detected, but submodule clang not initialized" >&5 +$as_echo "$as_me: WARNING: git repo detected, but submodule clang not initialized" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You may want to run" >&5 +$as_echo "$as_me: WARNING: You may want to run" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: git submodule init" >&5 +$as_echo "$as_me: WARNING: git submodule init" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: git submodule update" >&5 +$as_echo "$as_me: WARNING: git submodule update" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: sh autogen.sh" >&5 +$as_echo "$as_me: WARNING: sh autogen.sh" >&2;} + fi + if test -f $srcdir/clang/configure; then + with_clang="bundled" + else + with_clang="no" + fi + ;; + *) + with_clang="no" + ;; + esac + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which clang to use" >&5 +$as_echo_n "checking which clang to use... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_clang" >&5 +$as_echo "$with_clang" >&6; } + + +case "$with_clang" in +system) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + + llvm_config="llvm-config" + # Extract the first word of ""$llvm_config"", so it can be a program name with args. +set dummy "$llvm_config"; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_llvm_config_found+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$llvm_config_found"; then + ac_cv_prog_llvm_config_found="$llvm_config_found" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_llvm_config_found="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +llvm_config_found=$ac_cv_prog_llvm_config_found +if test -n "$llvm_config_found"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $llvm_config_found" >&5 +$as_echo "$llvm_config_found" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "x$with_clang_prefix" != "x"; then + llvm_config="$with_clang_prefix/bin/llvm-config" + if test -x "$llvm_config"; then + llvm_config_found=yes + fi + fi + if test "$llvm_config_found" != yes; then + as_fn_error $? "llvm-config not found" "$LINENO" 5 + fi + CLANG_CXXFLAGS=`$llvm_config --cxxflags | \ + $SED -e 's/-Wcovered-switch-default//'` + CLANG_LDFLAGS=`$llvm_config --ldflags` + targets=`$llvm_config --targets-built` + components="$targets asmparser bitreader support mc" + $llvm_config --components | $GREP option > /dev/null 2> /dev/null + if test $? -eq 0; then + components="$components option" + fi + CLANG_LIBS=`$llvm_config --libs $components` + systemlibs=`$llvm_config --system-libs 2> /dev/null | tail -1` + if test $? -eq 0; then + CLANG_LIBS="$CLANG_LIBS $systemlibs" + fi + CLANG_PREFIX=`$llvm_config --prefix` + +cat >>confdefs.h <<_ACEOF +#define CLANG_PREFIX "$CLANG_PREFIX" +_ACEOF + + + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CLANG_CXXFLAGS $CPPFLAGS" + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +ac_fn_cxx_check_header_mongrel "$LINENO" "clang/Basic/SourceLocation.h" "ac_cv_header_clang_Basic_SourceLocation_h" "$ac_includes_default" +if test "x$ac_cv_header_clang_Basic_SourceLocation_h" = xyes; then : + +else + as_fn_error $? "clang header file not found" "$LINENO" 5 +fi + + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "getDefaultTargetTriple" >/dev/null 2>&1; then : + +else + +$as_echo "#define getDefaultTargetTriple getHostTriple" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "getExpansionLineNumber" >/dev/null 2>&1; then : + +else + +$as_echo "#define getExpansionLineNumber getInstantiationLineNumber" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "DiagnosticsEngine" >/dev/null 2>&1; then : + +else + +$as_echo "#define DiagnosticsEngine Diagnostic" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "ArrayRef" >/dev/null 2>&1; then : + +$as_echo "#define USE_ARRAYREF /**/" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "CXXIsProduction" >/dev/null 2>&1; then : + +$as_echo "#define HAVE_CXXISPRODUCTION /**/" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP " IsProduction" >/dev/null 2>&1; then : + +$as_echo "#define HAVE_ISPRODUCTION /**/" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + using namespace clang; + DiagnosticsEngine *Diags; + new driver::Driver("", "", "", *Diags); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define DRIVER_CTOR_TAKES_DEFAULTIMAGENAME /**/" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "void HandleTopLevelDecl\(" >/dev/null 2>&1; then : + +$as_echo "#define HandleTopLevelDeclReturn void" >>confdefs.h + + +$as_echo "#define HandleTopLevelDeclContinue /**/" >>confdefs.h + +else + +$as_echo "#define HandleTopLevelDeclReturn bool" >>confdefs.h + + +$as_echo "#define HandleTopLevelDeclContinue true" >>confdefs.h + +fi +rm -f conftest* + + ac_fn_cxx_check_header_mongrel "$LINENO" "clang/Basic/DiagnosticOptions.h" "ac_cv_header_clang_Basic_DiagnosticOptions_h" "$ac_includes_default" +if test "x$ac_cv_header_clang_Basic_DiagnosticOptions_h" = xyes; then : + +$as_echo "#define HAVE_BASIC_DIAGNOSTICOPTIONS_H /**/" >>confdefs.h + +fi + + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + using namespace clang; + std::shared_ptr TO; + DiagnosticsEngine *Diags; + TargetInfo::CreateTargetInfo(*Diags, TO); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define CREATETARGETINFO_TAKES_SHARED_PTR /**/" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + using namespace clang; + TargetOptions *TO; + DiagnosticsEngine *Diags; + TargetInfo::CreateTargetInfo(*Diags, TO); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define CREATETARGETINFO_TAKES_POINTER /**/" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + using namespace clang; + DiagnosticConsumer *client; + CompilerInstance *Clang; + Clang->createDiagnostics(client); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + +$as_echo "#define CREATEDIAGNOSTICS_TAKES_ARG /**/" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + using namespace clang; + HeaderSearchOptions HSO; + HSO.AddPath("", frontend::Angled, false, false); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define ADDPATH_TAKES_4_ARGUMENTS /**/" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "getNumParams" >/dev/null 2>&1; then : + +$as_echo "#define getNumArgs getNumParams" >>confdefs.h + + +$as_echo "#define getArgType getParamType" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "getReturnType" >/dev/null 2>&1; then : + +else + +$as_echo "#define getReturnType getResultType" >>confdefs.h + +fi +rm -f conftest* + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + using namespace clang; + CompilerInstance *Clang; + Clang->createPreprocessor(TU_Complete); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define CREATEPREPROCESSOR_TAKES_TUKIND /**/" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "setMainFileID" >/dev/null 2>&1; then : + +$as_echo "#define HAVE_SETMAINFILEID /**/" >>confdefs.h + +fi +rm -f conftest* + + ac_fn_cxx_check_header_mongrel "$LINENO" "llvm/ADT/OwningPtr.h" "ac_cv_header_llvm_ADT_OwningPtr_h" "$ac_includes_default" +if test "x$ac_cv_header_llvm_ADT_OwningPtr_h" = xyes; then : + +$as_echo "#define HAVE_ADT_OWNINGPTR_H /**/" >>confdefs.h + +fi + + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "initializeBuiltins" >/dev/null 2>&1; then : + +else + +$as_echo "#define initializeBuiltins InitializeBuiltins" >>confdefs.h + +fi +rm -f conftest* + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + CPPFLAGS="$SAVE_CPPFLAGS" + + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$CLANG_LDFLAGS $LDFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lclangEdit" >&5 +$as_echo_n "checking for main in -lclangEdit... " >&6; } +if ${ac_cv_lib_clangEdit_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lclangEdit $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_clangEdit_main=yes +else + ac_cv_lib_clangEdit_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_clangEdit_main" >&5 +$as_echo "$ac_cv_lib_clangEdit_main" >&6; } +if test "x$ac_cv_lib_clangEdit_main" = xyes; then : + LIB_CLANG_EDIT=-lclangEdit +fi + + LDFLAGS="$SAVE_LDFLAGS" + ;; +esac + if test $with_clang = system; then + HAVE_CLANG_TRUE= + HAVE_CLANG_FALSE='#' +else + HAVE_CLANG_TRUE='#' + HAVE_CLANG_FALSE= +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler vendor" >&5 +$as_echo_n "checking for C compiler vendor... " >&6; } +if ${ax_cv_c_compiler_vendor+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_c_compiler_vendor=unknown + # note: don't check for gcc first since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale:__PATHCC__,__PATHSCALE__ clang:__clang__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#if !($vencpp) + thisisanerror; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_c_compiler_vendor=`echo $ventest | cut -d: -f1`; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_c_compiler_vendor" >&5 +$as_echo "$ax_cv_c_compiler_vendor" >&6; } + + + WARNING_FLAGS="" + + if test "${ax_cv_c_compiler_vendor}" = "clang"; then + WARNING_FLAGS="-Wall" + fi + + + + +PACKAGE_CFLAGS="$MP_CPPFLAGS" +PACKAGE_LDFLAGS="$MP_LDFLAGS" +PACKAGE_LIBS="-lisl $MP_LIBS" + +# we need the expanded forms... +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig libname" >&5 +$as_echo_n "checking our pkgconfig libname... " >&6; } +test ".$ax_create_pkgconfig_libname" != "." || \ +ax_create_pkgconfig_libname="${PACKAGE_NAME}" +test ".$ax_create_pkgconfig_libname" != "." || \ +ax_create_pkgconfig_libname="$PACKAGE" +ax_create_pkgconfig_libname=`eval echo "$ax_create_pkgconfig_libname"` +ax_create_pkgconfig_libname=`eval echo "$ax_create_pkgconfig_libname"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_libname" >&5 +$as_echo "$ax_create_pkgconfig_libname" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig version" >&5 +$as_echo_n "checking our pkgconfig version... " >&6; } +test ".$ax_create_pkgconfig_version" != "." || \ +ax_create_pkgconfig_version="${PACKAGE_VERSION}" +test ".$ax_create_pkgconfig_version" != "." || \ +ax_create_pkgconfig_version="$VERSION" +ax_create_pkgconfig_version=`eval echo "$ax_create_pkgconfig_version"` +ax_create_pkgconfig_version=`eval echo "$ax_create_pkgconfig_version"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_version" >&5 +$as_echo "$ax_create_pkgconfig_version" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig_libdir" >&5 +$as_echo_n "checking our pkgconfig_libdir... " >&6; } +test ".$pkgconfig_libdir" = "." && \ +pkgconfig_libdir='${libdir}/pkgconfig' +ax_create_pkgconfig_libdir=`eval echo "$pkgconfig_libdir"` +ax_create_pkgconfig_libdir=`eval echo "$ax_create_pkgconfig_libdir"` +ax_create_pkgconfig_libdir=`eval echo "$ax_create_pkgconfig_libdir"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pkgconfig_libdir" >&5 +$as_echo "$pkgconfig_libdir" >&6; } +test "$pkgconfig_libdir" != "$ax_create_pkgconfig_libdir" && ( +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: expanded our pkgconfig_libdir... $ax_create_pkgconfig_libdir" >&5 +$as_echo "expanded our pkgconfig_libdir... $ax_create_pkgconfig_libdir" >&6; }) + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig_libfile" >&5 +$as_echo_n "checking our pkgconfig_libfile... " >&6; } +test ".$pkgconfig_libfile" != "." || \ +pkgconfig_libfile="$ax_create_pkgconfig_libname.pc" +ax_create_pkgconfig_libfile=`eval echo "$pkgconfig_libfile"` +ax_create_pkgconfig_libfile=`eval echo "$ax_create_pkgconfig_libfile"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pkgconfig_libfile" >&5 +$as_echo "$pkgconfig_libfile" >&6; } +test "$pkgconfig_libfile" != "$ax_create_pkgconfig_libfile" && ( +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: expanded our pkgconfig_libfile... $ax_create_pkgconfig_libfile" >&5 +$as_echo "expanded our pkgconfig_libfile... $ax_create_pkgconfig_libfile" >&6; }) + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our package / suffix" >&5 +$as_echo_n "checking our package / suffix... " >&6; } +ax_create_pkgconfig_suffix="$program_suffix" +test ".$ax_create_pkgconfig_suffix" != .NONE || ax_create_pkgconfig_suffix="" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${PACKAGE_NAME} / ${ax_create_pkgconfig_suffix}" >&5 +$as_echo "${PACKAGE_NAME} / ${ax_create_pkgconfig_suffix}" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig description" >&5 +$as_echo_n "checking our pkgconfig description... " >&6; } +ax_create_pkgconfig_description="$PACKAGE_SUMMARY" +test ".$ax_create_pkgconfig_description" != "." || \ +ax_create_pkgconfig_description="$ax_create_pkgconfig_libname Library" +ax_create_pkgconfig_description=`eval echo "$ax_create_pkgconfig_description"` +ax_create_pkgconfig_description=`eval echo "$ax_create_pkgconfig_description"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_description" >&5 +$as_echo "$ax_create_pkgconfig_description" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig requires" >&5 +$as_echo_n "checking our pkgconfig requires... " >&6; } +ax_create_pkgconfig_requires="$PACKAGE_REQUIRES" +ax_create_pkgconfig_requires=`eval echo "$ax_create_pkgconfig_requires"` +ax_create_pkgconfig_requires=`eval echo "$ax_create_pkgconfig_requires"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_requires" >&5 +$as_echo "$ax_create_pkgconfig_requires" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig ext libs" >&5 +$as_echo_n "checking our pkgconfig ext libs... " >&6; } +ax_create_pkgconfig_pkglibs="$PACKAGE_LIBS" +test ".$ax_create_pkgconfig_pkglibs" != "." || ax_create_pkgconfig_pkglibs="-l$ax_create_pkgconfig_libname" +ax_create_pkgconfig_libs="$ax_create_pkgconfig_pkglibs $LIBS" +ax_create_pkgconfig_libs=`eval echo "$ax_create_pkgconfig_libs"` +ax_create_pkgconfig_libs=`eval echo "$ax_create_pkgconfig_libs"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_libs" >&5 +$as_echo "$ax_create_pkgconfig_libs" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig cppflags" >&5 +$as_echo_n "checking our pkgconfig cppflags... " >&6; } +ax_create_pkgconfig_cppflags="$PACKAGE_CFLAGS" +ax_create_pkgconfig_cppflags=`eval echo "$ax_create_pkgconfig_cppflags"` +ax_create_pkgconfig_cppflags=`eval echo "$ax_create_pkgconfig_cppflags"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_cppflags" >&5 +$as_echo "$ax_create_pkgconfig_cppflags" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking our pkgconfig ldflags" >&5 +$as_echo_n "checking our pkgconfig ldflags... " >&6; } +ax_create_pkgconfig_ldflags="$PACKAGE_LDFLAGS" +ax_create_pkgconfig_ldflags=`eval echo "$ax_create_pkgconfig_ldflags"` +ax_create_pkgconfig_ldflags=`eval echo "$ax_create_pkgconfig_ldflags"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_create_pkgconfig_ldflags" >&5 +$as_echo "$ax_create_pkgconfig_ldflags" >&6; } + +test ".$ax_create_pkgconfig_generate" != "." || \ +ax_create_pkgconfig_generate="$ax_create_pkgconfig_libname.pc" +ax_create_pkgconfig_generate=`eval echo "$ax_create_pkgconfig_generate"` +ax_create_pkgconfig_generate=`eval echo "$ax_create_pkgconfig_generate"` +test "$pkgconfig_libfile" != "$ax_create_pkgconfig_generate" && ( +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: generate the pkgconfig later... $ax_create_pkgconfig_generate" >&5 +$as_echo "generate the pkgconfig later... $ax_create_pkgconfig_generate" >&6; }) + +if test ".$ax_create_pkgconfig_src_libdir" = "." ; then +ax_create_pkgconfig_src_libdir=`pwd` +ax_create_pkgconfig_src_libdir=`$as_dirname -- "$ax_create_pkgconfig_src_libdir/$ax_create_pkgconfig_generate" || +$as_expr X"$ax_create_pkgconfig_src_libdir/$ax_create_pkgconfig_generate" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ax_create_pkgconfig_src_libdir/$ax_create_pkgconfig_generate" : 'X\(//\)[^/]' \| \ + X"$ax_create_pkgconfig_src_libdir/$ax_create_pkgconfig_generate" : 'X\(//\)$' \| \ + X"$ax_create_pkgconfig_src_libdir/$ax_create_pkgconfig_generate" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ax_create_pkgconfig_src_libdir/$ax_create_pkgconfig_generate" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` +test ! -d $ax_create_pkgconfig_src_libdir/src || \ +ax_create_pkgconfig_src_libdir="$ax_create_pkgconfig_src_libdir/src" +case ".$objdir" in +*libs) ax_create_pkgconfig_src_libdir="$ax_create_pkgconfig_src_libdir/$objdir" ;; esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: noninstalled pkgconfig -L $ax_create_pkgconfig_src_libdir" >&5 +$as_echo "noninstalled pkgconfig -L $ax_create_pkgconfig_src_libdir" >&6; } +fi + +if test ".$ax_create_pkgconfig_src_headers" = "." ; then +ax_create_pkgconfig_src_headers=`pwd` +v="$ac_top_srcdir" ; +test ".$v" != "." || v="$ax_spec_dir" +test ".$v" != "." || v="$srcdir" +case "$v" in /*) ax_create_pkgconfig_src_headers="" ;; esac +ax_create_pkgconfig_src_headers=`$as_dirname -- "$ax_create_pkgconfig_src_headers/$v/x" || +$as_expr X"$ax_create_pkgconfig_src_headers/$v/x" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ax_create_pkgconfig_src_headers/$v/x" : 'X\(//\)[^/]' \| \ + X"$ax_create_pkgconfig_src_headers/$v/x" : 'X\(//\)$' \| \ + X"$ax_create_pkgconfig_src_headers/$v/x" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ax_create_pkgconfig_src_headers/$v/x" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` +test ! -d $ax_create_pkgconfig_src_headers/include || \ +ax_create_pkgconfig_src_headers="$ax_create_pkgconfig_src_headers/include" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: noninstalled pkgconfig -I $ax_create_pkgconfig_src_headers" >&5 +$as_echo "noninstalled pkgconfig -I $ax_create_pkgconfig_src_headers" >&6; } +fi + + +ac_config_commands="$ac_config_commands $ax_create_pkgconfig_generate" + + + + + + + if test -f $srcdir/.git; then + gitdir=`GIT_DIR=$srcdir/.git git rev-parse --git-dir` + GIT_HEAD="$gitdir/index" + GIT_REPO="$gitdir" + GIT_HEAD_ID=`GIT_DIR=$GIT_REPO git describe --always` + elif test -f $srcdir/.git/HEAD; then + GIT_HEAD="$srcdir/.git/index" + GIT_REPO="$srcdir/.git" + GIT_HEAD_ID=`GIT_DIR=$GIT_REPO git describe --always` + elif test -f $srcdir/GIT_HEAD_ID; then + GIT_HEAD_ID=`cat $srcdir/GIT_HEAD_ID` + else + mysrcdir=`(cd $srcdir; pwd)` + head=`basename $mysrcdir | sed -e 's/.*-//'` + head2=`echo $head | sed -e 's/^0-9a-f//'` + head3=`echo $head2 | sed -e 's/........................................//'` + if test "x$head3" = "x" -a "x$head" = "x$head2"; then + GIT_HEAD_ID="$head" + else + GIT_HEAD_ID="UNKNOWN" + fi + fi + if test -z "$GIT_REPO" ; then + GIT_HEAD_VERSION="$GIT_HEAD_ID" + else + GIT_HEAD_VERSION="\`GIT_DIR=$GIT_REPO git describe --always\`" + fi + + + +ac_config_headers="$ac_config_headers isl_config.h" + +ac_config_files="$ac_config_files Makefile" + +ac_config_files="$ac_config_files doc/Makefile" + +if test $with_clang = system; then + ac_config_files="$ac_config_files interface/Makefile" + +fi +ac_config_files="$ac_config_files bound_test.sh" + +ac_config_files="$ac_config_files codegen_test.sh" + +ac_config_files="$ac_config_files pip_test.sh" + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GENERATE_DOC_TRUE}" && test -z "${GENERATE_DOC_FALSE}"; then + as_fn_error $? "conditional \"GENERATE_DOC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${NEED_GET_MEMORY_FUNCTIONS_TRUE}" && test -z "${NEED_GET_MEMORY_FUNCTIONS_FALSE}"; then + as_fn_error $? "conditional \"NEED_GET_MEMORY_FUNCTIONS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${NEED_GET_MEMORY_FUNCTIONS_TRUE}" && test -z "${NEED_GET_MEMORY_FUNCTIONS_FALSE}"; then + as_fn_error $? "conditional \"NEED_GET_MEMORY_FUNCTIONS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${IMATH_FOR_MP_TRUE}" && test -z "${IMATH_FOR_MP_FALSE}"; then + as_fn_error $? "conditional \"IMATH_FOR_MP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GMP_FOR_MP_TRUE}" && test -z "${GMP_FOR_MP_FALSE}"; then + as_fn_error $? "conditional \"GMP_FOR_MP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${SMALL_INT_OPT_TRUE}" && test -z "${SMALL_INT_OPT_FALSE}"; then + as_fn_error $? "conditional \"SMALL_INT_OPT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_CLANG_TRUE}" && test -z "${HAVE_CLANG_FALSE}"; then + as_fn_error $? "conditional \"HAVE_CLANG\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by isl $as_me 0.17.1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +isl config.status 0.17.1 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +nm_file_list_spec \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + + +# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=`$as_echo "_$PACKAGE-$ac_stdint_h" | $as_tr_cpp` +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" + + +ax_create_pkgconfig_generate="$ax_create_pkgconfig_generate" +pkgconfig_prefix='$prefix' +pkgconfig_execprefix='$exec_prefix' +pkgconfig_bindir='$bindir' +pkgconfig_libdir='$libdir' +pkgconfig_includedir='$includedir' +pkgconfig_datarootdir='$datarootdir' +pkgconfig_datadir='$datadir' +pkgconfig_sysconfdir='$sysconfdir' +pkgconfig_suffix='$ax_create_pkgconfig_suffix' +pkgconfig_package='$PACKAGE_NAME' +pkgconfig_libname='$ax_create_pkgconfig_libname' +pkgconfig_description='$ax_create_pkgconfig_description' +pkgconfig_version='$ax_create_pkgconfig_version' +pkgconfig_requires='$ax_create_pkgconfig_requires' +pkgconfig_libs='$ax_create_pkgconfig_libs' +pkgconfig_ldflags='$ax_create_pkgconfig_ldflags' +pkgconfig_cppflags='$ax_create_pkgconfig_cppflags' +pkgconfig_src_libdir='$ax_create_pkgconfig_src_libdir' +pkgconfig_src_headers='$ax_create_pkgconfig_src_headers' + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "$ac_stdint_h") CONFIG_COMMANDS="$CONFIG_COMMANDS $ac_stdint_h" ;; + "$ax_create_pkgconfig_generate") CONFIG_COMMANDS="$CONFIG_COMMANDS $ax_create_pkgconfig_generate" ;; + "isl_config.h") CONFIG_HEADERS="$CONFIG_HEADERS isl_config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "interface/Makefile") CONFIG_FILES="$CONFIG_FILES interface/Makefile" ;; + "bound_test.sh") CONFIG_FILES="$CONFIG_FILES bound_test.sh" ;; + "codegen_test.sh") CONFIG_FILES="$CONFIG_FILES codegen_test.sh" ;; + "pip_test.sh") CONFIG_FILES="$CONFIG_FILES pip_test.sh" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + "$ac_stdint_h":C) +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_stdint_h : $_ac_stdint_h" >&5 +$as_echo "$as_me: creating $ac_stdint_h : $_ac_stdint_h" >&6;} +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +#ifndef INT64_MIN +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +#endif +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +#ifndef INT64_MAX +# define INT64_MAX (__INT64_C(9223372036854775807)) +#endif + +/* Maximum of unsigned integral types. */ +#ifndef UINT8_MAX +# define UINT8_MAX (255) +#endif +#ifndef UINT16_MAX +# define UINT16_MAX (65535) +#endif +# define UINT32_MAX (4294967295U) +#ifndef UINT64_MAX +# define UINT64_MAX (__UINT64_C(18446744073709551615)) +#endif + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_stdint_h is unchanged" >&5 +$as_echo "$as_me: $ac_stdint_h is unchanged" >&6;} + else + ac_dir=`$as_dirname -- "$ac_stdint_h" || +$as_expr X"$ac_stdint_h" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_stdint_h" : 'X\(//\)[^/]' \| \ + X"$ac_stdint_h" : 'X\(//\)$' \| \ + X"$ac_stdint_h" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_stdint_h" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi + ;; + "$ax_create_pkgconfig_generate":C) +pkgconfig_generate="$ax_create_pkgconfig_generate" +if test ! -f "$pkgconfig_generate.in" +then generate="true" +elif grep ' generated by configure ' $pkgconfig_generate.in >/dev/null +then generate="true" +else generate="false"; +fi +if $generate ; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $pkgconfig_generate.in" >&5 +$as_echo "$as_me: creating $pkgconfig_generate.in" >&6;} +cat > $pkgconfig_generate.in <&5 +$as_echo "$as_me: creating $pkgconfig_generate" >&6;} +cat >conftest.sed < $pkgconfig_generate +if test ! -s $pkgconfig_generate ; then + as_fn_error $? "$pkgconfig_generate is empty" "$LINENO" 5 +fi ; rm conftest.sed # DONE generate $pkgconfig_generate +pkgconfig_uninstalled=`echo $pkgconfig_generate |sed 's/.pc$/-uninstalled.pc/'` +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $pkgconfig_uninstalled" >&5 +$as_echo "$as_me: creating $pkgconfig_uninstalled" >&6;} +cat >conftest.sed < $pkgconfig_uninstalled +if test ! -s $pkgconfig_uninstalled ; then + as_fn_error $? "$pkgconfig_uninstalled is empty" "$LINENO" 5 +fi ; rm conftest.sed # DONE generate $pkgconfig_uninstalled + pkgconfig_requires_add=`echo ${pkgconfig_requires}` +if test ".$pkgconfig_requires_add" != "." ; then + pkgconfig_requires_add="pkg-config $pkgconfig_requires_add" + else pkgconfig_requires_add=":" ; fi +pkgconfig_uninstalled=`echo $pkgconfig_generate |sed 's/.pc$/-uninstalled.sh/'` +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $pkgconfig_uninstalled" >&5 +$as_echo "$as_me: creating $pkgconfig_uninstalled" >&6;} +cat >conftest.sed <Name:>for option\\; do case \"\$option\" in --list-all|--name) echo > +s>Description: *>\\;\\; --help) pkg-config --help \\; echo Buildscript Of > +s>Version: *>\\;\\; --modversion|--version) echo > +s>Requires:>\\;\\; --requires) echo $pkgconfig_requires_add> +s>Libs: *>\\;\\; --libs) echo > +s>Cflags: *>\\;\\; --cflags) echo > +/--libs)/a\\ + $pkgconfig_requires_add +/--cflags)/a\\ + $pkgconfig_requires_add\\ +;; --variable=*) eval echo '\$'\`echo \$option | sed -e 's/.*=//'\`\\ +;; --uninstalled) exit 0 \\ +;; *) ;; esac done +AXEOF +sed -f conftest.sed $pkgconfig_generate.in > $pkgconfig_uninstalled +if test ! -s $pkgconfig_uninstalled ; then + as_fn_error $? "$pkgconfig_uninstalled is empty" "$LINENO" 5 +fi ; rm conftest.sed # DONE generate $pkgconfig_uninstalled + ;; + "bound_test.sh":F) chmod +x bound_test.sh ;; + "codegen_test.sh":F) chmod +x codegen_test.sh ;; + "pip_test.sh":F) chmod +x pip_test.sh ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + + ac_configure_args="$ac_configure_args $isl_configure_args" + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + Index: lib/Analysis/isl/configure.ac =================================================================== --- /dev/null +++ lib/Analysis/isl/configure.ac @@ -0,0 +1,272 @@ +AC_INIT([isl], [0.17.1], [isl-development@googlegroups.com]) +AC_CONFIG_AUX_DIR([.]) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([foreign]) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) +AC_SUBST(versioninfo) +versioninfo=17:1:2 + +if test "x$prefix" != "xNONE"; then + prefix_wd=`cd $prefix && pwd` + srcdir_wd=`cd $srcdir && pwd` + wd=`pwd` + if test "x$prefix_wd" = "x$srcdir_wd"; then + AC_MSG_ERROR(Installation in source directory not supported) + fi + if test "x$prefix_wd" = "x$wd"; then + AC_MSG_ERROR(Installation in build directory not supported) + fi +fi + +AC_PROG_CC +AC_PROG_CXX + +AX_CC_MAXOPT +AX_GCC_WARN_UNUSED_RESULT +AX_C___ATTRIBUTE__ + +AC_PROG_LIBTOOL + +AC_CHECK_PROG(PERL, perl, perl, []) +AC_CHECK_PROG(PDFLATEX, pdflatex, pdflatex, []) +AC_CHECK_PROG(POD2HTML, pod2html, pod2html, []) + +AM_CONDITIONAL(GENERATE_DOC, test -n "$PERL" -a -n "$PDFLATEX" -a -n "$POD2HTML") + +AX_CREATE_STDINT_H(include/isl/stdint.h) + +AC_ARG_WITH([int], + [AS_HELP_STRING([--with-int=gmp|imath|imath-32], + [Which package to use to represent + multi-precision integers [default=gmp]])], + [], [with_int=gmp]) +case "$with_int" in +gmp|imath|imath-32) + ;; +*) + AC_MSG_ERROR( + [bad value ${withval} for --with-int (use gmp, imath or imath-32)]) +esac + +AC_SUBST(MP_CPPFLAGS) +AC_SUBST(MP_LDFLAGS) +AC_SUBST(MP_LIBS) +case "$with_int" in +gmp) + AX_DETECT_GMP + ;; +imath|imath-32) + AX_DETECT_IMATH + ;; +esac +if test "x$with_int" = "ximath-32" -a "x$GCC" = "xyes"; then + MP_CPPFLAGS="-std=gnu99 $MP_CPPFLAGS" +fi + +AM_CONDITIONAL(IMATH_FOR_MP, test x$with_int = ximath -o x$with_int = ximath-32) +AM_CONDITIONAL(GMP_FOR_MP, test x$with_int = xgmp) + +AM_CONDITIONAL(SMALL_INT_OPT, test "x$with_int" == "ximath-32") +AS_IF([test "x$with_int" == "ximath-32"], [ + AC_DEFINE([USE_SMALL_INT_OPT], [], [Use small integer optimization]) +]) + +AC_CHECK_DECLS(ffs,[],[],[#include ]) +AC_CHECK_DECLS(__builtin_ffs,[],[],[]) +AC_CHECK_DECLS([_BitScanForward],[],[],[#include ]) +if test "x$ac_cv_have_decl_ffs" = xno -a \ + "x$ac_cv_have_decl___builtin_ffs" = xno -a \ + "x$ac_cv_have_decl__BitScanForward" = xno; then + AC_MSG_ERROR([No ffs implementation found]) +fi +AC_CHECK_DECLS([strcasecmp,strncasecmp],[],[],[#include ]) +AC_CHECK_DECLS([_stricmp,_strnicmp],[],[],[#include ]) +if test "x$ac_cv_have_decl_strcasecmp" = xno -a \ + "x$ac_cv_have_decl__stricmp" = xno; then + AC_MSG_ERROR([No strcasecmp implementation found]) +fi +if test "x$ac_cv_have_decl_strncasecmp" = xno -a \ + "x$ac_cv_have_decl__strnicmp" = xno; then + AC_MSG_ERROR([No strncasecmp implementation found]) +fi +AC_CHECK_DECLS([snprintf,_snprintf],[],[],[#include ]) +if test "x$ac_cv_have_decl_snprintf" = xno -a \ + "x$ac_cv_have_decl__snprintf" = xno; then + AC_MSG_ERROR([No snprintf implementation found]) +fi + +AC_SUBST(CLANG_CXXFLAGS) +AC_SUBST(CLANG_LDFLAGS) +AC_SUBST(CLANG_LIBS) +AX_SUBMODULE(clang,system|no,no) +case "$with_clang" in +system) + AC_PROG_GREP + AC_PROG_SED + llvm_config="llvm-config" + AC_CHECK_PROG([llvm_config_found], ["$llvm_config"], [yes]) + if test "x$with_clang_prefix" != "x"; then + llvm_config="$with_clang_prefix/bin/llvm-config" + if test -x "$llvm_config"; then + llvm_config_found=yes + fi + fi + if test "$llvm_config_found" != yes; then + AC_MSG_ERROR([llvm-config not found]) + fi + CLANG_CXXFLAGS=`$llvm_config --cxxflags | \ + $SED -e 's/-Wcovered-switch-default//'` + CLANG_LDFLAGS=`$llvm_config --ldflags` + targets=`$llvm_config --targets-built` + components="$targets asmparser bitreader support mc" + $llvm_config --components | $GREP option > /dev/null 2> /dev/null + if test $? -eq 0; then + components="$components option" + fi + CLANG_LIBS=`$llvm_config --libs $components` + systemlibs=`$llvm_config --system-libs 2> /dev/null | tail -1` + if test $? -eq 0; then + CLANG_LIBS="$CLANG_LIBS $systemlibs" + fi + CLANG_PREFIX=`$llvm_config --prefix` + AC_DEFINE_UNQUOTED(CLANG_PREFIX, ["$CLANG_PREFIX"], + [Clang installation prefix]) + + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CLANG_CXXFLAGS $CPPFLAGS" + AC_LANG_PUSH(C++) + AC_CHECK_HEADER([clang/Basic/SourceLocation.h], [], + [AC_ERROR([clang header file not found])]) + AC_EGREP_HEADER([getDefaultTargetTriple], [llvm/Support/Host.h], [], + [AC_DEFINE([getDefaultTargetTriple], [getHostTriple], + [Define to getHostTriple for older versions of clang])]) + AC_EGREP_HEADER([getExpansionLineNumber], + [clang/Basic/SourceLocation.h], [], + [AC_DEFINE([getExpansionLineNumber], + [getInstantiationLineNumber], + [Define to getInstantiationLineNumber for older versions of clang])]) + AC_EGREP_HEADER([DiagnosticsEngine], + [clang/Basic/Diagnostic.h], [], + [AC_DEFINE([DiagnosticsEngine], + [Diagnostic], + [Define to Diagnostic for older versions of clang])]) + AC_EGREP_HEADER([ArrayRef], [clang/Driver/Driver.h], + [AC_DEFINE([USE_ARRAYREF], [], + [Define if Driver::BuildCompilation takes ArrayRef])]) + AC_EGREP_HEADER([CXXIsProduction], [clang/Driver/Driver.h], + [AC_DEFINE([HAVE_CXXISPRODUCTION], [], + [Define if Driver constructor takes CXXIsProduction argument])]) + AC_EGREP_HEADER([ IsProduction], [clang/Driver/Driver.h], + [AC_DEFINE([HAVE_ISPRODUCTION], [], + [Define if Driver constructor takes IsProduction argument])]) + AC_TRY_COMPILE([#include ], [ + using namespace clang; + DiagnosticsEngine *Diags; + new driver::Driver("", "", "", *Diags); + ], [AC_DEFINE([DRIVER_CTOR_TAKES_DEFAULTIMAGENAME], [], + [Define if Driver constructor takes default image name])]) + AC_EGREP_HEADER([void HandleTopLevelDecl\(], [clang/AST/ASTConsumer.h], + [AC_DEFINE([HandleTopLevelDeclReturn], [void], + [Return type of HandleTopLevelDeclReturn]) + AC_DEFINE([HandleTopLevelDeclContinue], [], + [Return type of HandleTopLevelDeclReturn])], + [AC_DEFINE([HandleTopLevelDeclReturn], [bool], + [Return type of HandleTopLevelDeclReturn]) + AC_DEFINE([HandleTopLevelDeclContinue], [true], + [Return type of HandleTopLevelDeclReturn])]) + AC_CHECK_HEADER([clang/Basic/DiagnosticOptions.h], + [AC_DEFINE([HAVE_BASIC_DIAGNOSTICOPTIONS_H], [], + [Define if clang/Basic/DiagnosticOptions.h exists])]) + AC_TRY_COMPILE([#include ], [ + using namespace clang; + std::shared_ptr TO; + DiagnosticsEngine *Diags; + TargetInfo::CreateTargetInfo(*Diags, TO); + ], [AC_DEFINE([CREATETARGETINFO_TAKES_SHARED_PTR], [], + [Define if TargetInfo::CreateTargetInfo takes shared_ptr])]) + AC_TRY_COMPILE([#include ], [ + using namespace clang; + TargetOptions *TO; + DiagnosticsEngine *Diags; + TargetInfo::CreateTargetInfo(*Diags, TO); + ], [AC_DEFINE([CREATETARGETINFO_TAKES_POINTER], [], + [Define if TargetInfo::CreateTargetInfo takes pointer])]) + AC_TRY_COMPILE([#include ], [ + using namespace clang; + DiagnosticConsumer *client; + CompilerInstance *Clang; + Clang->createDiagnostics(client); + ], [], [AC_DEFINE([CREATEDIAGNOSTICS_TAKES_ARG], [], + [Define if CompilerInstance::createDiagnostics takes argc and argv])]) + AC_TRY_COMPILE([#include ], [ + using namespace clang; + HeaderSearchOptions HSO; + HSO.AddPath("", frontend::Angled, false, false); + ], [AC_DEFINE([ADDPATH_TAKES_4_ARGUMENTS], [], + [Define if HeaderSearchOptions::AddPath takes 4 arguments])]) + AC_EGREP_HEADER([getNumParams], + [clang/AST/CanonicalType.h], + [AC_DEFINE([getNumArgs], [getNumParams], + [Define to getNumParams for newer versions of clang]) + AC_DEFINE([getArgType], [getParamType], + [Define to getParamType for newer versions of clang])]) + AC_EGREP_HEADER([getReturnType], + [clang/AST/CanonicalType.h], [], + [AC_DEFINE([getReturnType], [getResultType], + [Define to getResultType for older versions of clang])]) + AC_TRY_COMPILE([#include ], [ + using namespace clang; + CompilerInstance *Clang; + Clang->createPreprocessor(TU_Complete); + ], [AC_DEFINE([CREATEPREPROCESSOR_TAKES_TUKIND], [], + [Define if CompilerInstance::createPreprocessor takes + TranslationUnitKind])]) + AC_EGREP_HEADER([setMainFileID], [clang/Basic/SourceManager.h], + [AC_DEFINE([HAVE_SETMAINFILEID], [], + [Define if SourceManager has a setMainFileID method])]) + AC_CHECK_HEADER([llvm/ADT/OwningPtr.h], + [AC_DEFINE([HAVE_ADT_OWNINGPTR_H], [], + [Define if llvm/ADT/OwningPtr.h exists])]) + AC_EGREP_HEADER([initializeBuiltins], + [clang/Basic/Builtins.h], [], + [AC_DEFINE([initializeBuiltins], [InitializeBuiltins], + [Define to InitializeBuiltins for older versions of clang])]) + AC_LANG_POP + CPPFLAGS="$SAVE_CPPFLAGS" + + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$CLANG_LDFLAGS $LDFLAGS" + AC_SUBST(LIB_CLANG_EDIT) + AC_CHECK_LIB([clangEdit], [main], [LIB_CLANG_EDIT=-lclangEdit], []) + LDFLAGS="$SAVE_LDFLAGS" + ;; +esac +AM_CONDITIONAL(HAVE_CLANG, test $with_clang = system) + +AX_SET_WARNING_FLAGS + +AC_SUBST(WARNING_FLAGS) + +PACKAGE_CFLAGS="$MP_CPPFLAGS" +PACKAGE_LDFLAGS="$MP_LDFLAGS" +PACKAGE_LIBS="-lisl $MP_LIBS" +AX_CREATE_PKGCONFIG_INFO + +AX_DETECT_GIT_HEAD + +AH_BOTTOM([#include ]) +AC_CONFIG_HEADERS(isl_config.h) +AC_CONFIG_FILES(Makefile) +AC_CONFIG_FILES(doc/Makefile) +if test $with_clang = system; then + AC_CONFIG_FILES(interface/Makefile) +fi +AC_CONFIG_FILES([bound_test.sh], [chmod +x bound_test.sh]) +AC_CONFIG_FILES([codegen_test.sh], [chmod +x codegen_test.sh]) +AC_CONFIG_FILES([pip_test.sh], [chmod +x pip_test.sh]) +AC_CONFIG_COMMANDS_POST([ + dnl pass on arguments to subdir configures, but don't + dnl add them to config.status + ac_configure_args="$ac_configure_args $isl_configure_args" +]) +AC_OUTPUT Index: lib/Analysis/isl/depcomp =================================================================== --- /dev/null +++ lib/Analysis/isl/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: Index: lib/Analysis/isl/doc/CodingStyle =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/CodingStyle @@ -0,0 +1,42 @@ +This document describes some aspects of the coding style of isl, +which is similar to that of the linux kernel and git. + +The general rule is to use the same style as that of the surrounding code. + +More specific rules: + - every line should have at most 80 columns + - use tabs for indentation, where a tab counts for 8 characters + - use single spaces around binary operators such as '+', '-', '=', '!=' + - no space after unary operators such as '!' + - use a single space after a comma and a semicolon + (except at the end of a line) + - no space between function name and arguments + - use a single space after control keywords such as if, for and while + - use a single space between the type of a cast and the value + that is being cast + - no whitespace at the end of a line + - opening brace of a function is placed on a new line + - opening brace of other blocks stays on the same line + - the body of a control statement is placed on the next line(s) + - an else appears on the same line as the closing brace of + the then branch, if there is such a closing brace + - if either the then or the else branch of an if has braces, + then they both have braces + - no parentheses around argument of return keyword + - use only C style comments (/* ... */) + - no comments inside function bodies; + if some part of a function deserves additional comments, then + extract it out into a separate function first + - no #ifs inside function bodies + - variables are declared at the start of a block, before any + other statements + +There are some exceptions to the general rule of using +the same style as the surrounding code, most notably +when the surrounding code is very old. +In particular, an "isl_space" used to be called "isl_dim" and +some variables of this type are still called "dim" or some variant thereof. +New variables of this type should be called "space" or a more specific name. +Some old functions do not have memory management annotations yet. +All new functions should have memory management annotations, +whenever appropriate Index: lib/Analysis/isl/doc/Makefile.am =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/Makefile.am @@ -0,0 +1,32 @@ + +CLEANFILES = \ + manual.toc \ + manual.bbl \ + version.tex \ + user.tex \ + manual.pdf \ + manual.aux \ + manual.out \ + manual.blg \ + manual.log \ + manual.brf \ + manual.bcf \ + manual.run.xml + +if GENERATE_DOC +export TEXINPUTS := $(srcdir):$(TEXINPUTS) +export BIBINPUTS := $(srcdir):$(BIBINPUTS) +export BSTINPUTS := $(srcdir):$(BSTINPUTS) + +user.tex: user.pod + $(PERL) $(srcdir)/mypod2latex $< $@ +manual.pdf: manual.tex user.tex $(srcdir)/implementation.tex reading.tex + (cd ..; echo "@GIT_HEAD_VERSION@") > version.tex + $(PDFLATEX) $< + biber manual + $(PDFLATEX) $< + $(PDFLATEX) $< +user.html: user.pod + (cd ..; echo "@GIT_HEAD_VERSION@") > version + $(POD2HTML) --infile=$< --outfile=$@ --title="Integer Set Library: Manual [version `cat version`]" +endif Index: lib/Analysis/isl/doc/Makefile.in =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/Makefile.in @@ -0,0 +1,501 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = doc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_cc_maxopt.m4 \ + $(top_srcdir)/m4/ax_check_compiler_flags.m4 \ + $(top_srcdir)/m4/ax_compiler_vendor.m4 \ + $(top_srcdir)/m4/ax_create_pkgconfig_info.m4 \ + $(top_srcdir)/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/m4/ax_detect_git_head.m4 \ + $(top_srcdir)/m4/ax_detect_gmp.m4 \ + $(top_srcdir)/m4/ax_detect_imath.m4 \ + $(top_srcdir)/m4/ax_gcc_archflag.m4 \ + $(top_srcdir)/m4/ax_gcc_warn_unused_result.m4 \ + $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \ + $(top_srcdir)/m4/ax_set_warning_flags.m4 \ + $(top_srcdir)/m4/ax_submodule.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/isl_config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CLANG_CXXFLAGS = @CLANG_CXXFLAGS@ +CLANG_LDFLAGS = @CLANG_LDFLAGS@ +CLANG_LIBS = @CLANG_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GIT_HEAD = @GIT_HEAD@ +GIT_HEAD_ID = @GIT_HEAD_ID@ +GIT_HEAD_VERSION = @GIT_HEAD_VERSION@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIB_CLANG_EDIT = @LIB_CLANG_EDIT@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MP_CPPFLAGS = @MP_CPPFLAGS@ +MP_LDFLAGS = @MP_LDFLAGS@ +MP_LIBS = @MP_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +POD2HTML = @POD2HTML@ +PRTDIAG = @PRTDIAG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WARNING_FLAGS = @WARNING_FLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +llvm_config_found = @llvm_config_found@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfig_libdir = @pkgconfig_libdir@ +pkgconfig_libfile = @pkgconfig_libfile@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +versioninfo = @versioninfo@ +CLEANFILES = \ + manual.toc \ + manual.bbl \ + version.tex \ + user.tex \ + manual.pdf \ + manual.aux \ + manual.out \ + manual.blg \ + manual.log \ + manual.brf \ + manual.bcf \ + manual.run.xml + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign doc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +@GENERATE_DOC_TRUE@export TEXINPUTS := $(srcdir):$(TEXINPUTS) +@GENERATE_DOC_TRUE@export BIBINPUTS := $(srcdir):$(BIBINPUTS) +@GENERATE_DOC_TRUE@export BSTINPUTS := $(srcdir):$(BSTINPUTS) + +@GENERATE_DOC_TRUE@user.tex: user.pod +@GENERATE_DOC_TRUE@ $(PERL) $(srcdir)/mypod2latex $< $@ +@GENERATE_DOC_TRUE@manual.pdf: manual.tex user.tex $(srcdir)/implementation.tex reading.tex +@GENERATE_DOC_TRUE@ (cd ..; echo "@GIT_HEAD_VERSION@") > version.tex +@GENERATE_DOC_TRUE@ $(PDFLATEX) $< +@GENERATE_DOC_TRUE@ biber manual +@GENERATE_DOC_TRUE@ $(PDFLATEX) $< +@GENERATE_DOC_TRUE@ $(PDFLATEX) $< +@GENERATE_DOC_TRUE@user.html: user.pod +@GENERATE_DOC_TRUE@ (cd ..; echo "@GIT_HEAD_VERSION@") > version +@GENERATE_DOC_TRUE@ $(POD2HTML) --infile=$< --outfile=$@ --title="Integer Set Library: Manual [version `cat version`]" + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: Index: lib/Analysis/isl/doc/SubmittingPatches =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/SubmittingPatches @@ -0,0 +1,52 @@ +[Mostly copied from git's SubmittingPatches] + + Commits: + + - make commits of logical units + - check for unnecessary whitespace with "git diff --check" + before committing + - do not check in commented out code or unneeded files + - the first line of the commit message should be a short + description and should skip the full stop + - the body should provide a meaningful commit message, which + includes motivation for the change, and contrasts + its implementation with previous behaviour + - if you want your work included in isl.git, add a + "Signed-off-by: Your Name " line to the + commit message (or just use the option "-s" when + committing) to confirm that you agree to the Developer's + Certificate of Origin + - make sure that you have tests for the bug you are fixing + - make sure that the test suite passes after your commit + + Patch: + + - use "git format-patch -M" to create the patch + - do not PGP sign your patch + - send a single patch per mail, e.g., using git-send-email(1) + - do not attach your patch, but read in the mail + body, unless you cannot teach your mailer to + leave the formatting of the patch alone. + - be careful doing cut & paste into your mailer, not to + corrupt whitespaces. + - provide additional information (which is unsuitable for + the commit message) between the "---" and the diffstat + - if you change, add, or remove a command line option or + make some other user interface change, the associated + documentation should be updated as well. + - if your name is not writable in ASCII, make sure that + you send off a message in the correct encoding. + - send the patch to the development mailing list + (isl-development@googlegroups.com). If you use + git-send-email(1), please test it first by sending email + to yourself. + + Revisions: + + - add the revision number inside square brackets to + the subject line (e.g., use --subject-prefix='PATCH v2' + when creating the patch) + - recall the major issues discovered during the previous + review and explain how you addressed them or why you + disagree. Do so either in a cover letter, between the + "---" and the diffstat or in a separate message. Index: lib/Analysis/isl/doc/implementation.tex =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/implementation.tex @@ -0,0 +1,2037 @@ +\section{Sets and Relations} + +\begin{definition}[Polyhedral Set] +A {\em polyhedral set}\index{polyhedral set} $S$ is a finite union of basic sets +$S = \bigcup_i S_i$, each of which can be represented using affine +constraints +$$ +S_i : \Z^n \to 2^{\Z^d} : \vec s \mapsto +S_i(\vec s) = +\{\, \vec x \in \Z^d \mid \exists \vec z \in \Z^e : +A \vec x + B \vec s + D \vec z + \vec c \geq \vec 0 \,\} +, +$$ +with $A \in \Z^{m \times d}$, +$B \in \Z^{m \times n}$, +$D \in \Z^{m \times e}$ +and $\vec c \in \Z^m$. +\end{definition} + +\begin{definition}[Parameter Domain of a Set] +Let $S \in \Z^n \to 2^{\Z^d}$ be a set. +The {\em parameter domain} of $S$ is the set +$$\pdom S \coloneqq \{\, \vec s \in \Z^n \mid S(\vec s) \ne \emptyset \,\}.$$ +\end{definition} + +\begin{definition}[Polyhedral Relation] +A {\em polyhedral relation}\index{polyhedral relation} +$R$ is a finite union of basic relations +$R = \bigcup_i R_i$ of type +$\Z^n \to 2^{\Z^{d_1+d_2}}$, +each of which can be represented using affine +constraints +$$ +R_i = \vec s \mapsto +R_i(\vec s) = +\{\, \vec x_1 \to \vec x_2 \in \Z^{d_1} \times \Z^{d_2} +\mid \exists \vec z \in \Z^e : +A_1 \vec x_1 + A_2 \vec x_2 + B \vec s + D \vec z + \vec c \geq \vec 0 \,\} +, +$$ +with $A_i \in \Z^{m \times d_i}$, +$B \in \Z^{m \times n}$, +$D \in \Z^{m \times e}$ +and $\vec c \in \Z^m$. +\end{definition} + +\begin{definition}[Parameter Domain of a Relation] +Let $R \in \Z^n \to 2^{\Z^{d+d}}$ be a relation. +The {\em parameter domain} of $R$ is the set +$$\pdom R \coloneqq \{\, \vec s \in \Z^n \mid R(\vec s) \ne \emptyset \,\}.$$ +\end{definition} + +\begin{definition}[Domain of a Relation] +Let $R \in \Z^n \to 2^{\Z^{d+d}}$ be a relation. +The {\em domain} of $R$ is the polyhedral set +$$\domain R \coloneqq \vec s \mapsto +\{\, \vec x_1 \in \Z^{d_1} \mid \exists \vec x_2 \in \Z^{d_2} : +(\vec x_1, \vec x_2) \in R(\vec s) \,\} +. +$$ +\end{definition} + +\begin{definition}[Range of a Relation] +Let $R \in \Z^n \to 2^{\Z^{d+d}}$ be a relation. +The {\em range} of $R$ is the polyhedral set +$$ +\range R \coloneqq \vec s \mapsto +\{\, \vec x_2 \in \Z^{d_2} \mid \exists \vec x_1 \in \Z^{d_1} : +(\vec x_1, \vec x_2) \in R(\vec s) \,\} +. +$$ +\end{definition} + +\begin{definition}[Composition of Relations] +Let $R \in \Z^n \to 2^{\Z^{d_1+d_2}}$ and +$S \in \Z^n \to 2^{\Z^{d_2+d_3}}$ be two relations, +then the composition of +$R$ and $S$ is defined as +$$ +S \circ R \coloneqq +\vec s \mapsto +\{\, \vec x_1 \to \vec x_3 \in \Z^{d_1} \times \Z^{d_3} +\mid \exists \vec x_2 \in \Z^{d_2} : +\vec x_1 \to \vec x_2 \in R(\vec s) \wedge +\vec x_2 \to \vec x_3 \in S(\vec s) +\,\} +. +$$ +\end{definition} + +\begin{definition}[Difference Set of a Relation] +Let $R \in \Z^n \to 2^{\Z^{d+d}}$ be a relation. +The difference set ($\Delta \, R$) of $R$ is the set +of differences between image elements and the corresponding +domain elements, +$$ +\diff R \coloneqq +\vec s \mapsto +\{\, \vec \delta \in \Z^{d} \mid \exists \vec x \to \vec y \in R : +\vec \delta = \vec y - \vec x +\,\} +$$ +\end{definition} + +\section{Simple Hull}\label{s:simple hull} + +It is sometimes useful to have a single +basic set or basic relation that contains a given set or relation. +For rational sets, the obvious choice would be to compute the +(rational) convex hull. For integer sets, the obvious choice +would be the integer hull. +However, {\tt isl} currently does not support an integer hull operation +and even if it did, it would be fairly expensive to compute. +The convex hull operation is supported, but it is also fairly +expensive to compute given only an implicit representation. + +Usually, it is not required to compute the exact integer hull, +and an overapproximation of this hull is sufficient. +The ``simple hull'' of a set is such an overapproximation +and it is defined as the (inclusion-wise) smallest basic set +that is described by constraints that are translates of +the constraints in the input set. +This means that the simple hull is relatively cheap to compute +and that the number of constraints in the simple hull is no +larger than the number of constraints in the input. +\begin{definition}[Simple Hull of a Set] +The {\em simple hull} of a set +$S = \bigcup_{1 \le i \le v} S_i$, with +$$ +S : \Z^n \to 2^{\Z^d} : \vec s \mapsto +S(\vec s) = +\left\{\, \vec x \in \Z^d \mid \exists \vec z \in \Z^e : +\bigvee_{1 \le i \le v} +A_i \vec x + B_i \vec s + D_i \vec z + \vec c_i \geq \vec 0 \,\right\} +$$ +is the set +$$ +H : \Z^n \to 2^{\Z^d} : \vec s \mapsto +S(\vec s) = +\left\{\, \vec x \in \Z^d \mid \exists \vec z \in \Z^e : +\bigwedge_{1 \le i \le v} +A_i \vec x + B_i \vec s + D_i \vec z + \vec c_i + \vec K_i \geq \vec 0 +\,\right\} +, +$$ +with $\vec K_i$ the (component-wise) smallest non-negative integer vectors +such that $S \subseteq H$. +\end{definition} +The $\vec K_i$ can be obtained by solving a number of +LP problems, one for each element of each $\vec K_i$. +If any LP problem is unbounded, then the corresponding constraint +is dropped. + +\section{Parametric Integer Programming} + +\subsection{Introduction}\label{s:intro} + +Parametric integer programming \parencite{Feautrier88parametric} +is used to solve many problems within the context of the polyhedral model. +Here, we are mainly interested in dependence analysis \parencite{Fea91} +and in computing a unique representation for existentially quantified +variables. The latter operation has been used for counting elements +in sets involving such variables +\parencite{BouletRe98,Verdoolaege2005experiences} and lies at the core +of the internal representation of {\tt isl}. + +Parametric integer programming was first implemented in \texttt{PipLib}. +An alternative method for parametric integer programming +was later implemented in {\tt barvinok} \cite{barvinok-0.22}. +This method is not based on Feautrier's algorithm, but on rational +generating functions \cite{Woods2003short} and was inspired by the +``digging'' technique of \textcite{DeLoera2004Three} for solving +non-parametric integer programming problems. + +In the following sections, we briefly recall the dual simplex +method combined with Gomory cuts and describe some extensions +and optimizations. The main algorithm is applied to a matrix +data structure known as a tableau. In case of parametric problems, +there are two tableaus, one for the main problem and one for +the constraints on the parameters, known as the context tableau. +The handling of the context tableau is described in \autoref{s:context}. + +\subsection{The Dual Simplex Method} + +Tableaus can be represented in several slightly different ways. +In {\tt isl}, the dual simplex method uses the same representation +as that used by its incremental LP solver based on the \emph{primal} +simplex method. The implementation of this LP solver is based +on that of {\tt Simplify} \parencite{Detlefs2005simplify}, which, in turn, +was derived from the work of \textcite{Nelson1980phd}. +In the original \parencite{Nelson1980phd}, the tableau was implemented +as a sparse matrix, but neither {\tt Simplify} nor the current +implementation of {\tt isl} does so. + +Given some affine constraints on the variables, +$A \vec x + \vec b \ge \vec 0$, the tableau represents the relationship +between the variables $\vec x$ and non-negative variables +$\vec y = A \vec x + \vec b$ corresponding to the constraints. +The initial tableau contains $\begin{pmatrix} +\vec b & A +\end{pmatrix}$ and expresses the constraints $\vec y$ in the rows in terms +of the variables $\vec x$ in the columns. The main operation defined +on a tableau exchanges a column and a row variable and is called a pivot. +During this process, some coefficients may become rational. +As in the \texttt{PipLib} implementation, +{\tt isl} maintains a shared denominator per row. +The sample value of a tableau is one where each column variable is assigned +zero and each row variable is assigned the constant term of the row. +This sample value represents a valid solution if each constraint variable +is assigned a non-negative value, i.e., if the constant terms of +rows corresponding to constraints are all non-negative. + +The dual simplex method starts from an initial sample value that +may be invalid, but that is known to be (lexicographically) no +greater than any solution, and gradually increments this sample value +through pivoting until a valid solution is obtained. +In particular, each pivot exchanges a row variable +$r = -n + \sum_i a_i \, c_i$ with negative +sample value $-n$ with a column variable $c_j$ +such that $a_j > 0$. Since $c_j = (n + r - \sum_{i\ne j} a_i \, c_i)/a_j$, +the new row variable will have a positive sample value $n$. +If no such column can be found, then the problem is infeasible. +By always choosing the column that leads to the (lexicographically) +smallest increment in the variables $\vec x$, +the first solution found is guaranteed to be the (lexicographically) +minimal solution \cite{Feautrier88parametric}. +In order to be able to determine the smallest increment, the tableau +is (implicitly) extended with extra rows defining the original +variables in terms of the column variables. +If we assume that all variables are non-negative, then we know +that the zero vector is no greater than the minimal solution and +then the initial extended tableau looks as follows. +$$ +\begin{tikzpicture} +\matrix (m) [matrix of math nodes] +{ +& {} & 1 & \vec c \\ +\vec x && |(top)| \vec 0 & I \\ +\vec r && \vec b & |(bottom)|A \\ +}; +\begin{pgfonlayer}{background} +\node (core) [inner sep=0pt,fill=black!20,right delimiter=),left delimiter=(,fit=(top)(bottom)] {}; +\end{pgfonlayer} +\end{tikzpicture} +$$ +Each column in this extended tableau is lexicographically positive +and will remain so because of the column choice explained above. +It is then clear that the value of $\vec x$ will increase in each step. +Note that there is no need to store the extra rows explicitly. +If a given $x_i$ is a column variable, then the corresponding row +is the unit vector $e_i$. If, on the other hand, it is a row variable, +then the row already appears somewhere else in the tableau. + +In case of parametric problems, the sign of the constant term +may depend on the parameters. Each time the constant term of a constraint row +changes, we therefore need to check whether the new term can attain +negative and/or positive values over the current set of possible +parameter values, i.e., the context. +If all these terms can only attain non-negative values, the current +state of the tableau represents a solution. If one of the terms +can only attain non-positive values and is not identically zero, +the corresponding row can be pivoted. +Otherwise, we pick one of the terms that can attain both positive +and negative values and split the context into a part where +it only attains non-negative values and a part where it only attains +negative values. + +\subsection{Gomory Cuts} + +The solution found by the dual simplex method may have +non-integral coordinates. If so, some rational solutions +(including the current sample value), can be cut off by +applying a (parametric) Gomory cut. +Let $r = b(\vec p) + \sp {\vec a} {\vec c}$ be the row +corresponding to the first non-integral coordinate of $\vec x$, +with $b(\vec p)$ the constant term, an affine expression in the +parameters $\vec p$, i.e., $b(\vec p) = \sp {\vec f} {\vec p} + g$. +Note that only row variables can attain +non-integral values as the sample value of the column variables is zero. +Consider the expression +$b(\vec p) - \ceil{b(\vec p)} + \sp {\fract{\vec a}} {\vec c}$, +with $\ceil\cdot$ the ceiling function and $\fract\cdot$ the +fractional part. This expression is negative at the sample value +since $\vec c = \vec 0$ and $r = b(\vec p)$ is fractional, i.e., +$\ceil{b(\vec p)} > b(\vec p)$. On the other hand, for each integral +value of $r$ and $\vec c \ge 0$, the expression is non-negative +because $b(\vec p) - \ceil{b(\vec p)} > -1$. +Imposing this expression to be non-negative therefore does not +invalidate any integral solutions, while it does cut away the current +fractional sample value. To be able to formulate this constraint, +a new variable $q = \floor{-b(\vec p)} = - \ceil{b(\vec p)}$ is added +to the context. This integral variable is uniquely defined by the constraints +$0 \le -d \, b(\vec p) - d \, q \le d - 1$, with $d$ the common +denominator of $\vec f$ and $g$. In practice, the variable +$q' = \floor{\sp {\fract{-f}} {\vec p} + \fract{-g}}$ is used instead +and the coefficients of the new constraint are adjusted accordingly. +The sign of the constant term of this new constraint need not be determined +as it is non-positive by construction. +When several of these extra context variables are added, it is important +to avoid adding duplicates. +Recent versions of {\tt PipLib} also check for such duplicates. + +\subsection{Negative Unknowns and Maximization} + +There are two places in the above algorithm where the unknowns $\vec x$ +are assumed to be non-negative: the initial tableau starts from +sample value $\vec x = \vec 0$ and $\vec c$ is assumed to be non-negative +during the construction of Gomory cuts. +To deal with negative unknowns, \textcite[Appendix A.2]{Fea91} +proposed to use a ``big parameter'', say $M$, that is taken to be +an arbitrarily large positive number. Instead of looking for the +lexicographically minimal value of $\vec x$, we search instead +for the lexicographically minimal value of $\vec x' = \vec M + \vec x$. +The sample value $\vec x' = \vec 0$ of the initial tableau then +corresponds to $\vec x = -\vec M$, which is clearly not greater than +any potential solution. The sign of the constant term of a row +is determined lexicographically, with the coefficient of $M$ considered +first. That is, if the coefficient of $M$ is not zero, then its sign +is the sign of the entire term. Otherwise, the sign is determined +by the remaining affine expression in the parameters. +If the original problem has a bounded optimum, then the final sample +value will be of the form $\vec M + \vec v$ and the optimal value +of the original problem is then $\vec v$. +Maximization problems can be handled in a similar way by computing +the minimum of $\vec M - \vec x$. + +When the optimum is unbounded, the optimal value computed for +the original problem will involve the big parameter. +In the original implementation of {\tt PipLib}, the big parameter could +even appear in some of the extra variables $\vec q$ created during +the application of a Gomory cut. The final result could then contain +implicit conditions on the big parameter through conditions on such +$\vec q$ variables. This problem was resolved in later versions +of {\tt PipLib} by taking $M$ to be divisible by any positive number. +The big parameter can then never appear in any $\vec q$ because +$\fract {\alpha M } = 0$. It should be noted, though, that an unbounded +problem usually (but not always) +indicates an incorrect formulation of the problem. + +The original version of {\tt PipLib} required the user to ``manually'' +add a big parameter, perform the reformulation and interpret the result +\parencite{Feautrier02}. Recent versions allow the user to simply +specify that the unknowns may be negative or that the maximum should +be computed and then these transformations are performed internally. +Although there are some application, e.g., +that of \textcite{Feautrier92multi}, +where it is useful to have explicit control over the big parameter, +negative unknowns and maximization are by far the most common applications +of the big parameter and we believe that the user should not be bothered +with such implementation issues. +The current version of {\tt isl} therefore does not +provide any interface for specifying big parameters. Instead, the user +can specify whether a maximum needs to be computed and no assumptions +are made on the sign of the unknowns. Instead, the sign of the unknowns +is checked internally and a big parameter is automatically introduced when +needed. For compatibility with {\tt PipLib}, the {\tt isl\_pip} tool +does explicitly add non-negativity constraints on the unknowns unless +the \verb+Urs_unknowns+ option is specified. +Currently, there is also no way in {\tt isl} of expressing a big +parameter in the output. Even though +{\tt isl} makes the same divisibility assumption on the big parameter +as recent versions of {\tt PipLib}, it will therefore eventually +produce an error if the problem turns out to be unbounded. + +\subsection{Preprocessing} + +In this section, we describe some transformations that are +or can be applied in advance to reduce the running time +of the actual dual simplex method with Gomory cuts. + +\subsubsection{Feasibility Check and Detection of Equalities} + +Experience with the original {\tt PipLib} has shown that Gomory cuts +do not perform very well on problems that are (non-obviously) empty, +i.e., problems with rational solutions, but no integer solutions. +In {\tt isl}, we therefore first perform a feasibility check on +the original problem considered as a non-parametric problem +over the combined space of unknowns and parameters. +In fact, we do not simply check the feasibility, but we also +check for implicit equalities among the integer points by computing +the integer affine hull. The algorithm used is the same as that +described in \autoref{s:GBR} below. +Computing the affine hull is fairly expensive, but it can +bring huge benefits if any equalities can be found or if the problem +turns out to be empty. + +\subsubsection{Constraint Simplification} + +If the coefficients of the unknown and parameters in a constraint +have a common factor, then this factor should be removed, possibly +rounding down the constant term. For example, the constraint +$2 x - 5 \ge 0$ should be simplified to $x - 3 \ge 0$. +{\tt isl} performs such simplifications on all sets and relations. +Recent versions of {\tt PipLib} also perform this simplification +on the input. + +\subsubsection{Exploiting Equalities}\label{s:equalities} + +If there are any (explicit) equalities in the input description, +{\tt PipLib} converts each into a pair of inequalities. +It is also possible to write $r$ equalities as $r+1$ inequalities +\parencite{Feautrier02}, but it is even better to \emph{exploit} the +equalities to reduce the dimensionality of the problem. +Given an equality involving at least one unknown, we pivot +the row corresponding to the equality with the column corresponding +to the last unknown with non-zero coefficient. The new column variable +can then be removed completely because it is identically zero, +thereby reducing the dimensionality of the problem by one. +The last unknown is chosen to ensure that the columns of the initial +tableau remain lexicographically positive. In particular, if +the equality is of the form $b + \sum_{i \le j} a_i \, x_i = 0$ with +$a_j \ne 0$, then the (implicit) top rows of the initial tableau +are changed as follows +$$ +\begin{tikzpicture} +\matrix [matrix of math nodes] +{ + & {} & |(top)| 0 & I_1 & |(j)| & \\ +j && 0 & & 1 & \\ + && 0 & & & |(bottom)|I_2 \\ +}; +\node[overlay,above=2mm of j,anchor=south]{j}; +\begin{pgfonlayer}{background} +\node (m) [inner sep=0pt,fill=black!20,right delimiter=),left delimiter=(,fit=(top)(bottom)] {}; +\end{pgfonlayer} +\begin{scope}[xshift=4cm] +\matrix [matrix of math nodes] +{ + & {} & |(top)| 0 & I_1 & \\ +j && |(left)| -b/a_j & -a_i/a_j & \\ + && 0 & & |(bottom)|I_2 \\ +}; +\begin{pgfonlayer}{background} +\node (m2) [inner sep=0pt,fill=black!20,right delimiter=),left delimiter=(,fit=(top)(bottom)(left)] {}; +\end{pgfonlayer} +\end{scope} + \draw [shorten >=7mm,-to,thick,decorate, + decoration={snake,amplitude=.4mm,segment length=2mm, + pre=moveto,pre length=5mm,post length=8mm}] + (m) -- (m2); +\end{tikzpicture} +$$ +Currently, {\tt isl} also eliminates equalities involving only parameters +in a similar way, provided at least one of the coefficients is equal to one. +The application of parameter compression (see below) +would obviate the need for removing parametric equalities. + +\subsubsection{Offline Symmetry Detection}\label{s:offline} + +Some problems, notably those of \textcite{Bygde2010licentiate}, +have a collection of constraints, say +$b_i(\vec p) + \sp {\vec a} {\vec x} \ge 0$, +that only differ in their (parametric) constant terms. +These constant terms will be non-negative on different parts +of the context and this context may have to be split for each +of the constraints. In the worst case, the basic algorithm may +have to consider all possible orderings of the constant terms. +Instead, {\tt isl} introduces a new parameter, say $u$, and +replaces the collection of constraints by the single +constraint $u + \sp {\vec a} {\vec x} \ge 0$ along with +context constraints $u \le b_i(\vec p)$. +Any solution to the new system is also a solution +to the original system since +$\sp {\vec a} {\vec x} \ge -u \ge -b_i(\vec p)$. +Conversely, $m = \min_i b_i(\vec p)$ satisfies the constraints +on $u$ and therefore extends a solution to the new system. +It can also be plugged into a new solution. +See \autoref{s:post} for how this substitution is currently performed +in {\tt isl}. +The method described in this section can only detect symmetries +that are explicitly available in the input. +See \autoref{s:online} for the detection +and exploitation of symmetries that appear during the course of +the dual simplex method. + +\subsubsection{Parameter Compression}\label{s:compression} + +It may in some cases be apparent from the equalities in the problem +description that there can only be a solution for a sublattice +of the parameters. In such cases ``parameter compression'' +\parencite{Meister2004PhD,Meister2008} can be used to replace +the parameters by alternative ``dense'' parameters. +For example, if there is a constraint $2x = n$, then the system +will only have solutions for even values of $n$ and $n$ can be replaced +by $2n'$. Similarly, the parameters $n$ and $m$ in a system with +the constraint $2n = 3m$ can be replaced by a single parameter $n'$ +with $n=3n'$ and $m=2n'$. +It is also possible to perform a similar compression on the unknowns, +but it would be more complicated as the compression would have to +preserve the lexicographical order. Moreover, due to our handling +of equalities described above there should be +no need for such variable compression. +Although parameter compression has been implemented in {\tt isl}, +it is currently not yet used during parametric integer programming. + +\subsection{Postprocessing}\label{s:post} + +The output of {\tt PipLib} is a quast (quasi-affine selection tree). +Each internal node in this tree corresponds to a split of the context +based on a parametric constant term in the main tableau with indeterminate +sign. Each of these nodes may introduce extra variables in the context +corresponding to integer divisions. Each leaf of the tree prescribes +the solution in that part of the context that satisfies all the conditions +on the path leading to the leaf. +Such a quast is a very economical way of representing the solution, but +it would not be suitable as the (only) internal representation of +sets and relations in {\tt isl}. Instead, {\tt isl} represents +the constraints of a set or relation in disjunctive normal form. +The result of a parametric integer programming problem is then also +converted to this internal representation. Unfortunately, the conversion +to disjunctive normal form can lead to an explosion of the size +of the representation. +In some cases, this overhead would have to be paid anyway in subsequent +operations, but in other cases, especially for outside users that just +want to solve parametric integer programming problems, we would like +to avoid this overhead in future. That is, we are planning on introducing +quasts or a related representation as one of several possible internal +representations and on allowing the output of {\tt isl\_pip} to optionally +be printed as a quast. + +Currently, {\tt isl} also does not have an internal representation +for expressions such as $\min_i b_i(\vec p)$ from the offline +symmetry detection of \autoref{s:offline}. +Assume that one of these expressions has $n$ bounds $b_i(\vec p)$. +If the expression +does not appear in the affine expression describing the solution, +but only in the constraints, and if moreover, the expression +only appears with a positive coefficient, i.e., +$\min_i b_i(\vec p) \ge f_j(\vec p)$, then each of these constraints +can simply be reduplicated $n$ times, once for each of the bounds. +Otherwise, a conversion to disjunctive normal form +leads to $n$ cases, each described as $u = b_i(\vec p)$ with constraints +$b_i(\vec p) \le b_j(\vec p)$ for $j > i$ +and +$b_i(\vec p) < b_j(\vec p)$ for $j < i$. +Note that even though this conversion leads to a size increase +by a factor of $n$, not detecting the symmetry could lead to +an increase by a factor of $n!$ if all possible orderings end up being +considered. + +\subsection{Context Tableau}\label{s:context} + +The main operation that a context tableau needs to provide is a test +on the sign of an affine expression over the elements of the context. +This sign can be determined by solving two integer linear feasibility +problems, one with a constraint added to the context that enforces +the expression to be non-negative and one where the expression is +negative. As already mentioned by \textcite{Feautrier88parametric}, +any integer linear feasibility solver could be used, but the {\tt PipLib} +implementation uses a recursive call to the dual simplex with Gomory +cuts algorithm to determine the feasibility of a context. +In {\tt isl}, two ways of handling the context have been implemented, +one that performs the recursive call and one, used by default, that +uses generalized basis reduction. +We start with some optimizations that are shared between the two +implementations and then discuss additional details of each of them. + +\subsubsection{Maintaining Witnesses}\label{s:witness} + +A common feature of both integer linear feasibility solvers is that +they will not only say whether a set is empty or not, but if the set +is non-empty, they will also provide a \emph{witness} for this result, +i.e., a point that belongs to the set. By maintaining a list of such +witnesses, we can avoid many feasibility tests during the determination +of the signs of affine expressions. In particular, if the expression +evaluates to a positive number on some of these points and to a negative +number on some others, then no feasibility test needs to be performed. +If all the evaluations are non-negative, we only need to check for the +possibility of a negative value and similarly in case of all +non-positive evaluations. Finally, in the rare case that all points +evaluate to zero or at the start, when no points have been collected yet, +one or two feasibility tests need to be performed depending on the result +of the first test. + +When a new constraint is added to the context, the points that +violate the constraint are temporarily removed. They are reconsidered +when we backtrack over the addition of the constraint, as they will +satisfy the negation of the constraint. It is only when we backtrack +over the addition of the points that they are finally removed completely. +When an extra integer division is added to the context, +the new coordinates of the +witnesses can easily be computed by evaluating the integer division. +The idea of keeping track of witnesses was first used in {\tt barvinok}. + +\subsubsection{Choice of Constant Term on which to Split} + +Recall that if there are no rows with a non-positive constant term, +but there are rows with an indeterminate sign, then the context +needs to be split along the constant term of one of these rows. +If there is more than one such row, then we need to choose which row +to split on first. {\tt PipLib} uses a heuristic based on the (absolute) +sizes of the coefficients. In particular, it takes the largest coefficient +of each row and then selects the row where this largest coefficient is smaller +than those of the other rows. + +In {\tt isl}, we take that row for which non-negativity of its constant +term implies non-negativity of as many of the constant terms of the other +rows as possible. The intuition behind this heuristic is that on the +positive side, we will have fewer negative and indeterminate signs, +while on the negative side, we need to perform a pivot, which may +affect any number of rows meaning that the effect on the signs +is difficult to predict. This heuristic is of course much more +expensive to evaluate than the heuristic used by {\tt PipLib}. +More extensive tests are needed to evaluate whether the heuristic is worthwhile. + +\subsubsection{Dual Simplex + Gomory Cuts} + +When a new constraint is added to the context, the first steps +of the dual simplex method applied to this new context will be the same +or at least very similar to those taken on the original context, i.e., +before the constraint was added. In {\tt isl}, we therefore apply +the dual simplex method incrementally on the context and backtrack +to a previous state when a constraint is removed again. +An initial implementation that was never made public would also +keep the Gomory cuts, but the current implementation backtracks +to before the point where Gomory cuts are added before adding +an extra constraint to the context. +Keeping the Gomory cuts has the advantage that the sample value +is always an integer point and that this point may also satisfy +the new constraint. However, due to the technique of maintaining +witnesses explained above, +we would not perform a feasibility test in such cases and then +the previously added cuts may be redundant, possibly resulting +in an accumulation of a large number of cuts. + +If the parameters may be negative, then the same big parameter trick +used in the main tableau is applied to the context. This big parameter +is of course unrelated to the big parameter from the main tableau. +Note that it is not a requirement for this parameter to be ``big'', +but it does allow for some code reuse in {\tt isl}. +In {\tt PipLib}, the extra parameter is not ``big'', but this may be because +the big parameter of the main tableau also appears +in the context tableau. + +Finally, it was reported by \textcite{Galea2009personal}, who +worked on a parametric integer programming implementation +in {\tt PPL} \parencite{PPL}, +that it is beneficial to add cuts for \emph{all} rational coordinates +in the context tableau. Based on this report, +the initial {\tt isl} implementation was adapted accordingly. + +\subsubsection{Generalized Basis Reduction}\label{s:GBR} + +The default algorithm used in {\tt isl} for feasibility checking +is generalized basis reduction \parencite{Cook1991implementation}. +This algorithm is also used in the {\tt barvinok} implementation. +The algorithm is fairly robust, but it has some overhead. +We therefore try to avoid calling the algorithm in easy cases. +In particular, we incrementally keep track of points for which +the entire unit hypercube positioned at that point lies in the context. +This set is described by translates of the constraints of the context +and if (rationally) non-empty, any rational point +in the set can be rounded up to yield an integer point in the context. + +A restriction of the algorithm is that it only works on bounded sets. +The affine hull of the recession cone therefore needs to be projected +out first. As soon as the algorithm is invoked, we then also +incrementally keep track of this recession cone. The reduced basis +found by one call of the algorithm is also reused as initial basis +for the next call. + +Some problems lead to the +introduction of many integer divisions. Within a given context, +some of these integer divisions may be equal to each other, even +if the expressions are not identical, or they may be equal to some +affine combination of other variables. +To detect such cases, we compute the affine hull of the context +each time a new integer division is added. The algorithm used +for computing this affine hull is that of \textcite{Karr1976affine}, +while the points used in this algorithm are obtained by performing +integer feasibility checks on that part of the context outside +the current approximation of the affine hull. +The list of witnesses is used to construct an initial approximation +of the hull, while any extra points found during the construction +of the hull is added to this list. +Any equality found in this way that expresses an integer division +as an \emph{integer} affine combination of other variables is +propagated to the main tableau, where it is used to eliminate that +integer division. + +\subsection{Experiments} + +\autoref{t:comparison} compares the execution times of {\tt isl} +(with both types of context tableau) +on some more difficult instances to those of other tools, +run on an Intel Xeon W3520 @ 2.66GHz. +These instances are available in the \lstinline{testsets/pip} directory +of the {\tt isl} distribution. +Easier problems such as the +test cases distributed with {\tt Pip\-Lib} can be solved so quickly +that we would only be measuring overhead such as input/output and conversions +and not the running time of the actual algorithm. +We compare the following versions: +{\tt piplib-1.4.0-5-g0132fd9}, +{\tt barvinok-0.32.1-73-gc5d7751}, +{\tt isl-0.05.1-82-g3a37260} +and {\tt PPL} version 0.11.2. + +The first test case is the following dependence analysis problem +originating from the Phideo project \parencite{Verhaegh1995PhD} +that was communicated to us by Bart Kienhuis: +\begin{lstlisting}[flexiblecolumns=true,breaklines=true]{} +lexmax { [j1,j2] -> [i1,i2,i3,i4,i5,i6,i7,i8,i9,i10] : 1 <= i1,j1 <= 8 and 1 <= i2,i3,i4,i5,i6,i7,i8,i9,i10 <= 2 and 1 <= j2 <= 128 and i1-1 = j1-1 and i2-1+2*i3-2+4*i4-4+8*i5-8+16*i6-16+32*i7-32+64*i8-64+128*i9-128+256*i10-256=3*j2-3+66 }; +\end{lstlisting} +This problem was the main inspiration +for some of the optimizations in \autoref{s:GBR}. +The second group of test cases are projections used during counting. +The first nine of these come from \textcite{Seghir2006minimizing}. +The remaining two come from \textcite{Verdoolaege2005experiences} and +were used to drive the first, Gomory cuts based, implementation +in {\tt isl}. +The third and final group of test cases are borrowed from +\textcite{Bygde2010licentiate} and inspired the offline symmetry detection +of \autoref{s:offline}. Without symmetry detection, the running times +are 11s and 5.9s. +All running times of {\tt barvinok} and {\tt isl} include a conversion +to disjunctive normal form. Without this conversion, the final two +cases can be solved in 0.07s and 0.21s. +The {\tt PipLib} implementation has some fixed limits and will +sometimes report the problem to be too complex (TC), while on some other +problems it will run out of memory (OOM). +The {\tt barvinok} implementation does not support problems +with a non-trivial lineality space (line) nor maximization problems (max). +The Gomory cuts based {\tt isl} implementation was terminated after 1000 +minutes on the first problem. The gbr version introduces some +overhead on some of the easier problems, but is overall the clear winner. + +\begin{table} +\begin{center} +\begin{tabular}{lrrrrr} + & {\tt PipLib} & {\tt barvinok} & {\tt isl} cut & {\tt isl} gbr & {\tt PPL} \\ +\hline +\hline +% bart.pip +Phideo & TC & 793m & $>$999m & 2.7s & 372m \\ +\hline +e1 & 0.33s & 3.5s & 0.08s & 0.11s & 0.18s \\ +e3 & 0.14s & 0.13s & 0.10s & 0.10s & 0.17s \\ +e4 & 0.24s & 9.1s & 0.09s & 0.11s & 0.70s \\ +e5 & 0.12s & 6.0s & 0.06s & 0.14s & 0.17s \\ +e6 & 0.10s & 6.8s & 0.17s & 0.08s & 0.21s \\ +e7 & 0.03s & 0.27s & 0.04s & 0.04s & 0.03s \\ +e8 & 0.03s & 0.18s & 0.03s & 0.04s & 0.01s \\ +e9 & OOM & 70m & 2.6s & 0.94s & 22s \\ +vd & 0.04s & 0.10s & 0.03s & 0.03s & 0.03s \\ +bouleti & 0.25s & line & 0.06s & 0.06s & 0.15s \\ +difficult & OOM & 1.3s & 1.7s & 0.33s & 1.4s \\ +\hline +cnt/sum & TC & max & 2.2s & 2.2s & OOM \\ +jcomplex & TC & max & 3.7s & 3.9s & OOM \\ +\end{tabular} +\caption{Comparison of Execution Times} +\label{t:comparison} +\end{center} +\end{table} + +\subsection{Online Symmetry Detection}\label{s:online} + +Manual experiments on small instances of the problems of +\textcite{Bygde2010licentiate} and an analysis of the results +by the approximate MPA method developed by \textcite{Bygde2010licentiate} +have revealed that these problems contain many more symmetries +than can be detected using the offline method of \autoref{s:offline}. +In this section, we present an online detection mechanism that has +not been implemented yet, but that has shown promising results +in manual applications. + +Let us first consider what happens when we do not perform offline +symmetry detection. At some point, one of the +$b_i(\vec p) + \sp {\vec a} {\vec x} \ge 0$ constraints, +say the $j$th constraint, appears as a column +variable, say $c_1$, while the other constraints are represented +as rows of the form $b_i(\vec p) - b_j(\vec p) + c$. +The context is then split according to the relative order of +$b_j(\vec p)$ and one of the remaining $b_i(\vec p)$. +The offline method avoids this split by replacing all $b_i(\vec p)$ +by a single newly introduced parameter that represents the minimum +of these $b_i(\vec p)$. +In the online method the split is similarly avoided by the introduction +of a new parameter. In particular, a new parameter is introduced +that represents +$\left| b_j(\vec p) - b_i(\vec p) \right|_+ = +\max(b_j(\vec p) - b_i(\vec p), 0)$. + +In general, let $r = b(\vec p) + \sp {\vec a} {\vec c}$ be a row +of the tableau such that the sign of $b(\vec p)$ is indeterminate +and such that exactly one of the elements of $\vec a$ is a $1$, +while all remaining elements are non-positive. +That is, $r = b(\vec p) + c_j - f$ with $f = -\sum_{i\ne j} a_i c_i \ge 0$. +We introduce a new parameter $t$ with +context constraints $t \ge -b(\vec p)$ and $t \ge 0$ and replace +the column variable $c_j$ by $c' + t$. The row $r$ is now equal +to $b(\vec p) + t + c' - f$. The constant term of this row is always +non-negative because any negative value of $b(\vec p)$ is compensated +by $t \ge -b(\vec p)$ while and non-negative value remains non-negative +because $t \ge 0$. + +We need to show that this transformation does not eliminate any valid +solutions and that it does not introduce any spurious solutions. +Given a valid solution for the original problem, we need to find +a non-negative value of $c'$ satisfying the constraints. +If $b(\vec p) \ge 0$, we can take $t = 0$ so that +$c' = c_j - t = c_j \ge 0$. +If $b(\vec p) < 0$, we can take $t = -b(\vec p)$. +Since $r = b(\vec p) + c_j - f \ge 0$ and $f \ge 0$, we have +$c' = c_j + b(\vec p) \ge 0$. +Note that these choices amount to plugging in +$t = \left|-b(\vec p)\right|_+ = \max(-b(\vec p), 0)$. +Conversely, given a solution to the new problem, we need to find +a non-negative value of $c_j$, but this is easy since $c_j = c' + t$ +and both of these are non-negative. + +Plugging in $t = \max(-b(\vec p), 0)$ can be performed as in +\autoref{s:post}, but, as in the case of offline symmetry detection, +it may be better to provide a direct representation for such +expressions in the internal representation of sets and relations +or at least in a quast-like output format. + +\section{Coalescing}\label{s:coalescing} + +See \textcite{Verdoolaege2015impact} for details on integer set coalescing. + +\section{Transitive Closure} + +\subsection{Introduction} + +\begin{definition}[Power of a Relation] +Let $R \in \Z^n \to 2^{\Z^{d+d}}$ be a relation and +$k \in \Z_{\ge 1}$ +a positive number, then power $k$ of relation $R$ is defined as +\begin{equation} +\label{eq:transitive:power} +R^k \coloneqq +\begin{cases} +R & \text{if $k = 1$} +\\ +R \circ R^{k-1} & \text{if $k \ge 2$} +. +\end{cases} +\end{equation} +\end{definition} + +\begin{definition}[Transitive Closure of a Relation] +Let $R \in \Z^n \to 2^{\Z^{d+d}}$ be a relation, +then the transitive closure $R^+$ of $R$ is the union +of all positive powers of $R$, +$$ +R^+ \coloneqq \bigcup_{k \ge 1} R^k +. +$$ +\end{definition} +Alternatively, the transitive closure may be defined +inductively as +\begin{equation} +\label{eq:transitive:inductive} +R^+ \coloneqq R \cup \left(R \circ R^+\right) +. +\end{equation} + +Since the transitive closure of a polyhedral relation +may no longer be a polyhedral relation \parencite{Kelly1996closure}, +we can, in the general case, only compute an approximation +of the transitive closure. +Whereas \textcite{Kelly1996closure} compute underapproximations, +we, like \textcite{Beletska2009}, compute overapproximations. +That is, given a relation $R$, we will compute a relation $T$ +such that $R^+ \subseteq T$. Of course, we want this approximation +to be as close as possible to the actual transitive closure +$R^+$ and we want to detect the cases where the approximation is +exact, i.e., where $T = R^+$. + +For computing an approximation of the transitive closure of $R$, +we follow the same general strategy as \textcite{Beletska2009} +and first compute an approximation of $R^k$ for $k \ge 1$ and then project +out the parameter $k$ from the resulting relation. + +\begin{example} +As a trivial example, consider the relation +$R = \{\, x \to x + 1 \,\}$. The $k$th power of this map +for arbitrary $k$ is +$$ +R^k = k \mapsto \{\, x \to x + k \mid k \ge 1 \,\} +. +$$ +The transitive closure is then +$$ +\begin{aligned} +R^+ & = \{\, x \to y \mid \exists k \in \Z_{\ge 1} : y = x + k \,\} +\\ +& = \{\, x \to y \mid y \ge x + 1 \,\} +. +\end{aligned} +$$ +\end{example} + +\subsection{Computing an Approximation of $R^k$} +\label{s:power} + +There are some special cases where the computation of $R^k$ is very easy. +One such case is that where $R$ does not compose with itself, +i.e., $R \circ R = \emptyset$ or $\domain R \cap \range R = \emptyset$. +In this case, $R^k$ is only non-empty for $k=1$ where it is equal +to $R$ itself. + +In general, it is impossible to construct a closed form +of $R^k$ as a polyhedral relation. +We will therefore need to make some approximations. +As a first approximations, we will consider each of the basic +relations in $R$ as simply adding one or more offsets to a domain element +to arrive at an image element and ignore the fact that some of these +offsets may only be applied to some of the domain elements. +That is, we will only consider the difference set $\Delta\,R$ of the relation. +In particular, we will first construct a collection $P$ of paths +that move through +a total of $k$ offsets and then intersect domain and range of this +collection with those of $R$. +That is, +\begin{equation} +\label{eq:transitive:approx} +K = P \cap \left(\domain R \to \range R\right) +, +\end{equation} +with +\begin{equation} +\label{eq:transitive:path} +P = \vec s \mapsto \{\, \vec x \to \vec y \mid +\exists k_i \in \Z_{\ge 0}, \vec\delta_i \in k_i \, \Delta_i(\vec s) : +\vec y = \vec x + \sum_i \vec\delta_i +\wedge +\sum_i k_i = k > 0 +\,\} +\end{equation} +and with $\Delta_i$ the basic sets that compose +the difference set $\Delta\,R$. +Note that the number of basic sets $\Delta_i$ need not be +the same as the number of basic relations in $R$. +Also note that since addition is commutative, it does not +matter in which order we add the offsets and so we are allowed +to group them as we did in \eqref{eq:transitive:path}. + +If all the $\Delta_i$s are singleton sets +$\Delta_i = \{\, \vec \delta_i \,\}$ with $\vec \delta_i \in \Z^d$, +then \eqref{eq:transitive:path} simplifies to +\begin{equation} +\label{eq:transitive:singleton} +P = \{\, \vec x \to \vec y \mid +\exists k_i \in \Z_{\ge 0} : +\vec y = \vec x + \sum_i k_i \, \vec \delta_i +\wedge +\sum_i k_i = k > 0 +\,\} +\end{equation} +and then the approximation computed in \eqref{eq:transitive:approx} +is essentially the same as that of \textcite{Beletska2009}. +If some of the $\Delta_i$s are not singleton sets or if +some of $\vec \delta_i$s are parametric, then we need +to resort to further approximations. + +To ease both the exposition and the implementation, we will for +the remainder of this section work with extended offsets +$\Delta_i' = \Delta_i \times \{\, 1 \,\}$. +That is, each offset is extended with an extra coordinate that is +set equal to one. The paths constructed by summing such extended +offsets have the length encoded as the difference of their +final coordinates. The path $P'$ can then be decomposed into +paths $P_i'$, one for each $\Delta_i$, +\begin{equation} +\label{eq:transitive:decompose} +P' = \left( +(P_m' \cup \identity) \circ \cdots \circ +(P_2' \cup \identity) \circ +(P_1' \cup \identity) +\right) \cap +\{\, +\vec x' \to \vec y' \mid y_{d+1} - x_{d+1} = k > 0 +\,\} +, +\end{equation} +with +$$ +P_i' = \vec s \mapsto \{\, \vec x' \to \vec y' \mid +\exists k \in \Z_{\ge 1}, \vec \delta \in k \, \Delta_i'(\vec s) : +\vec y' = \vec x' + \vec \delta +\,\} +. +$$ +Note that each $P_i'$ contains paths of length at least one. +We therefore need to take the union with the identity relation +when composing the $P_i'$s to allow for paths that do not contain +any offsets from one or more $\Delta_i'$. +The path that consists of only identity relations is removed +by imposing the constraint $y_{d+1} - x_{d+1} > 0$. +Taking the union with the identity relation means that +that the relations we compose in \eqref{eq:transitive:decompose} +each consist of two basic relations. If there are $m$ +disjuncts in the input relation, then a direct application +of the composition operation may therefore result in a relation +with $2^m$ disjuncts, which is prohibitively expensive. +It is therefore crucial to apply coalescing (\autoref{s:coalescing}) +after each composition. + +Let us now consider how to compute an overapproximation of $P_i'$. +Those that correspond to singleton $\Delta_i$s are grouped together +and handled as in \eqref{eq:transitive:singleton}. +Note that this is just an optimization. The procedure described +below would produce results that are at least as accurate. +For simplicity, we first assume that no constraint in $\Delta_i'$ +involves any existentially quantified variables. +We will return to existentially quantified variables at the end +of this section. +Without existentially quantified variables, we can classify +the constraints of $\Delta_i'$ as follows +\begin{enumerate} +\item non-parametric constraints +\begin{equation} +\label{eq:transitive:non-parametric} +A_1 \vec x + \vec c_1 \geq \vec 0 +\end{equation} +\item purely parametric constraints +\begin{equation} +\label{eq:transitive:parametric} +B_2 \vec s + \vec c_2 \geq \vec 0 +\end{equation} +\item negative mixed constraints +\begin{equation} +\label{eq:transitive:mixed} +A_3 \vec x + B_3 \vec s + \vec c_3 \geq \vec 0 +\end{equation} +such that for each row $j$ and for all $\vec s$, +$$ +\Delta_i'(\vec s) \cap +\{\, \vec \delta' \mid B_{3,j} \vec s + c_{3,j} > 0 \,\} += \emptyset +$$ +\item positive mixed constraints +$$ +A_4 \vec x + B_4 \vec s + \vec c_4 \geq \vec 0 +$$ +such that for each row $j$, there is at least one $\vec s$ such that +$$ +\Delta_i'(\vec s) \cap +\{\, \vec \delta' \mid B_{4,j} \vec s + c_{4,j} > 0 \,\} +\ne \emptyset +$$ +\end{enumerate} +We will use the following approximation $Q_i$ for $P_i'$: +\begin{equation} +\label{eq:transitive:Q} +\begin{aligned} +Q_i = \vec s \mapsto +\{\, +\vec x' \to \vec y' +\mid {} & \exists k \in \Z_{\ge 1}, \vec f \in \Z^d : +\vec y' = \vec x' + (\vec f, k) +\wedge {} +\\ +& +A_1 \vec f + k \vec c_1 \geq \vec 0 +\wedge +B_2 \vec s + \vec c_2 \geq \vec 0 +\wedge +A_3 \vec f + B_3 \vec s + \vec c_3 \geq \vec 0 +\,\} +. +\end{aligned} +\end{equation} +To prove that $Q_i$ is indeed an overapproximation of $P_i'$, +we need to show that for every $\vec s \in \Z^n$, for every +$k \in \Z_{\ge 1}$ and for every $\vec f \in k \, \Delta_i(\vec s)$ +we have that +$(\vec f, k)$ satisfies the constraints in \eqref{eq:transitive:Q}. +If $\Delta_i(\vec s)$ is non-empty, then $\vec s$ must satisfy +the constraints in \eqref{eq:transitive:parametric}. +Each element $(\vec f, k) \in k \, \Delta_i'(\vec s)$ is a sum +of $k$ elements $(\vec f_j, 1)$ in $\Delta_i'(\vec s)$. +Each of these elements satisfies the constraints in +\eqref{eq:transitive:non-parametric}, i.e., +$$ +\left[ +\begin{matrix} +A_1 & \vec c_1 +\end{matrix} +\right] +\left[ +\begin{matrix} +\vec f_j \\ 1 +\end{matrix} +\right] +\ge \vec 0 +. +$$ +The sum of these elements therefore satisfies the same set of inequalities, +i.e., $A_1 \vec f + k \vec c_1 \geq \vec 0$. +Finally, the constraints in \eqref{eq:transitive:mixed} are such +that for any $\vec s$ in the parameter domain of $\Delta$, +we have $-\vec r(\vec s) \coloneqq B_3 \vec s + \vec c_3 \le \vec 0$, +i.e., $A_3 \vec f_j \ge \vec r(\vec s) \ge \vec 0$ +and therefore also $A_3 \vec f \ge \vec r(\vec s)$. +Note that if there are no mixed constraints and if the +rational relaxation of $\Delta_i(\vec s)$, i.e., +$\{\, \vec x \in \Q^d \mid A_1 \vec x + \vec c_1 \ge \vec 0\,\}$, +has integer vertices, then the approximation is exact, i.e., +$Q_i = P_i'$. In this case, the vertices of $\Delta'_i(\vec s)$ +generate the rational cone +$\{\, \vec x' \in \Q^{d+1} \mid \left[ +\begin{matrix} +A_1 & \vec c_1 +\end{matrix} +\right] \vec x' \,\}$ and therefore $\Delta'_i(\vec s)$ is +a Hilbert basis of this cone \parencite[Theorem~16.4]{Schrijver1986}. + +Note however that, as pointed out by \textcite{DeSmet2010personal}, +if there \emph{are} any mixed constraints, then the above procedure may +not compute the most accurate affine approximation of +$k \, \Delta_i(\vec s)$ with $k \ge 1$. +In particular, we only consider the negative mixed constraints that +happen to appear in the description of $\Delta_i(\vec s)$, while we +should instead consider \emph{all} valid such constraints. +It is also sufficient to consider those constraints because any +constraint that is valid for $k \, \Delta_i(\vec s)$ is also +valid for $1 \, \Delta_i(\vec s) = \Delta_i(\vec s)$. +Take therefore any constraint +$\spv a x + \spv b s + c \ge 0$ valid for $\Delta_i(\vec s)$. +This constraint is also valid for $k \, \Delta_i(\vec s)$ iff +$k \, \spv a x + \spv b s + c \ge 0$. +If $\spv b s + c$ can attain any positive value, then $\spv a x$ +may be negative for some elements of $\Delta_i(\vec s)$. +We then have $k \, \spv a x < \spv a x$ for $k > 1$ and so the constraint +is not valid for $k \, \Delta_i(\vec s)$. +We therefore need to impose $\spv b s + c \le 0$ for all values +of $\vec s$ such that $\Delta_i(\vec s)$ is non-empty, i.e., +$\vec b$ and $c$ need to be such that $- \spv b s - c \ge 0$ is a valid +constraint of $\Delta_i(\vec s)$. That is, $(\vec b, c)$ are the opposites +of the coefficients of a valid constraint of $\Delta_i(\vec s)$. +The approximation of $k \, \Delta_i(\vec s)$ can therefore be obtained +using three applications of Farkas' lemma. The first obtains the coefficients +of constraints valid for $\Delta_i(\vec s)$. The second obtains +the coefficients of constraints valid for the projection of $\Delta_i(\vec s)$ +onto the parameters. The opposite of the second set is then computed +and intersected with the first set. The result is the set of coefficients +of constraints valid for $k \, \Delta_i(\vec s)$. A final application +of Farkas' lemma is needed to obtain the approximation of +$k \, \Delta_i(\vec s)$ itself. + +\begin{example} +Consider the relation +$$ +n \to \{\, (x, y) \to (1 + x, 1 - n + y) \mid n \ge 2 \,\} +. +$$ +The difference set of this relation is +$$ +\Delta = n \to \{\, (1, 1 - n) \mid n \ge 2 \,\} +. +$$ +Using our approach, we would only consider the mixed constraint +$y - 1 + n \ge 0$, leading to the following approximation of the +transitive closure: +$$ +n \to \{\, (x, y) \to (o_0, o_1) \mid n \ge 2 \wedge o_1 \le 1 - n + y \wedge o_0 \ge 1 + x \,\} +. +$$ +If, instead, we apply Farkas's lemma to $\Delta$, i.e., +\begin{verbatim} +D := [n] -> { [1, 1 - n] : n >= 2 }; +CD := coefficients D; +CD; +\end{verbatim} +we obtain +\begin{verbatim} +{ rat: coefficients[[c_cst, c_n] -> [i2, i3]] : i3 <= c_n and + i3 <= c_cst + 2c_n + i2 } +\end{verbatim} +The pure-parametric constraints valid for $\Delta$, +\begin{verbatim} +P := { [a,b] -> [] }(D); +CP := coefficients P; +CP; +\end{verbatim} +are +\begin{verbatim} +{ rat: coefficients[[c_cst, c_n] -> []] : c_n >= 0 and 2c_n >= -c_cst } +\end{verbatim} +Negating these coefficients and intersecting with \verb+CD+, +\begin{verbatim} +NCP := { rat: coefficients[[a,b] -> []] + -> coefficients[[-a,-b] -> []] }(CP); +CK := wrap((unwrap CD) * (dom (unwrap NCP))); +CK; +\end{verbatim} +we obtain +\begin{verbatim} +{ rat: [[c_cst, c_n] -> [i2, i3]] : i3 <= c_n and + i3 <= c_cst + 2c_n + i2 and c_n <= 0 and 2c_n <= -c_cst } +\end{verbatim} +The approximation for $k\,\Delta$, +\begin{verbatim} +K := solutions CK; +K; +\end{verbatim} +is then +\begin{verbatim} +[n] -> { rat: [i0, i1] : i1 <= -i0 and i0 >= 1 and i1 <= 2 - n - i0 } +\end{verbatim} +Finally, the computed approximation for $R^+$, +\begin{verbatim} +T := unwrap({ [dx,dy] -> [[x,y] -> [x+dx,y+dy]] }(K)); +R := [n] -> { [x,y] -> [x+1,y+1-n] : n >= 2 }; +T := T * ((dom R) -> (ran R)); +T; +\end{verbatim} +is +\begin{verbatim} +[n] -> { [x, y] -> [o0, o1] : o1 <= x + y - o0 and + o0 >= 1 + x and o1 <= 2 - n + x + y - o0 and n >= 2 } +\end{verbatim} +\end{example} + +Existentially quantified variables can be handled by +classifying them into variables that are uniquely +determined by the parameters, variables that are independent +of the parameters and others. The first set can be treated +as parameters and the second as variables. Constraints involving +the other existentially quantified variables are removed. + +\begin{example} +Consider the relation +$$ +R = +n \to \{\, x \to y \mid \exists \, \alpha_0, \alpha_1: 7\alpha_0 = -2 + n \wedge 5\alpha_1 = -1 - x + y \wedge y \ge 6 + x \,\} +. +$$ +The difference set of this relation is +$$ +\Delta = \Delta \, R = +n \to \{\, x \mid \exists \, \alpha_0, \alpha_1: 7\alpha_0 = -2 + n \wedge 5\alpha_1 = -1 + x \wedge x \ge 6 \,\} +. +$$ +The existentially quantified variables can be defined in terms +of the parameters and variables as +$$ +\alpha_0 = \floor{\frac{-2 + n}7} +\qquad +\text{and} +\qquad +\alpha_1 = \floor{\frac{-1 + x}5} +. +$$ +$\alpha_0$ can therefore be treated as a parameter, +while $\alpha_1$ can be treated as a variable. +This in turn means that $7\alpha_0 = -2 + n$ can be treated as +a purely parametric constraint, while the other two constraints are +non-parametric. +The corresponding $Q$~\eqref{eq:transitive:Q} is therefore +$$ +\begin{aligned} +n \to \{\, (x,z) \to (y,w) \mid +\exists\, \alpha_0, \alpha_1, k, f : {} & +k \ge 1 \wedge +y = x + f \wedge +w = z + k \wedge {} \\ +& +7\alpha_0 = -2 + n \wedge +5\alpha_1 = -k + x \wedge +x \ge 6 k +\,\} +. +\end{aligned} +$$ +Projecting out the final coordinates encoding the length of the paths, +results in the exact transitive closure +$$ +R^+ = +n \to \{\, x \to y \mid \exists \, \alpha_0, \alpha_1: 7\alpha_1 = -2 + n \wedge 6\alpha_0 \ge -x + y \wedge 5\alpha_0 \le -1 - x + y \,\} +. +$$ +\end{example} + +The fact that we ignore some impure constraints clearly leads +to a loss of accuracy. In some cases, some of this loss can be recovered +by not considering the parameters in a special way. +That is, instead of considering the set +$$ +\Delta = \diff R = +\vec s \mapsto +\{\, \vec \delta \in \Z^{d} \mid \exists \vec x \to \vec y \in R : +\vec \delta = \vec y - \vec x +\,\} +$$ +we consider the set +$$ +\Delta' = \diff R' = +\{\, \vec \delta \in \Z^{n+d} \mid \exists +(\vec s, \vec x) \to (\vec s, \vec y) \in R' : +\vec \delta = (\vec s - \vec s, \vec y - \vec x) +\,\} +. +$$ +The first $n$ coordinates of every element in $\Delta'$ are zero. +Projecting out these zero coordinates from $\Delta'$ is equivalent +to projecting out the parameters in $\Delta$. +The result is obviously a superset of $\Delta$, but all its constraints +are of type \eqref{eq:transitive:non-parametric} and they can therefore +all be used in the construction of $Q_i$. + +\begin{example} +Consider the relation +$$ +% [n] -> { [x, y] -> [1 + x, 1 - n + y] | n >= 2 } +R = n \to \{\, (x, y) \to (1 + x, 1 - n + y) \mid n \ge 2 \,\} +. +$$ +We have +$$ +\diff R = n \to \{\, (1, 1 - n) \mid n \ge 2 \,\} +$$ +and so, by treating the parameters in a special way, we obtain +the following approximation for $R^+$: +$$ +n \to \{\, (x, y) \to (x', y') \mid n \ge 2 \wedge y' \le 1 - n + y \wedge x' \ge 1 + x \,\} +. +$$ +If we consider instead +$$ +R' = \{\, (n, x, y) \to (n, 1 + x, 1 - n + y) \mid n \ge 2 \,\} +$$ +then +$$ +\diff R' = \{\, (0, 1, y) \mid y \le -1 \,\} +$$ +and we obtain the approximation +$$ +n \to \{\, (x, y) \to (x', y') \mid n \ge 2 \wedge x' \ge 1 + x \wedge y' \le x + y - x' \,\} +. +$$ +If we consider both $\diff R$ and $\diff R'$, then we obtain +$$ +n \to \{\, (x, y) \to (x', y') \mid n \ge 2 \wedge y' \le 1 - n + y \wedge x' \ge 1 + x \wedge y' \le x + y - x' \,\} +. +$$ +Note, however, that this is not the most accurate affine approximation that +can be obtained. That would be +$$ +n \to \{\, (x, y) \to (x', y') \mid y' \le 2 - n + x + y - x' \wedge n \ge 2 \wedge x' \ge 1 + x \,\} +. +$$ +\end{example} + +\subsection{Checking Exactness} + +The approximation $T$ for the transitive closure $R^+$ can be obtained +by projecting out the parameter $k$ from the approximation $K$ +\eqref{eq:transitive:approx} of the power $R^k$. +Since $K$ is an overapproximation of $R^k$, $T$ will also be an +overapproximation of $R^+$. +To check whether the results are exact, we need to consider two +cases depending on whether $R$ is {\em cyclic}, where $R$ is defined +to be cyclic if $R^+$ maps any element to itself, i.e., +$R^+ \cap \identity \ne \emptyset$. +If $R$ is acyclic, then the inductive definition of +\eqref{eq:transitive:inductive} is equivalent to its completion, +i.e., +$$ +R^+ = R \cup \left(R \circ R^+\right) +$$ +is a defining property. +Since $T$ is known to be an overapproximation, we only need to check +whether +$$ +T \subseteq R \cup \left(R \circ T\right) +. +$$ +This is essentially Theorem~5 of \textcite{Kelly1996closure}. +The only difference is that they only consider lexicographically +forward relations, a special case of acyclic relations. + +If, on the other hand, $R$ is cyclic, then we have to resort +to checking whether the approximation $K$ of the power is exact. +Note that $T$ may be exact even if $K$ is not exact, so the check +is sound, but incomplete. +To check exactness of the power, we simply need to check +\eqref{eq:transitive:power}. Since again $K$ is known +to be an overapproximation, we only need to check whether +$$ +\begin{aligned} +K'|_{y_{d+1} - x_{d+1} = 1} & \subseteq R' +\\ +K'|_{y_{d+1} - x_{d+1} \ge 2} & \subseteq R' \circ K'|_{y_{d+1} - x_{d+1} \ge 1} +, +\end{aligned} +$$ +where $R' = \{\, \vec x' \to \vec y' \mid \vec x \to \vec y \in R +\wedge y_{d+1} - x_{d+1} = 1\,\}$, i.e., $R$ extended with path +lengths equal to 1. + +All that remains is to explain how to check the cyclicity of $R$. +Note that the exactness on the power is always sound, even +in the acyclic case, so we only need to be careful that we find +all cyclic cases. Now, if $R$ is cyclic, i.e., +$R^+ \cap \identity \ne \emptyset$, then, since $T$ is +an overapproximation of $R^+$, also +$T \cap \identity \ne \emptyset$. This in turn means +that $\Delta \, K'$ contains a point whose first $d$ coordinates +are zero and whose final coordinate is positive. +In the implementation we currently perform this test on $P'$ instead of $K'$. +Note that if $R^+$ is acyclic and $T$ is not, then the approximation +is clearly not exact and the approximation of the power $K$ +will not be exact either. + +\subsection{Decomposing $R$ into strongly connected components} + +If the input relation $R$ is a union of several basic relations +that can be partially ordered +then the accuracy of the approximation may be improved by computing +an approximation of each strongly connected components separately. +For example, if $R = R_1 \cup R_2$ and $R_1 \circ R_2 = \emptyset$, +then we know that any path that passes through $R_2$ cannot later +pass through $R_1$, i.e., +\begin{equation} +\label{eq:transitive:components} +R^+ = R_1^+ \cup R_2^+ \cup \left(R_2^+ \circ R_1^+\right) +. +\end{equation} +We can therefore compute (approximations of) transitive closures +of $R_1$ and $R_2$ separately. +Note, however, that the condition $R_1 \circ R_2 = \emptyset$ +is actually too strong. +If $R_1 \circ R_2$ is a subset of $R_2 \circ R_1$ +then we can reorder the segments +in any path that moves through both $R_1$ and $R_2$ to +first move through $R_1$ and then through $R_2$. + +This idea can be generalized to relations that are unions +of more than two basic relations by constructing the +strongly connected components in the graph with as vertices +the basic relations and an edge between two basic relations +$R_i$ and $R_j$ if $R_i$ needs to follow $R_j$ in some paths. +That is, there is an edge from $R_i$ to $R_j$ iff +\begin{equation} +\label{eq:transitive:edge} +R_i \circ R_j +\not\subseteq +R_j \circ R_i +. +\end{equation} +The components can be obtained from the graph by applying +Tarjan's algorithm \parencite{Tarjan1972}. + +In practice, we compute the (extended) powers $K_i'$ of each component +separately and then compose them as in \eqref{eq:transitive:decompose}. +Note, however, that in this case the order in which we apply them is +important and should correspond to a topological ordering of the +strongly connected components. Simply applying Tarjan's +algorithm will produce topologically sorted strongly connected components. +The graph on which Tarjan's algorithm is applied is constructed on-the-fly. +That is, whenever the algorithm checks if there is an edge between +two vertices, we evaluate \eqref{eq:transitive:edge}. +The exactness check is performed on each component separately. +If the approximation turns out to be inexact for any of the components, +then the entire result is marked inexact and the exactness check +is skipped on the components that still need to be handled. + +It should be noted that \eqref{eq:transitive:components} +is only valid for exact transitive closures. +If overapproximations are computed in the right hand side, then the result will +still be an overapproximation of the left hand side, but this result +may not be transitively closed. If we only separate components based +on the condition $R_i \circ R_j = \emptyset$, then there is no problem, +as this condition will still hold on the computed approximations +of the transitive closures. If, however, we have exploited +\eqref{eq:transitive:edge} during the decomposition and if the +result turns out not to be exact, then we check whether +the result is transitively closed. If not, we recompute +the transitive closure, skipping the decomposition. +Note that testing for transitive closedness on the result may +be fairly expensive, so we may want to make this check +configurable. + +\begin{figure} +\begin{center} +\begin{tikzpicture}[x=0.5cm,y=0.5cm,>=stealth,shorten >=1pt] +\foreach \x in {1,...,10}{ + \foreach \y in {1,...,10}{ + \draw[->] (\x,\y) -- (\x,\y+1); + } +} +\foreach \x in {1,...,20}{ + \foreach \y in {5,...,15}{ + \draw[->] (\x,\y) -- (\x+1,\y); + } +} +\end{tikzpicture} +\end{center} +\caption{The relation from \autoref{ex:closure4}} +\label{f:closure4} +\end{figure} +\begin{example} +\label{ex:closure4} +Consider the relation in example {\tt closure4} that comes with +the Omega calculator~\parencite{Omega_calc}, $R = R_1 \cup R_2$, +with +$$ +\begin{aligned} +R_1 & = \{\, (x,y) \to (x,y+1) \mid 1 \le x,y \le 10 \,\} +\\ +R_2 & = \{\, (x,y) \to (x+1,y) \mid 1 \le x \le 20 \wedge 5 \le y \le 15 \,\} +. +\end{aligned} +$$ +This relation is shown graphically in \autoref{f:closure4}. +We have +$$ +\begin{aligned} +R_1 \circ R_2 &= +\{\, (x,y) \to (x+1,y+1) \mid 1 \le x \le 9 \wedge 5 \le y \le 10 \,\} +\\ +R_2 \circ R_1 &= +\{\, (x,y) \to (x+1,y+1) \mid 1 \le x \le 10 \wedge 4 \le y \le 10 \,\} +. +\end{aligned} +$$ +Clearly, $R_1 \circ R_2 \subseteq R_2 \circ R_1$ and so +$$ +\left( +R_1 \cup R_2 +\right)^+ += +\left(R_2^+ \circ R_1^+\right) +\cup R_1^+ +\cup R_2^+ +. +$$ +\end{example} + +\begin{figure} +\newcounter{n} +\newcounter{t1} +\newcounter{t2} +\newcounter{t3} +\newcounter{t4} +\begin{center} +\begin{tikzpicture}[>=stealth,shorten >=1pt] +\setcounter{n}{7} +\foreach \i in {1,...,\value{n}}{ + \foreach \j in {1,...,\value{n}}{ + \setcounter{t1}{2 * \j - 4 - \i + 1} + \setcounter{t2}{\value{n} - 3 - \i + 1} + \setcounter{t3}{2 * \i - 1 - \j + 1} + \setcounter{t4}{\value{n} - \j + 1} + \ifnum\value{t1}>0\ifnum\value{t2}>0 + \ifnum\value{t3}>0\ifnum\value{t4}>0 + \draw[thick,->] (\i,\j) to[out=20] (\i+3,\j); + \fi\fi\fi\fi + \setcounter{t1}{2 * \j - 1 - \i + 1} + \setcounter{t2}{\value{n} - \i + 1} + \setcounter{t3}{2 * \i - 4 - \j + 1} + \setcounter{t4}{\value{n} - 3 - \j + 1} + \ifnum\value{t1}>0\ifnum\value{t2}>0 + \ifnum\value{t3}>0\ifnum\value{t4}>0 + \draw[thick,->] (\i,\j) to[in=-20,out=20] (\i,\j+3); + \fi\fi\fi\fi + \setcounter{t1}{2 * \j - 1 - \i + 1} + \setcounter{t2}{\value{n} - 1 - \i + 1} + \setcounter{t3}{2 * \i - 1 - \j + 1} + \setcounter{t4}{\value{n} - 1 - \j + 1} + \ifnum\value{t1}>0\ifnum\value{t2}>0 + \ifnum\value{t3}>0\ifnum\value{t4}>0 + \draw[thick,->] (\i,\j) to (\i+1,\j+1); + \fi\fi\fi\fi + } +} +\end{tikzpicture} +\end{center} +\caption{The relation from \autoref{ex:decomposition}} +\label{f:decomposition} +\end{figure} +\begin{example} +\label{ex:decomposition} +Consider the relation on the right of \textcite[Figure~2]{Beletska2009}, +reproduced in \autoref{f:decomposition}. +The relation can be described as $R = R_1 \cup R_2 \cup R_3$, +with +$$ +\begin{aligned} +R_1 &= n \mapsto \{\, (i,j) \to (i+3,j) \mid +i \le 2 j - 4 \wedge +i \le n - 3 \wedge +j \le 2 i - 1 \wedge +j \le n \,\} +\\ +R_2 &= n \mapsto \{\, (i,j) \to (i,j+3) \mid +i \le 2 j - 1 \wedge +i \le n \wedge +j \le 2 i - 4 \wedge +j \le n - 3 \,\} +\\ +R_3 &= n \mapsto \{\, (i,j) \to (i+1,j+1) \mid +i \le 2 j - 1 \wedge +i \le n - 1 \wedge +j \le 2 i - 1 \wedge +j \le n - 1\,\} +. +\end{aligned} +$$ +The figure shows this relation for $n = 7$. +Both +$R_3 \circ R_1 \subseteq R_1 \circ R_3$ +and +$R_3 \circ R_2 \subseteq R_2 \circ R_3$, +which the reader can verify using the {\tt iscc} calculator: +\begin{verbatim} +R1 := [n] -> { [i,j] -> [i+3,j] : i <= 2 j - 4 and i <= n - 3 and + j <= 2 i - 1 and j <= n }; +R2 := [n] -> { [i,j] -> [i,j+3] : i <= 2 j - 1 and i <= n and + j <= 2 i - 4 and j <= n - 3 }; +R3 := [n] -> { [i,j] -> [i+1,j+1] : i <= 2 j - 1 and i <= n - 1 and + j <= 2 i - 1 and j <= n - 1 }; +(R1 . R3) - (R3 . R1); +(R2 . R3) - (R3 . R2); +\end{verbatim} +$R_3$ can therefore be moved forward in any path. +For the other two basic relations, we have both +$R_2 \circ R_1 \not\subseteq R_1 \circ R_2$ +and +$R_1 \circ R_2 \not\subseteq R_2 \circ R_1$ +and so $R_1$ and $R_2$ form a strongly connected component. +By computing the power of $R_3$ and $R_1 \cup R_2$ separately +and composing the results, the power of $R$ can be computed exactly +using \eqref{eq:transitive:singleton}. +As explained by \textcite{Beletska2009}, applying the same formula +to $R$ directly, without a decomposition, would result in +an overapproximation of the power. +\end{example} + +\subsection{Partitioning the domains and ranges of $R$} + +The algorithm of \autoref{s:power} assumes that the input relation $R$ +can be treated as a union of translations. +This is a reasonable assumption if $R$ maps elements of a given +abstract domain to the same domain. +However, if $R$ is a union of relations that map between different +domains, then this assumption no longer holds. +In particular, when an entire dependence graph is encoded +in a single relation, as is done by, e.g., +\textcite[Section~6.1]{Barthou2000MSE}, then it does not make +sense to look at differences between iterations of different domains. +Now, arguably, a modified Floyd-Warshall algorithm should +be applied to the dependence graph, as advocated by +\textcite{Kelly1996closure}, with the transitive closure operation +only being applied to relations from a given domain to itself. +However, it is also possible to detect disjoint domains and ranges +and to apply Floyd-Warshall internally. + +\LinesNumbered +\begin{algorithm} +\caption{The modified Floyd-Warshall algorithm of +\protect\textcite{Kelly1996closure}} +\label{a:Floyd} +\SetKwInput{Input}{Input} +\SetKwInput{Output}{Output} +\Input{Relations $R_{pq}$, $0 \le p, q < n$} +\Output{Updated relations $R_{pq}$ such that each relation +$R_{pq}$ contains all indirect paths from $p$ to $q$ in the input graph} +% +\BlankLine +\SetAlgoVlined +\DontPrintSemicolon +% +\For{$r \in [0, n-1]$}{ + $R_{rr} \coloneqq R_{rr}^+$ \nllabel{l:Floyd:closure}\; + \For{$p \in [0, n-1]$}{ + \For{$q \in [0, n-1]$}{ + \If{$p \ne r$ or $q \ne r$}{ + $R_{pq} \coloneqq R_{pq} \cup \left(R_{rq} \circ R_{pr}\right) + \cup \left(R_{rq} \circ R_{rr} \circ R_{pr}\right)$ + \nllabel{l:Floyd:update} + } + } + } +} +\end{algorithm} + +Let the input relation $R$ be a union of $m$ basic relations $R_i$. +Let $D_{2i}$ be the domains of $R_i$ and $D_{2i+1}$ the ranges of $R_i$. +The first step is to group overlapping $D_j$ until a partition is +obtained. If the resulting partition consists of a single part, +then we continue with the algorithm of \autoref{s:power}. +Otherwise, we apply Floyd-Warshall on the graph with as vertices +the parts of the partition and as edges the $R_i$ attached to +the appropriate pairs of vertices. +In particular, let there be $n$ parts $P_k$ in the partition. +We construct $n^2$ relations +$$ +R_{pq} \coloneqq \bigcup_{i \text{ s.t. } \domain R_i \subseteq P_p \wedge + \range R_i \subseteq P_q} R_i +, +$$ +apply \autoref{a:Floyd} and return the union of all resulting +$R_{pq}$ as the transitive closure of $R$. +Each iteration of the $r$-loop in \autoref{a:Floyd} updates +all relations $R_{pq}$ to include paths that go from $p$ to $r$, +possibly stay there for a while, and then go from $r$ to $q$. +Note that paths that ``stay in $r$'' include all paths that +pass through earlier vertices since $R_{rr}$ itself has been updated +accordingly in previous iterations of the outer loop. +In principle, it would be sufficient to use the $R_{pr}$ +and $R_{rq}$ computed in the previous iteration of the +$r$-loop in Line~\ref{l:Floyd:update}. +However, from an implementation perspective, it is easier +to allow either or both of these to have been updated +in the same iteration of the $r$-loop. +This may result in duplicate paths, but these can usually +be removed by coalescing (\autoref{s:coalescing}) the result of the union +in Line~\ref{l:Floyd:update}, which should be done in any case. +The transitive closure in Line~\ref{l:Floyd:closure} +is performed using a recursive call. This recursive call +includes the partitioning step, but the resulting partition will +usually be a singleton. +The result of the recursive call will either be exact or an +overapproximation. The final result of Floyd-Warshall is therefore +also exact or an overapproximation. + +\begin{figure} +\begin{center} +\begin{tikzpicture}[x=1cm,y=1cm,>=stealth,shorten >=3pt] +\foreach \x/\y in {0/0,1/1,3/2} { + \fill (\x,\y) circle (2pt); +} +\foreach \x/\y in {0/1,2/2,3/3} { + \draw (\x,\y) circle (2pt); +} +\draw[->] (0,0) -- (0,1); +\draw[->] (0,1) -- (1,1); +\draw[->] (2,2) -- (3,2); +\draw[->] (3,2) -- (3,3); +\draw[->,dashed] (2,2) -- (3,3); +\draw[->,dotted] (0,0) -- (1,1); +\end{tikzpicture} +\end{center} +\caption{The relation (solid arrows) on the right of Figure~1 of +\protect\textcite{Beletska2009} and its transitive closure} +\label{f:COCOA:1} +\end{figure} +\begin{example} +Consider the relation on the right of Figure~1 of +\textcite{Beletska2009}, +reproduced in \autoref{f:COCOA:1}. +This relation can be described as +$$ +\begin{aligned} +\{\, (x, y) \to (x_2, y_2) \mid {} & (3y = 2x \wedge x_2 = x \wedge 3y_2 = 3 + 2x \wedge x \ge 0 \wedge x \le 3) \vee {} \\ +& (x_2 = 1 + x \wedge y_2 = y \wedge x \ge 0 \wedge 3y \ge 2 + 2x \wedge x \le 2 \wedge 3y \le 3 + 2x) \,\} +. +\end{aligned} +$$ +Note that the domain of the upward relation overlaps with the range +of the rightward relation and vice versa, but that the domain +of neither relation overlaps with its own range or the domain of +the other relation. +The domains and ranges can therefore be partitioned into two parts, +$P_0$ and $P_1$, shown as the white and black dots in \autoref{f:COCOA:1}, +respectively. +Initially, we have +$$ +\begin{aligned} +R_{00} & = \emptyset +\\ +R_{01} & = +\{\, (x, y) \to (x+1, y) \mid +(x \ge 0 \wedge 3y \ge 2 + 2x \wedge x \le 2 \wedge 3y \le 3 + 2x) \,\} +\\ +R_{10} & = +\{\, (x, y) \to (x_2, y_2) \mid (3y = 2x \wedge x_2 = x \wedge 3y_2 = 3 + 2x \wedge x \ge 0 \wedge x \le 3) \,\} +\\ +R_{11} & = \emptyset +. +\end{aligned} +$$ +In the first iteration, $R_{00}$ remains the same ($\emptyset^+ = \emptyset$). +$R_{01}$ and $R_{10}$ are therefore also unaffected, but +$R_{11}$ is updated to include $R_{01} \circ R_{10}$, i.e., +the dashed arrow in the figure. +This new $R_{11}$ is obviously transitively closed, so it is not +changed in the second iteration and it does not have an effect +on $R_{01}$ and $R_{10}$. However, $R_{00}$ is updated to +include $R_{10} \circ R_{01}$, i.e., the dotted arrow in the figure. +The transitive closure of the original relation is then equal to +$R_{00} \cup R_{01} \cup R_{10} \cup R_{11}$. +\end{example} + +\subsection{Incremental Computation} +\label{s:incremental} + +In some cases it is possible and useful to compute the transitive closure +of union of basic relations incrementally. In particular, +if $R$ is a union of $m$ basic maps, +$$ +R = \bigcup_j R_j +, +$$ +then we can pick some $R_i$ and compute the transitive closure of $R$ as +\begin{equation} +\label{eq:transitive:incremental} +R^+ = R_i^+ \cup +\left( +\bigcup_{j \ne i} +R_i^* \circ R_j \circ R_i^* +\right)^+ +. +\end{equation} +For this approach to be successful, it is crucial that each +of the disjuncts in the argument of the second transitive +closure in \eqref{eq:transitive:incremental} be representable +as a single basic relation, i.e., without a union. +If this condition holds, then by using \eqref{eq:transitive:incremental}, +the number of disjuncts in the argument of the transitive closure +can be reduced by one. +Now, $R_i^* = R_i^+ \cup \identity$, but in some cases it is possible +to relax the constraints of $R_i^+$ to include part of the identity relation, +say on domain $D$. We will use the notation +${\cal C}(R_i,D) = R_i^+ \cup \identity_D$ to represent +this relaxed version of $R^+$. +\textcite{Kelly1996closure} use the notation $R_i^?$. +${\cal C}(R_i,D)$ can be computed by allowing $k$ to attain +the value $0$ in \eqref{eq:transitive:Q} and by using +$$ +P \cap \left(D \to D\right) +$$ +instead of \eqref{eq:transitive:approx}. +Typically, $D$ will be a strict superset of both $\domain R_i$ +and $\range R_i$. We therefore need to check that domain +and range of the transitive closure are part of ${\cal C}(R_i,D)$, +i.e., the part that results from the paths of positive length ($k \ge 1$), +are equal to the domain and range of $R_i$. +If not, then the incremental approach cannot be applied for +the given choice of $R_i$ and $D$. + +In order to be able to replace $R^*$ by ${\cal C}(R_i,D)$ +in \eqref{eq:transitive:incremental}, $D$ should be chosen +to include both $\domain R$ and $\range R$, i.e., such +that $\identity_D \circ R_j \circ \identity_D = R_j$ for all $j\ne i$. +\textcite{Kelly1996closure} say that they use +$D = \domain R_i \cup \range R_i$, but presumably they mean that +they use $D = \domain R \cup \range R$. +Now, this expression of $D$ contains a union, so it not directly usable. +\textcite{Kelly1996closure} do not explain how they avoid this union. +Apparently, in their implementation, +they are using the convex hull of $\domain R \cup \range R$ +or at least an approximation of this convex hull. +We use the simple hull (\autoref{s:simple hull}) of $\domain R \cup \range R$. + +It is also possible to use a domain $D$ that does {\em not\/} +include $\domain R \cup \range R$, but then we have to +compose with ${\cal C}(R_i,D)$ more selectively. +In particular, if we have +\begin{equation} +\label{eq:transitive:right} +\text{for each $j \ne i$ either } +\domain R_j \subseteq D \text{ or } \domain R_j \cap \range R_i = \emptyset +\end{equation} +and, similarly, +\begin{equation} +\label{eq:transitive:left} +\text{for each $j \ne i$ either } +\range R_j \subseteq D \text{ or } \range R_j \cap \domain R_i = \emptyset +\end{equation} +then we can refine \eqref{eq:transitive:incremental} to +$$ +R_i^+ \cup +\left( +\left( +\bigcup_{\shortstack{$\scriptstyle\domain R_j \subseteq D $\\ + $\scriptstyle\range R_j \subseteq D$}} +{\cal C} \circ R_j \circ {\cal C} +\right) +\cup +\left( +\bigcup_{\shortstack{$\scriptstyle\domain R_j \cap \range R_i = \emptyset$\\ + $\scriptstyle\range R_j \subseteq D$}} +\!\!\!\!\! +{\cal C} \circ R_j +\right) +\cup +\left( +\bigcup_{\shortstack{$\scriptstyle\domain R_j \subseteq D $\\ + $\scriptstyle\range R_j \cap \domain R_i = \emptyset$}} +\!\!\!\!\! +R_j \circ {\cal C} +\right) +\cup +\left( +\bigcup_{\shortstack{$\scriptstyle\domain R_j \cap \range R_i = \emptyset$\\ + $\scriptstyle\range R_j \cap \domain R_i = \emptyset$}} +\!\!\!\!\! +R_j +\right) +\right)^+ +. +$$ +If only property~\eqref{eq:transitive:right} holds, +we can use +$$ +R_i^+ \cup +\left( +\left( +R_i^+ \cup \identity +\right) +\circ +\left( +\left( +\bigcup_{\shortstack{$\scriptstyle\domain R_j \subseteq D $}} +R_j \circ {\cal C} +\right) +\cup +\left( +\bigcup_{\shortstack{$\scriptstyle\domain R_j \cap \range R_i = \emptyset$}} +\!\!\!\!\! +R_j +\right) +\right)^+ +\right) +, +$$ +while if only property~\eqref{eq:transitive:left} holds, +we can use +$$ +R_i^+ \cup +\left( +\left( +\left( +\bigcup_{\shortstack{$\scriptstyle\range R_j \subseteq D $}} +{\cal C} \circ R_j +\right) +\cup +\left( +\bigcup_{\shortstack{$\scriptstyle\range R_j \cap \domain R_i = \emptyset$}} +\!\!\!\!\! +R_j +\right) +\right)^+ +\circ +\left( +R_i^+ \cup \identity +\right) +\right) +. +$$ + +It should be noted that if we want the result of the incremental +approach to be transitively closed, then we can only apply it +if all of the transitive closure operations involved are exact. +If, say, the second transitive closure in \eqref{eq:transitive:incremental} +contains extra elements, then the result does not necessarily contain +the composition of these extra elements with powers of $R_i$. + +\subsection{An {\tt Omega}-like implementation} + +While the main algorithm of \textcite{Kelly1996closure} is +designed to compute and underapproximation of the transitive closure, +the authors mention that they could also compute overapproximations. +In this section, we describe our implementation of an algorithm +that is based on their ideas. +Note that the {\tt Omega} library computes underapproximations +\parencite[Section 6.4]{Omega_lib}. + +The main tool is Equation~(2) of \textcite{Kelly1996closure}. +The input relation $R$ is first overapproximated by a ``d-form'' relation +$$ +\{\, \vec i \to \vec j \mid \exists \vec \alpha : +\vec L \le \vec j - \vec i \le \vec U +\wedge +(\forall p : j_p - i_p = M_p \alpha_p) +\,\} +, +$$ +where $p$ ranges over the dimensions and $\vec L$, $\vec U$ and +$\vec M$ are constant integer vectors. The elements of $\vec U$ +may be $\infty$, meaning that there is no upper bound corresponding +to that element, and similarly for $\vec L$. +Such an overapproximation can be obtained by computing strides, +lower and upper bounds on the difference set $\Delta \, R$. +The transitive closure of such a ``d-form'' relation is +\begin{equation} +\label{eq:omega} +\{\, \vec i \to \vec j \mid \exists \vec \alpha, k : +k \ge 1 \wedge +k \, \vec L \le \vec j - \vec i \le k \, \vec U +\wedge +(\forall p : j_p - i_p = M_p \alpha_p) +\,\} +. +\end{equation} +The domain and range of this transitive closure are then +intersected with those of the input relation. +This is a special case of the algorithm in \autoref{s:power}. + +In their algorithm for computing lower bounds, the authors +use the above algorithm as a substep on the disjuncts in the relation. +At the end, they say +\begin{quote} +If an upper bound is required, it can be calculated in a manner +similar to that of a single conjunct [sic] relation. +\end{quote} +Presumably, the authors mean that a ``d-form'' approximation +of the whole input relation should be used. +However, the accuracy can be improved by also trying to +apply the incremental technique from the same paper, +which is explained in more detail in \autoref{s:incremental}. +In this case, ${\cal C}(R_i,D)$ can be obtained by +allowing the value zero for $k$ in \eqref{eq:omega}, +i.e., by computing +$$ +\{\, \vec i \to \vec j \mid \exists \vec \alpha, k : +k \ge 0 \wedge +k \, \vec L \le \vec j - \vec i \le k \, \vec U +\wedge +(\forall p : j_p - i_p = M_p \alpha_p) +\,\} +. +$$ +In our implementation we take as $D$ the simple hull +(\autoref{s:simple hull}) of $\domain R \cup \range R$. +To determine whether it is safe to use ${\cal C}(R_i,D)$, +we check the following conditions, as proposed by +\textcite{Kelly1996closure}: +${\cal C}(R_i,D) - R_i^+$ is not a union and for each $j \ne i$ +the condition +$$ +\left({\cal C}(R_i,D) - R_i^+\right) +\circ +R_j +\circ +\left({\cal C}(R_i,D) - R_i^+\right) += +R_j +$$ +holds. Index: lib/Analysis/isl/doc/isl.bib =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/isl.bib @@ -0,0 +1,485 @@ +@inproceedings{Kelly1996closure, + author = {Wayne Kelly and + William Pugh and + Evan Rosser and + Tatiana Shpeisman}, + title = {Transitive Closure of Infinite Graphs and Its Applications}, + pages = {126-140}, + editor = {Chua-Huang Huang and + P. Sadayappan and + Utpal Banerjee and + David Gelernter and + Alexandru Nicolau and + David A. Padua}, + booktitle = {Languages and Compilers for Parallel Computing, 8th International + Workshop, LCPC'95, Columbus, Ohio, USA, August 10-12, 1995, + Proceedings}, + publisher = {Springer}, + series = {Lecture Notes in Computer Science}, + volume = {1033}, + year = {1996}, + isbn = {3-540-60765-X}, + doi = "10.1007/BFb0014196", +} + +@inproceedings{Beletska2009, + author = {Beletska, Anna and Barthou, Denis and Bielecki, Wlodzimierz and Cohen, Albert}, + title = {Computing the Transitive Closure of a Union of Affine Integer Tuple Relations}, + booktitle = {COCOA '09: Proceedings of the 3rd International Conference on Combinatorial Optimization and Applications}, + year = {2009}, + isbn = {978-3-642-02025-4}, + pages = {98--109}, + location = {Huangshan, China}, + doi = {10.1007/978-3-642-02026-1_9}, + publisher = {Springer-Verlag}, + address = {Berlin, Heidelberg}, +} + +@book{Schrijver1986, + author = "Schrijver, Alexander", + title = "Theory of Linear and Integer Programming", + publisher = "John Wiley \& Sons", + year = 1986 +} + +@article{Tarjan1972, + author = {Tarjan, Robert}, + journal = {SIAM Journal on Computing}, + number = {2}, + pages = {146--160}, + publisher = {SIAM}, + title = {Depth-First Search and Linear Graph Algorithms}, + volume = {1}, + year = {1972}, + doi = "10.1137/0201010", +} + +@TechReport{ Omega_calc, + author = "Wayne Kelly and Vadim Maslov and William Pugh and Evan Rosser and Tatiana Shpeisman and Dave Wonnacott", + title = "The {Omega} Calculator and Library", + month = nov, + institution = "University of Maryland", + year = 1996 +} + +@TechReport{ Omega_lib, + author = "Wayne Kelly and Vadim Maslov and William Pugh and Evan Rosser and Tatiana Shpeisman and Dave Wonnacott", + title = "The {Omega} Library", + month = nov, + institution = "University of Maryland", + year = 1996 +} + +@unpublished{Verdoolaege2009isl, + author = "Verdoolaege, Sven", + title = "An integer set library for program analysis", + note = "Advances in the Theory of Integer Linear Optimization and its Extensions,AMS 2009 Spring Western Section Meeting, San Francisco, California, 25-26 April 2009", + month = Apr, + year = "2009", + url = "https://lirias.kuleuven.be/handle/123456789/228373", +} + +@article{Barthou2000MSE, + author = {Barthou, Denis and Cohen, Albert and Collard, Jean-Fran\c{c}ois}, + title = {Maximal Static Expansion}, + journal = {Int. J. Parallel Program.}, + volume = {28}, + number = {3}, + year = {2000}, + issn = {0885-7458}, + pages = {213--243}, + doi = {10.1023/A:1007500431910}, + publisher = {Kluwer Academic Publishers}, + address = {Norwell, MA, USA}, +} + +@article{ Feautrier88parametric, + author = "P. Feautrier", + title = "Parametric Integer Programming", + journal = "RAIRO Recherche Op\'erationnelle", + volume = "22", + number = "3", + pages = "243--268", + year = "1988", +} + +@Article{ Fea91, + author = {Feautrier, P.}, + title = {Dataflow analysis of array and scalar references}, + journal = {International Journal of Parallel Programming}, + year = {1991}, + OPTkey = {}, + volume = {20}, + number = {1}, + OPTmonth = {}, + pages = {23--53}, + OPTnote = {}, + OPTannote = {}, + doi = "10.1007/BF01407931", +} + +@INPROCEEDINGS{BouletRe98, + AUTHOR = {Pierre Boulet and Xavier Redon}, + TITLE = {Communication Pre-evaluation in {HPF}}, + BOOKTITLE = {EUROPAR'98}, + PAGES = {263--272}, + YEAR = 1998, + VOLUME = 1470, + series = {Lecture Notes in Computer Science}, + PUBLISHER = {Springer-Verlag, Berlin}, + ABSTRACT = { Parallel computers are difficult to program efficiently. We believe + that a good way to help programmers write efficient programs is to + provide them with tools that show them how their programs behave on + a parallel computer. Data distribution is the major performance + factor of data-parallel programs and so automatic data layout for + HPF programs has been studied by many researchers recently. The + communication volume induced by a data distribution is a good + estimator of the efficiency of this data distribution. + + We present here a symbolic method to compute the communication + volume generated by a given data distribution during the program + writing phase (before compilation). We stay machine-independent to + assure portability. Our goal is to help the programmer understand + the data movements its program generates and thus find a good data + distribution. Our method is based on parametric polyhedral + computations. It can be applied to a large class of regular codes.}, + doi = "10.1007/BFb0057861", +} + +@INPROCEEDINGS {Verdoolaege2005experiences, + AUTHOR = "Verdoolaege, Sven and Beyls, Kristof and Bruynooghe, Maurice and Catthoor, Francky", + TITLE = {{E}xperiences with enumeration of integer projections of parametric polytopes}, + BOOKTITLE = {{P}roceedings of 14th {I}nternational {C}onference on {C}ompiler {C}onstruction, {E}dinburgh, {S}cotland}, + YEAR = {2005}, + EDITOR = {Bodik, R.}, + VOLUME = 3443, + pages = "91-105", + series = "Lecture Notes in Computer Science", + publisher = "Springer-Verlag", + address = "Berlin", + doi = "10.1007/b107108", +} + +@article{Detlefs2005simplify, + author = {David Detlefs and Greg Nelson and James B. Saxe}, + title = {Simplify: a theorem prover for program checking}, + journal = {J. ACM}, + volume = {52}, + number = {3}, + year = {2005}, + issn = {0004-5411}, + pages = {365--473}, + doi = {10.1145/1066100.1066102}, + publisher = {ACM}, + address = {New York, NY, USA}, + } + +@phdthesis{Nelson1980phd, + author = {Charles Gregory Nelson}, + title = {Techniques for program verification}, + year = {1980}, + order_no = {AAI8011683}, + school = {Stanford University}, + address = {Stanford, CA, USA}, + } + +@article{Woods2003short, + year = 2003, + Journal = "J. Amer. Math. Soc.", + volume = 16, + pages = "957--979", + month = apr, + title = {{Short rational generating functions for lattice point + problems}}, + author = {Alexander Barvinok and Kevin Woods}, + doi = "10.1090/S0894-0347-03-00428-4", +} + +@misc{barvinok-0.22, + author = {Sven Verdoolaege}, + title = {{\texttt{barvinok}}, version 0.22}, + howpublished = {Available from \url{http://barvinok.gforge.inria.fr/}}, + year = 2006 +} + +@inproceedings{DeLoera2004Three, + title = "Three Kinds of Integer Programming Algorithms based on Barvinok's Rational Functions", + author = "De Loera, J. A. and D. Haws and R. Hemmecke and P. Huggins and R. Yoshida", + booktitle = "Integer Programming and Combinatorial Optimization: 10th International IPCO Conference", + year = "2004", + month = jan, + series = "Lecture Notes in Computer Science", + Volume = 3064, + Pages = "244-255", + doi = "10.1007/978-3-540-25960-2_19", +} + +@TechReport{Feautrier02, + author = {P. Feautrier and J. Collard and C. Bastoul}, + title = {Solving systems of affine (in)equalities}, + institution = {PRiSM, Versailles University}, + year = 2002 +} + +@article{ Feautrier92multi, + author = "Paul Feautrier", + title = "Some Efficient Solutions to the Affine Scheduling Problem. {P}art {II}. Multidimensional Time", + journal = "International Journal of Parallel Programming", + volume = "21", + number = "6", + pages = "389--420", + year = "1992", + month = dec, + url = "citeseer.nj.nec.com/article/feautrier92some.html", + doi = "10.1007/BF01379404", +} + +@misc{Bygde2010licentiate, + author = {Stefan Bygde}, + title = {Static {WCET} Analysis based on Abstract Interpretation and Counting of Elements}, + month = {March}, + year = {2010}, + howpublished = {Licentiate thesis}, + publisher = {M{\"{a}}lardalen University Press}, + url = {http://www.mrtc.mdh.se/index.php?choice=publications&id=2144}, +} + +@phdthesis{Meister2004PhD, + title = {Stating and Manipulating Periodicity in the Polytope Model. Applications to Program Analysis and Optimization}, + author= {Beno\^it Meister}, + school = {Universit\'e Louis Pasteur}, + month = Dec, + year = {2004}, +} + +@inproceedings{Meister2008, + author = {Beno\^it Meister and Sven Verdoolaege}, + title = {Polynomial Approximations in the Polytope Model: Bringing the Power + of Quasi-Polynomials to the Masses}, + year = {2008}, + booktitle = {Digest of the 6th Workshop on Optimization for DSP and Embedded Systems, ODES-6}, + editor = "Jagadeesh Sankaran and Vander Aa, Tom", + month = apr, +} + +@misc{Galea2009personal, + author = "Fran\c{c}ois Galea", + title = "personal communication", + year = 2009, + month = nov, +} + +@misc{PPL, + author = "R. Bagnara and P. M. Hill and E. Zaffanella", + title = "The {Parma Polyhedra Library}", + howpublished = {\url{http://www.cs.unipr.it/ppl/}}, +} + +@TECHREPORT{Cook1991implementation, +AUTHOR={William Cook and Thomas Rutherford and Herbert E. Scarf and David F. Shallcross}, +TITLE={An Implementation of the Generalized Basis Reduction Algorithm for Integer Programming}, +YEAR=1991, +MONTH=Aug, +INSTITUTION={Cowles Foundation, Yale University}, +TYPE={Cowles Foundation Discussion Papers}, +NOTE={available at \url{http://ideas.repec.org/p/cwl/cwldpp/990.html}}, +NUMBER={990}, +} + + @article{Karr1976affine, +author={ Michael Karr}, +title={ Affine Relationships Among Variables of a Program }, +journal={Acta Informatica}, +Volume={6}, +pages={133-151}, +year={1976}, +publisher={Springer-Verlag}, +ignore={ }, + doi = "10.1007/BF00268497", +} + +@PhdThesis{Verhaegh1995PhD, + title = "Multidimensional Periodic Scheduling", + author = "Wim F. J. Verhaegh", + school = "Technische Universiteit Eindhoven", + year = 1995, +} + +@INPROCEEDINGS{Seghir2006minimizing, + AUTHOR = "Rachid Seghir and Vincent Loechner", + TITLE = {Memory Optimization by Counting Points in Integer Transformations of Parametric Polytopes}, + BOOKTITLE = {{P}roceedings of the {I}nternational {C}onference on {C}ompilers, {A}rchitectures, and {S}ynthesis for {E}mbedded Systems, CASES 2006, {S}eoul, {K}orea}, + month = oct, + YEAR = {2006}, + doi = {10.1145/1176760.1176771}, +} + +@misc{DeSmet2010personal, + author = "De Smet, Sven", + title = "personal communication", + year = 2010, + month = apr, +} + +@inproceedings{Verdoolaege2015impact, + author = {Verdoolaege, Sven}, + title = {Integer Set Coalescing}, + booktitle = {Proceedings of the 5th International Workshop on + Polyhedral Compilation Techniques}, + year = 2015, + month = Jan, + address = {Amsterdam, The Netherlands}, + abstract = { +In polyhedral compilation, various core concepts such as the set +of statement instances, the access relations, the dependences and +the schedule are represented or approximated using sets and binary +relations of sequences of integers bounded by (quasi-)affine constraints. +When these sets and relations are represented in disjunctive normal form, +it is important to keep the number of disjuncts small, both for efficiency +and to improve the computation of transitive closure overapproximations +and AST generation. This paper describes the set coalescing operation +of isl that looks for opportunities to combine several disjuncts into +a single disjunct without affecting the elements in the set. The main +purpose of the paper is to explain the various heuristics and to prove +their correctness. + }, + doi = "10.13140/2.1.1313.6968", +} + +@misc{Verdoolaege2016tutorial, + author = "Sven Verdoolaege", + title = "Presburger Formulas and Polyhedral Compilation", + year = 2016, + doi = "10.13140/RG.2.1.1174.6323", +} + +@inproceedings{Verdoolaege2009equivalence, + author = "Sven Verdoolaege and Gerda Janssens and Maurice Bruynooghe", + title = "Equivalence checking of static affine programs using widening to handle recurrences", + booktitle = "Computer Aided Verification 21", + month = Jun, + year = 2009, + pages = "599--613", + publisher = "Springer", + doi = "10.1007/978-3-642-02658-4_44", +} + +@incollection{Verdoolaege2010isl, + author = {Verdoolaege, Sven}, + title = {isl: An Integer Set Library for the Polyhedral Model}, + booktitle = {Mathematical Software - ICMS 2010}, + series = {Lecture Notes in Computer Science}, + editor = {Fukuda, Komei and Hoeven, Joris and Joswig, Michael and Takayama, Nobuki}, + publisher = {Springer}, + isbn = {}, + pages = {299-302}, + volume = {6327}, + year = {2010}, + doi = {10.1007/978-3-642-15582-6_49}, +} + +@incollection{Verdoolaege2010networks, + author = "Verdoolaege, Sven", + title = "Polyhedral process networks", + editor = "Bhattacharrya, Shuvra and Deprettere, Ed and Leupers, Rainer and Takala, Jarmo", + publisher = "Springer", + year = "2010", + doi = "10.1007/978-1-4419-6345-1\_{}33", + pages = "931--965", + booktitle = "Handbook of Signal Processing Systems", + url = "https://lirias.kuleuven.be/handle/123456789/235370", + doi = "10.1007/978-1-4419-6345-1_33", +} + +@InProceedings{Verdoolaege2011iscc, + author = {Sven Verdoolaege}, + title = {Counting Affine Calculator and Applications}, + booktitle = { First International Workshop on Polyhedral Compilation Techniques (IMPACT'11)}, + address = { Chamonix, France}, + month = apr, + year = {2011}, + doi = "10.13140/RG.2.1.2959.5601", +} + +@inproceedings{Verdoolaege2011closure, + author = {Verdoolaege, Sven and Cohen, Albert and Beletska, Anna}, + title = {Transitive Closures of Affine Integer Tuple Relations and Their Overapproximations}, + booktitle = {Proceedings of the 18th International Conference on Static Analysis}, + series = {SAS'11}, + year = {2011}, + isbn = {978-3-642-23701-0}, + location = {Venice, Italy}, + pages = {216--232}, + numpages = {17}, + acmid = {2041570}, + publisher = {Springer-Verlag}, + address = {Berlin, Heidelberg}, + doi = "10.1007/978-3-642-23702-7_18", +} + +@article{Verdoolaege2013PPCG, + title={Polyhedral parallel code generation for {CUDA}}, + author={Verdoolaege, Sven and Juega, Juan Carlos and Cohen, Albert and G{\'o}mez, Jos{\'e} Ignacio and Tenllado, Christian and Catthoor, Francky}, + journal = {ACM Trans. Archit. Code Optim.}, + volume={9}, + number={4}, + pages={54}, + year={2013}, + publisher={ACM}, + doi = {10.1145/2400682.2400713}, +} + +@inproceedings{Verdoolaege2014impact, + author = {Verdoolaege, Sven and Guelton, Serge and + Grosser, Tobias and Cohen, Albert}, + title = {Schedule Trees}, + booktitle = {Proceedings of the 4th International Workshop on Polyhedral Compilation Techniques}, + year = 2014, + month = Jan, + address = {Vienna, Austria}, + url = {http://impact.gforge.inria.fr/impact2014/papers/impact2014-verdoolaege.pdf}, + abstract = { + Schedules in the polyhedral model, both those that represent the original +execution order and those produced by scheduling algorithms, naturally have the +form of a tree. Generic schedule representations proposed in the literature +encode this tree structure such that it is only implicitly available. +Following the internal representation of isl , we propose to represent +schedules as explicit trees and further extend the concept by introducing +different kinds of nodes. We compare our schedule trees to other +representations in detail and illustrate how they have been successfully used +to simplify the implementation of a non-trivial polyhedral compiler. + }, + DOI = {10.13140/RG.2.1.4475.6001}, +} + +@article{Grosser2015AST, + author = "Tobias Grosser and Sven Verdoolaege and Albert Cohen", + title = "Polyhedral {AST} generation is more than scanning polyhedra", + journal = "ACM Transactions on Programming Languages and Systems", + issue_date = {August 2015}, + volume = {37}, + number = {4}, + month = jul, + year = {2015}, + issn = {0164-0925}, + pages = {12:1--12:50}, + articleno = {12}, + numpages = {50}, + url = {http://doi.acm.org/10.1145/2743016}, + doi = {10.1145/2743016}, + acmid = {2743016}, + publisher = {ACM}, + address = {New York, NY, USA}, + keywords = {Polyhedral compilation, Presburger relations, code generation, index set splitting, unrolling}, +} + +@inproceedings{Verdoolaege2016reordering, + author = {Sven Verdoolaege and Albert Cohen}, + title = "Live-Range Reordering", + booktitle = {Proceedings of the sixth International Workshop on + Polyhedral Compilation Techniques}, + year = 2016, + month = Jan, + address = {Prague, Czech Republic}, + doi = "10.13140/RG.2.1.3272.9680", +} Index: lib/Analysis/isl/doc/manual.tex =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/manual.tex @@ -0,0 +1,95 @@ +\documentclass{report} +\usepackage[T1]{fontenc} +\usepackage[plainpages=false,pdfpagelabels,breaklinks]{hyperref} +\usepackage[backend=biber,isbn=false,url=false,doi=true,% +maxbibnames=99,style=authoryear,sortcites=true,sorting=nyt,backref=true,% +indexing=true,mincitenames=2,maxcitenames=2,datelabel=comp,dashed=false,% +useprefix=true]{biblatex} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{txfonts} +\usepackage{aliascnt} +\usepackage{tikz} +\usepackage{calc} +\usepackage[ruled]{algorithm2e} +\usetikzlibrary{matrix,fit,backgrounds,decorations.pathmorphing,positioning} +\usepackage{listings} + +\addbibresource{isl.bib} + +\renewbibmacro*{finentry}{\iflistundef{pageref}{}{\renewcommand{\finentrypunct}{}}\finentry} +\renewbibmacro*{pageref}{% + \iflistundef{pageref} + {} + {\setunit{\adddot\addspace}\printtext{% + \mbox{}\penalty100\hfill\hbox{[\printlist[pageref][-\value{listtotal}]{pageref}]}}}} + +\lstset{basicstyle=\tt,flexiblecolumns=false} + +\def\vec#1{\mathchoice{\mbox{\boldmath$\displaystyle\bf#1$}} +{\mbox{\boldmath$\textstyle\bf#1$}} +{\mbox{\boldmath$\scriptstyle\bf#1$}} +{\mbox{\boldmath$\scriptscriptstyle\bf#1$}}} + +\providecommand{\fract}[1]{\left\{#1\right\}} +\providecommand{\floor}[1]{\left\lfloor#1\right\rfloor} +\providecommand{\ceil}[1]{\left\lceil#1\right\rceil} +\def\sp#1#2{\langle #1, #2 \rangle} +\def\spv#1#2{\langle\vec #1,\vec #2\rangle} + +\newtheorem{theorem}{Theorem} +\newaliascnt{example}{theorem} +\newtheorem{example}[example]{Example} +\newaliascnt{def}{theorem} +\newtheorem{definition}[def]{Definition} +\aliascntresetthe{example} +\aliascntresetthe{def} +\numberwithin{theorem}{section} +\numberwithin{def}{section} +\numberwithin{example}{section} + +\newcommand{\algocflineautorefname}{Algorithm} +\newcommand{\exampleautorefname}{Example} +\newcommand{\lstnumberautorefname}{Line} +\renewcommand{\sectionautorefname}{Section} +\renewcommand{\subsectionautorefname}{Section} +\renewcommand{\algorithmautorefname}{Algorithm} + +\DeclareFieldFormat{date}{\hypertarget{\thefield{entrykey}}{#1}} +\def\isl{\hyperlink{Verdoolaege2010isl}{\texttt{isl}}\xspace} + +\def\Z{\mathbb{Z}} +\def\Q{\mathbb{Q}} + +\def\pdom{\mathop{\rm pdom}\nolimits} +\def\domain{\mathop{\rm dom}\nolimits} +\def\range{\mathop{\rm ran}\nolimits} +\def\identity{\mathop{\rm Id}\nolimits} +\def\diff{\mathop{\Delta}\nolimits} + +\providecommand{\floor}[1]{\left\lfloor#1\right\rfloor} + +\begin{document} + +\title{Integer Set Library: Manual\\ +\small Version: \input{version} } +\author{Sven Verdoolaege} + +\maketitle +\tableofcontents + +\chapter{User Manual} + +\input{user} + +\chapter{Implementation Details} + +\input{implementation} + +\chapter{Further Reading} + +\input{reading} + +\printbibliography + +\end{document} Index: lib/Analysis/isl/doc/mypod2latex =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/mypod2latex @@ -0,0 +1,14 @@ +#!/usr/bin/perl + +use strict; +use Pod::LaTeX; + +my ($in, $out) = @ARGV; + +my $parser = new Pod::LaTeX( + AddPreamble => 0, + AddPostamble => 0, + LevelNoNum => 5, + ); + +$parser->parse_from_file($in, $out); Index: lib/Analysis/isl/doc/reading.tex =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/reading.tex @@ -0,0 +1,46 @@ +\textcite{Verdoolaege2016tutorial} describes the concepts behind +\isl in some detail, mainly focusing on Presburger formulas, +but also including some information on polyhedral compilation, +especially on dependence analysis. +Individual aspects of \isl are described in the following publications. +\begin{itemize} +\item +\textcite{Verdoolaege2009equivalence} introduce \isl as a library +for manipulating sets of integers defined by linear inequalities and +integer divisions that is used in their equivalence checker. + +\item +\textcite{Verdoolaege2010isl} provides a more detailed description +of \isl at the time and still stands as the official reference for +\isl. However, many features were only added later on and one or +more of the publications below may be more appropriate as +a reference to these features. + +\item +\textcite[Section 5.1]{Verdoolaege2010networks} provides some +details on the dataflow analysis step, but also see +\textcite[Chapter 6]{Verdoolaege2016tutorial} and +\textcite{Verdoolaege2016reordering} for a more recent treatment. + +\item The concepts of structured and named spaces and the manipulation +of sets containing elements in different spaces were introduced +by \textcite{Verdoolaege2011iscc}. + +\item The transitive closure operation is described +by \textcite{Verdoolaege2011closure}. + +\item The scheduler is briefly described by +\textcite[Section 6.2]{Verdoolaege2013PPCG} and +\textcite[Section 2.4]{Verdoolaege2016reordering}. + +\item Schedule trees started out as ``trees of bands'' +\parencite[Section 6.2]{Verdoolaege2013PPCG}, were formally +introduced by \textcite{Verdoolaege2014impact}, and were +slightly refined by \textcite{Grosser2015AST}. + +\item The coalescing operation is described by +\textcite{Verdoolaege2015impact}. + +\item The AST generator is described by \textcite{Grosser2015AST}. + +\end{itemize} Index: lib/Analysis/isl/doc/user.pod =================================================================== --- /dev/null +++ lib/Analysis/isl/doc/user.pod @@ -0,0 +1,10584 @@ +=head1 Introduction + +C is a thread-safe C library for manipulating +sets and relations of integer points bounded by affine constraints. +The descriptions of the sets and relations may involve +both parameters and existentially quantified variables. +All computations are performed in exact integer arithmetic +using C or C. +The C library offers functionality that is similar +to that offered by the C and C libraries, +but the underlying algorithms are in most cases completely different. + +The library is by no means complete and some fairly basic +functionality is still missing. +Still, even in its current form, the library has been successfully +used as a backend polyhedral library for the polyhedral +scanner C and as part of an equivalence checker of +static affine programs. +For bug reports, feature requests and questions, +visit the discussion group at +L. + +=head2 Backward Incompatible Changes + +=head3 Changes since isl-0.02 + +=over + +=item * The old printing functions have been deprecated +and replaced by C functions, see L. + +=item * Most functions related to dependence analysis have acquired +an extra C argument. To obtain the old behavior, this argument +should be given the value 1. See L. + +=back + +=head3 Changes since isl-0.03 + +=over + +=item * The function C has been +renamed to C. +Similarly, C has been +renamed to C. + +=back + +=head3 Changes since isl-0.04 + +=over + +=item * All header files have been renamed from C +to C. + +=back + +=head3 Changes since isl-0.05 + +=over + +=item * The functions C and +C no longer print a newline. + +=item * The functions C +and C now return +the accesses for which no source could be found instead of +the iterations where those accesses occur. + +=item * The functions C and +C now take a B space as input. An old call +C can be rewritten to +C. + +=item * The function C no longer takes +a parameter position as input. Instead, the exponent +is now expressed as the domain of the resulting relation. + +=back + +=head3 Changes since isl-0.06 + +=over + +=item * The format of C's +C output has changed. +Use C to obtain the old output. + +=item * The C<*_fast_*> functions have been renamed to C<*_plain_*>. +Some of the old names have been kept for backward compatibility, +but they will be removed in the future. + +=back + +=head3 Changes since isl-0.07 + +=over + +=item * The function C has been renamed to +C. +Similarly, the function C has been renamed to +C. + +=item * The C type has been renamed to C +along with the associated functions. +Some of the old names have been kept for backward compatibility, +but they will be removed in the future. + +=item * Spaces of maps, sets and parameter domains are now +treated differently. The distinction between map spaces and set spaces +has always been made on a conceptual level, but proper use of such spaces +was never checked. Furthermore, up until isl-0.07 there was no way +of explicitly creating a parameter space. These can now be created +directly using C or from other spaces using +C. + +=item * The space in which C, C, C, +C, C and C +objects live is now a map space +instead of a set space. This means, for example, that the dimensions +of the domain of an C are now considered to be of type +C instead of C. Extra functions have been +added to obtain the domain space. Some of the constructors still +take a domain space and have therefore been renamed. + +=item * The functions C and C +now take an C instead of an C. +An C can be created from an C +using C. + +=item * The C type has been removed. Functions that used +to return an C now return an C. +Note that the space of an C is that of relation. +When replacing a call to C by a call to +C any C argument needs +to be replaced by C. +A call to C can be replaced by a call +to C. +A call to C call be replaced by +the nested call + + isl_qpolynomial_from_aff(isl_aff_floor(div)) + +The function C has also been renamed +to C. + +=item * The C argument has been removed from +C and similar functions. +When reading input in the original PolyLib format, +the result will have no parameters. +If parameters are expected, the caller may want to perform +dimension manipulation on the result. + +=back + +=head3 Changes since isl-0.09 + +=over + +=item * The C option has been replaced +by the C option. + +=item * The first argument of C is now +an C instead of an C. +A call C can be replaced by + + isl_pw_aff_cond(isl_set_indicator_function(a), b, c) + +=back + +=head3 Changes since isl-0.10 + +=over + +=item * The functions C and +C have been renamed to +C and +C. +The new C and +C have slightly different meanings. + +=back + +=head3 Changes since isl-0.12 + +=over + +=item * C has been replaced by C. +Some of the old functions are still available in C +but they will be removed in the future. + +=item * The functions C, +C, C +and C have been changed to return +an C instead of an C. + +=item * The function C +has been removed. Essentially the same functionality is available +through C, except that it requires +setting up coincidence constraints. +The option C has accordingly been +replaced by the option C. + +=item * The function C has been changed +to return an C instead of a rational C. +The function C has been changed to return +a regular basic set, rather than a rational basic set. + +=back + +=head3 Changes since isl-0.14 + +=over + +=item * The function C now consistently +computes the sum on the shared definition domain. +The function C has been added +to compute the sum on the union of definition domains. +The original behavior of C was +confused and is no longer available. + +=item * Band forests have been replaced by schedule trees. + +=item * The function C has been +replaced by the function C. +Note that the may dependence relation returned by +C is the union of +the two dependence relations returned by +C. Similarly for the no source relations. +The function C is still available +for backward compatibility, but it will be removed in the future. + +=item * The function C has been +deprecated. + +=item * The function C has been +renamed to C. +The original name is still available +for backward compatibility, but it will be removed in the future. + +=item * The C AST generation option has been +deprecated. + +=item * The functions C and C +have been renamed to C and +C. The original names have been +kept for backward compatibility, but they will be removed in the future. + +=item * The C option has been replaced +by the C option. The effect +of setting the C option to C +is now obtained by turning on the C option. + +=back + +=head1 License + +C is released under the MIT license. + +=over + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +=back + +Note that by default C requires C, which is released +under the GNU Lesser General Public License (LGPL). This means +that code linked against C is also linked against LGPL code. + +When configuring with C<--with-int=imath> or C<--with-int=imath-32>, C +will link against C, a library for exact integer arithmetic released +under the MIT license. + +=head1 Installation + +The source of C can be obtained either as a tarball +or from the git repository. Both are available from +L. +The installation process depends on how you obtained +the source. + +=head2 Installation from the git repository + +=over + +=item 1 Clone or update the repository + +The first time the source is obtained, you need to clone +the repository. + + git clone git://repo.or.cz/isl.git + +To obtain updates, you need to pull in the latest changes + + git pull + +=item 2 Optionally get C submodule + +To build C with C, you need to obtain the C +submodule by running in the git source tree of C + + git submodule init + git submodule update + +This will fetch the required version of C in a subdirectory of C. + +=item 2 Generate C + + ./autogen.sh + +=back + +After performing the above steps, continue +with the L. + +=head2 Common installation instructions + +=over + +=item 1 Obtain C + +By default, building C requires C, including its headers files. +Your distribution may not provide these header files by default +and you may need to install a package called C or something +similar. Alternatively, C can be built from +source, available from L. +C is not needed if you build C with C. + +=item 2 Configure + +C uses the standard C C script. +To run it, just type + + ./configure + +optionally followed by some configure options. +A complete list of options can be obtained by running + + ./configure --help + +Below we discuss some of the more common options. + +=over + +=item C<--prefix> + +Installation prefix for C + +=item C<--with-int=[gmp|imath|imath-32]> + +Select the integer library to be used by C, the default is C. +With C, C will use 32 bit integers, but fall back to C +for values out of the 32 bit range. In most applications, C will run +fastest with the C option, followed by C and C, the +slowest. + +=item C<--with-gmp-prefix> + +Installation prefix for C (architecture-independent files). + +=item C<--with-gmp-exec-prefix> + +Installation prefix for C (architecture-dependent files). + +=back + +=item 3 Compile + + make + +=item 4 Install (optional) + + make install + +=back + +=head1 Integer Set Library + +=head2 Memory Management + +Since a high-level operation on isl objects usually involves +several substeps and since the user is usually not interested in +the intermediate results, most functions that return a new object +will also release all the objects passed as arguments. +If the user still wants to use one or more of these arguments +after the function call, she should pass along a copy of the +object rather than the object itself. +The user is then responsible for making sure that the original +object gets used somewhere else or is explicitly freed. + +The arguments and return values of all documented functions are +annotated to make clear which arguments are released and which +arguments are preserved. In particular, the following annotations +are used + +=over + +=item C<__isl_give> + +C<__isl_give> means that a new object is returned. +The user should make sure that the returned pointer is +used exactly once as a value for an C<__isl_take> argument. +In between, it can be used as a value for as many +C<__isl_keep> arguments as the user likes. +There is one exception, and that is the case where the +pointer returned is C. Is this case, the user +is free to use it as an C<__isl_take> argument or not. +When applied to a C, the returned pointer needs to be +freed using C. + +=item C<__isl_null> + +C<__isl_null> means that a C value is returned. + +=item C<__isl_take> + +C<__isl_take> means that the object the argument points to +is taken over by the function and may no longer be used +by the user as an argument to any other function. +The pointer value must be one returned by a function +returning an C<__isl_give> pointer. +If the user passes in a C value, then this will +be treated as an error in the sense that the function will +not perform its usual operation. However, it will still +make sure that all the other C<__isl_take> arguments +are released. + +=item C<__isl_keep> + +C<__isl_keep> means that the function will only use the object +temporarily. After the function has finished, the user +can still use it as an argument to other functions. +A C value will be treated in the same way as +a C value for an C<__isl_take> argument. +This annotation may also be used on return values of +type C, in which case the returned pointer should +not be freed by the user and is only valid until the object +from which it was derived is updated or freed. + +=back + +=head2 Initialization + +All manipulations of integer sets and relations occur within +the context of an C. +A given C can only be used within a single thread. +All arguments of a function are required to have been allocated +within the same context. +There are currently no functions available for moving an object +from one C to another C. This means that +there is currently no way of safely moving an object from one +thread to another, unless the whole C is moved. + +An C can be allocated using C and +freed using C. +All objects allocated within an C should be freed +before the C itself is freed. + + isl_ctx *isl_ctx_alloc(); + void isl_ctx_free(isl_ctx *ctx); + +The user can impose a bound on the number of low-level I +that can be performed by an C. This bound can be set and +retrieved using the following functions. A bound of zero means that +no bound is imposed. The number of operations performed can be +reset using C. Note that the number +of low-level operations needed to perform a high-level computation +may differ significantly across different versions +of C, but it should be the same across different platforms +for the same version of C. + +Warning: This feature is experimental. C has good support to abort and +bail out during the computation, but this feature may exercise error code paths +that are normally not used that much. Consequently, it is not unlikely that +hidden bugs will be exposed. + + void isl_ctx_set_max_operations(isl_ctx *ctx, + unsigned long max_operations); + unsigned long isl_ctx_get_max_operations(isl_ctx *ctx); + void isl_ctx_reset_operations(isl_ctx *ctx); + +In order to be able to create an object in the same context +as another object, most object types (described later in +this document) provide a function to obtain the context +in which the object was created. + + #include + isl_ctx *isl_val_get_ctx(__isl_keep isl_val *val); + isl_ctx *isl_multi_val_get_ctx( + __isl_keep isl_multi_val *mv); + + #include + isl_ctx *isl_id_get_ctx(__isl_keep isl_id *id); + + #include + isl_ctx *isl_local_space_get_ctx( + __isl_keep isl_local_space *ls); + + #include + isl_ctx *isl_set_list_get_ctx( + __isl_keep isl_set_list *list); + + #include + isl_ctx *isl_aff_get_ctx(__isl_keep isl_aff *aff); + isl_ctx *isl_multi_aff_get_ctx( + __isl_keep isl_multi_aff *maff); + isl_ctx *isl_pw_aff_get_ctx(__isl_keep isl_pw_aff *pa); + isl_ctx *isl_pw_multi_aff_get_ctx( + __isl_keep isl_pw_multi_aff *pma); + isl_ctx *isl_multi_pw_aff_get_ctx( + __isl_keep isl_multi_pw_aff *mpa); + isl_ctx *isl_union_pw_aff_get_ctx( + __isl_keep isl_union_pw_aff *upa); + isl_ctx *isl_union_pw_multi_aff_get_ctx( + __isl_keep isl_union_pw_multi_aff *upma); + isl_ctx *isl_multi_union_pw_aff_get_ctx( + __isl_keep isl_multi_union_pw_aff *mupa); + + #include + isl_ctx *isl_id_to_ast_expr_get_ctx( + __isl_keep isl_id_to_ast_expr *id2expr); + + #include + isl_ctx *isl_point_get_ctx(__isl_keep isl_point *pnt); + + #include + isl_ctx *isl_vec_get_ctx(__isl_keep isl_vec *vec); + + #include + isl_ctx *isl_mat_get_ctx(__isl_keep isl_mat *mat); + + #include + isl_ctx *isl_vertices_get_ctx( + __isl_keep isl_vertices *vertices); + isl_ctx *isl_vertex_get_ctx(__isl_keep isl_vertex *vertex); + isl_ctx *isl_cell_get_ctx(__isl_keep isl_cell *cell); + + #include + isl_ctx *isl_restriction_get_ctx( + __isl_keep isl_restriction *restr); + isl_ctx *isl_union_access_info_get_ctx( + __isl_keep isl_union_access_info *access); + isl_ctx *isl_union_flow_get_ctx( + __isl_keep isl_union_flow *flow); + + #include + isl_ctx *isl_schedule_get_ctx( + __isl_keep isl_schedule *sched); + isl_ctx *isl_schedule_constraints_get_ctx( + __isl_keep isl_schedule_constraints *sc); + + #include + isl_ctx *isl_schedule_node_get_ctx( + __isl_keep isl_schedule_node *node); + + #include + isl_ctx *isl_band_get_ctx(__isl_keep isl_band *band); + + #include + isl_ctx *isl_ast_build_get_ctx( + __isl_keep isl_ast_build *build); + + #include + isl_ctx *isl_ast_expr_get_ctx( + __isl_keep isl_ast_expr *expr); + isl_ctx *isl_ast_node_get_ctx( + __isl_keep isl_ast_node *node); + +=head2 Return Types + +C uses two special return types for functions that either return +a boolean or that in principle do not return anything. +In particular, the C type has three possible values: +C (a positive integer value), indicating I or I; +C (the integer value zero), indicating I or I; and +C (a negative integer value), indicating that something +went wrong. The following function can be used to negate an C, +where the negation of C is C again. + + #include + isl_bool isl_bool_not(isl_bool b); + +The C type has two possible values: +C (the integer value zero), indicating a successful +operation; and +C (a negative integer value), indicating that something +went wrong. +See L for more information on +C and C. + +=head2 Values + +An C represents an integer value, a rational value +or one of three special values, infinity, negative infinity and NaN. +Some predefined values can be created using the following functions. + + #include + __isl_give isl_val *isl_val_zero(isl_ctx *ctx); + __isl_give isl_val *isl_val_one(isl_ctx *ctx); + __isl_give isl_val *isl_val_negone(isl_ctx *ctx); + __isl_give isl_val *isl_val_nan(isl_ctx *ctx); + __isl_give isl_val *isl_val_infty(isl_ctx *ctx); + __isl_give isl_val *isl_val_neginfty(isl_ctx *ctx); + +Specific integer values can be created using the following functions. + + #include + __isl_give isl_val *isl_val_int_from_si(isl_ctx *ctx, + long i); + __isl_give isl_val *isl_val_int_from_ui(isl_ctx *ctx, + unsigned long u); + __isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, + size_t n, size_t size, const void *chunks); + +The function C constructs an C +from the C I, each consisting of C bytes, stored at C. +The least significant digit is assumed to be stored first. + +Value objects can be copied and freed using the following functions. + + #include + __isl_give isl_val *isl_val_copy(__isl_keep isl_val *v); + __isl_null isl_val *isl_val_free(__isl_take isl_val *v); + +They can be inspected using the following functions. + + #include + long isl_val_get_num_si(__isl_keep isl_val *v); + long isl_val_get_den_si(__isl_keep isl_val *v); + __isl_give isl_val *isl_val_get_den_val( + __isl_keep isl_val *v); + double isl_val_get_d(__isl_keep isl_val *v); + size_t isl_val_n_abs_num_chunks(__isl_keep isl_val *v, + size_t size); + int isl_val_get_abs_num_chunks(__isl_keep isl_val *v, + size_t size, void *chunks); + +C returns the number of I +of C bytes needed to store the absolute value of the +numerator of C. +C stores these digits at C, +which is assumed to have been preallocated by the caller. +The least significant digit is stored first. +Note that C, C, +C, C +and C can only be applied to rational values. + +An C can be modified using the following function. + + #include + __isl_give isl_val *isl_val_set_si(__isl_take isl_val *v, + long i); + +The following unary properties are defined on Cs. + + #include + int isl_val_sgn(__isl_keep isl_val *v); + isl_bool isl_val_is_zero(__isl_keep isl_val *v); + isl_bool isl_val_is_one(__isl_keep isl_val *v); + isl_bool isl_val_is_negone(__isl_keep isl_val *v); + isl_bool isl_val_is_nonneg(__isl_keep isl_val *v); + isl_bool isl_val_is_nonpos(__isl_keep isl_val *v); + isl_bool isl_val_is_pos(__isl_keep isl_val *v); + isl_bool isl_val_is_neg(__isl_keep isl_val *v); + isl_bool isl_val_is_int(__isl_keep isl_val *v); + isl_bool isl_val_is_rat(__isl_keep isl_val *v); + isl_bool isl_val_is_nan(__isl_keep isl_val *v); + isl_bool isl_val_is_infty(__isl_keep isl_val *v); + isl_bool isl_val_is_neginfty(__isl_keep isl_val *v); + +Note that the sign of NaN is undefined. + +The following binary properties are defined on pairs of Cs. + + #include + isl_bool isl_val_lt(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + isl_bool isl_val_le(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + isl_bool isl_val_gt(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + isl_bool isl_val_ge(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + isl_bool isl_val_eq(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + isl_bool isl_val_ne(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + isl_bool isl_val_abs_eq(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + +The function C checks whether its two arguments +are equal in absolute value. + +For integer Cs we additionally have the following binary property. + + #include + isl_bool isl_val_is_divisible_by(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + +An C can also be compared to an integer using the following +function. The result is undefined for NaN. + + #include + int isl_val_cmp_si(__isl_keep isl_val *v, long i); + +The following unary operations are available on Cs. + + #include + __isl_give isl_val *isl_val_abs(__isl_take isl_val *v); + __isl_give isl_val *isl_val_neg(__isl_take isl_val *v); + __isl_give isl_val *isl_val_floor(__isl_take isl_val *v); + __isl_give isl_val *isl_val_ceil(__isl_take isl_val *v); + __isl_give isl_val *isl_val_trunc(__isl_take isl_val *v); + __isl_give isl_val *isl_val_inv(__isl_take isl_val *v); + __isl_give isl_val *isl_val_2exp(__isl_take isl_val *v); + +The following binary operations are available on Cs. + + #include + __isl_give isl_val *isl_val_min(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_max(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_add(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_add_ui(__isl_take isl_val *v1, + unsigned long v2); + __isl_give isl_val *isl_val_sub(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_sub_ui(__isl_take isl_val *v1, + unsigned long v2); + __isl_give isl_val *isl_val_mul(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_mul_ui(__isl_take isl_val *v1, + unsigned long v2); + __isl_give isl_val *isl_val_div(__isl_take isl_val *v1, + __isl_take isl_val *v2); + +On integer values, we additionally have the following operations. + + #include + __isl_give isl_val *isl_val_2exp(__isl_take isl_val *v); + __isl_give isl_val *isl_val_mod(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_gcd(__isl_take isl_val *v1, + __isl_take isl_val *v2); + __isl_give isl_val *isl_val_gcdext(__isl_take isl_val *v1, + __isl_take isl_val *v2, __isl_give isl_val **x, + __isl_give isl_val **y); + +The function C returns the greatest common divisor g +of C and C as well as two integers C<*x> and C<*y> such +that C<*x> * C + C<*y> * C = g. + +=head3 GMP specific functions + +These functions are only available if C has been compiled with C +support. + +Specific integer and rational values can be created from C values using +the following functions. + + #include + __isl_give isl_val *isl_val_int_from_gmp(isl_ctx *ctx, + mpz_t z); + __isl_give isl_val *isl_val_from_gmp(isl_ctx *ctx, + const mpz_t n, const mpz_t d); + +The numerator and denominator of a rational value can be extracted as +C values using the following functions. + + #include + int isl_val_get_num_gmp(__isl_keep isl_val *v, mpz_t z); + int isl_val_get_den_gmp(__isl_keep isl_val *v, mpz_t z); + +=head2 Sets and Relations + +C uses six types of objects for representing sets and relations, +C, C, C, C, +C and C. +C and C represent sets and relations that +can be described as a conjunction of affine constraints, while +C and C represent unions of +Cs and Cs, respectively. +However, all Cs or Cs in the union need +to live in the same space. Cs and Cs +represent unions of Cs or Cs in I spaces, +where spaces are considered different if they have a different number +of dimensions and/or different names (see L<"Spaces">). +The difference between sets and relations (maps) is that sets have +one set of variables, while relations have two sets of variables, +input variables and output variables. + +=head2 Error Handling + +C supports different ways to react in case a runtime error is triggered. +Runtime errors arise, e.g., if a function such as C is called +with two maps that have incompatible spaces. There are three possible ways +to react on error: to warn, to continue or to abort. + +The default behavior is to warn. In this mode, C prints a warning, stores +the last error in the corresponding C and the function in which the +error was triggered returns a value indicating that some error has +occurred. In case of functions returning a pointer, this value is +C. In case of functions returning an C or an +C, this valus is C or C. +An error does not corrupt internal state, +such that isl can continue to be used. C also provides functions to +read the last error and to reset the memory that stores the last error. The +last error is only stored for information purposes. Its presence does not +change the behavior of C. Hence, resetting an error is not required to +continue to use isl, but only to observe new errors. + + #include + enum isl_error isl_ctx_last_error(isl_ctx *ctx); + void isl_ctx_reset_error(isl_ctx *ctx); + +Another option is to continue on error. This is similar to warn on error mode, +except that C does not print any warning. This allows a program to +implement its own error reporting. + +The last option is to directly abort the execution of the program from within +the isl library. This makes it obviously impossible to recover from an error, +but it allows to directly spot the error location. By aborting on error, +debuggers break at the location the error occurred and can provide a stack +trace. Other tools that automatically provide stack traces on abort or that do +not want to continue execution after an error was triggered may also prefer to +abort on error. + +The on error behavior of isl can be specified by calling +C or by setting the command line option +C<--isl-on-error>. Valid arguments for the function call are +C, C and C. The +choices for the command line option are C, C and C. +It is also possible to query the current error mode. + + #include + isl_stat isl_options_set_on_error(isl_ctx *ctx, int val); + int isl_options_get_on_error(isl_ctx *ctx); + +=head2 Identifiers + +Identifiers are used to identify both individual dimensions +and tuples of dimensions. They consist of an optional name and an optional +user pointer. The name and the user pointer cannot both be C, however. +Identifiers with the same name but different pointer values +are considered to be distinct. +Similarly, identifiers with different names but the same pointer value +are also considered to be distinct. +Equal identifiers are represented using the same object. +Pairs of identifiers can therefore be tested for equality using the +C<==> operator. +Identifiers can be constructed, copied, freed, inspected and printed +using the following functions. + + #include + __isl_give isl_id *isl_id_alloc(isl_ctx *ctx, + __isl_keep const char *name, void *user); + __isl_give isl_id *isl_id_set_free_user( + __isl_take isl_id *id, + void (*free_user)(void *user)); + __isl_give isl_id *isl_id_copy(isl_id *id); + __isl_null isl_id *isl_id_free(__isl_take isl_id *id); + + void *isl_id_get_user(__isl_keep isl_id *id); + __isl_keep const char *isl_id_get_name(__isl_keep isl_id *id); + + __isl_give isl_printer *isl_printer_print_id( + __isl_take isl_printer *p, __isl_keep isl_id *id); + +The callback set by C is called on the user +pointer when the last reference to the C is freed. +Note that C returns a pointer to some internal +data structure, so the result can only be used while the +corresponding C is alive. + +=head2 Spaces + +Whenever a new set, relation or similar object is created from scratch, +the space in which it lives needs to be specified using an C. +Each space involves zero or more parameters and zero, one or two +tuples of set or input/output dimensions. The parameters and dimensions +are identified by an C and a position. +The type C refers to parameters, +the type C refers to set dimensions (for spaces +with a single tuple of dimensions) and the types C +and C refer to input and output dimensions +(for spaces with two tuples of dimensions). +Local spaces (see L) also contain dimensions +of type C. +Note that parameters are only identified by their position within +a given object. Across different objects, parameters are (usually) +identified by their names or identifiers. Only unnamed parameters +are identified by their positions across objects. The use of unnamed +parameters is discouraged. + + #include + __isl_give isl_space *isl_space_alloc(isl_ctx *ctx, + unsigned nparam, unsigned n_in, unsigned n_out); + __isl_give isl_space *isl_space_params_alloc(isl_ctx *ctx, + unsigned nparam); + __isl_give isl_space *isl_space_set_alloc(isl_ctx *ctx, + unsigned nparam, unsigned dim); + __isl_give isl_space *isl_space_copy(__isl_keep isl_space *space); + __isl_null isl_space *isl_space_free(__isl_take isl_space *space); + +The space used for creating a parameter domain +needs to be created using C. +For other sets, the space +needs to be created using C, while +for a relation, the space +needs to be created using C. + +To check whether a given space is that of a set or a map +or whether it is a parameter space, use these functions: + + #include + isl_bool isl_space_is_params(__isl_keep isl_space *space); + isl_bool isl_space_is_set(__isl_keep isl_space *space); + isl_bool isl_space_is_map(__isl_keep isl_space *space); + +Spaces can be compared using the following functions: + + #include + isl_bool isl_space_is_equal(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); + isl_bool isl_space_is_domain(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); + isl_bool isl_space_is_range(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); + isl_bool isl_space_tuple_is_equal( + __isl_keep isl_space *space1, + enum isl_dim_type type1, + __isl_keep isl_space *space2, + enum isl_dim_type type2); + +C checks whether the first argument is equal +to the domain of the second argument. This requires in particular that +the first argument is a set space and that the second argument +is a map space. C checks whether the given +tuples (C, C or C) of the given +spaces are the same. That is, it checks if they have the same +identifier (if any), the same dimension and the same internal structure +(if any). + +It is often useful to create objects that live in the +same space as some other object. This can be accomplished +by creating the new objects +(see L or +L) based on the space +of the original object. + + #include + __isl_give isl_space *isl_basic_set_get_space( + __isl_keep isl_basic_set *bset); + __isl_give isl_space *isl_set_get_space(__isl_keep isl_set *set); + + #include + __isl_give isl_space *isl_union_set_get_space( + __isl_keep isl_union_set *uset); + + #include + __isl_give isl_space *isl_basic_map_get_space( + __isl_keep isl_basic_map *bmap); + __isl_give isl_space *isl_map_get_space(__isl_keep isl_map *map); + + #include + __isl_give isl_space *isl_union_map_get_space( + __isl_keep isl_union_map *umap); + + #include + __isl_give isl_space *isl_constraint_get_space( + __isl_keep isl_constraint *constraint); + + #include + __isl_give isl_space *isl_qpolynomial_get_domain_space( + __isl_keep isl_qpolynomial *qp); + __isl_give isl_space *isl_qpolynomial_get_space( + __isl_keep isl_qpolynomial *qp); + __isl_give isl_space * + isl_qpolynomial_fold_get_domain_space( + __isl_keep isl_qpolynomial_fold *fold); + __isl_give isl_space *isl_qpolynomial_fold_get_space( + __isl_keep isl_qpolynomial_fold *fold); + __isl_give isl_space *isl_pw_qpolynomial_get_domain_space( + __isl_keep isl_pw_qpolynomial *pwqp); + __isl_give isl_space *isl_pw_qpolynomial_get_space( + __isl_keep isl_pw_qpolynomial *pwqp); + __isl_give isl_space *isl_pw_qpolynomial_fold_get_domain_space( + __isl_keep isl_pw_qpolynomial_fold *pwf); + __isl_give isl_space *isl_pw_qpolynomial_fold_get_space( + __isl_keep isl_pw_qpolynomial_fold *pwf); + __isl_give isl_space *isl_union_pw_qpolynomial_get_space( + __isl_keep isl_union_pw_qpolynomial *upwqp); + __isl_give isl_space *isl_union_pw_qpolynomial_fold_get_space( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + + #include + __isl_give isl_space *isl_multi_val_get_space( + __isl_keep isl_multi_val *mv); + + #include + __isl_give isl_space *isl_aff_get_domain_space( + __isl_keep isl_aff *aff); + __isl_give isl_space *isl_aff_get_space( + __isl_keep isl_aff *aff); + __isl_give isl_space *isl_pw_aff_get_domain_space( + __isl_keep isl_pw_aff *pwaff); + __isl_give isl_space *isl_pw_aff_get_space( + __isl_keep isl_pw_aff *pwaff); + __isl_give isl_space *isl_multi_aff_get_domain_space( + __isl_keep isl_multi_aff *maff); + __isl_give isl_space *isl_multi_aff_get_space( + __isl_keep isl_multi_aff *maff); + __isl_give isl_space *isl_pw_multi_aff_get_domain_space( + __isl_keep isl_pw_multi_aff *pma); + __isl_give isl_space *isl_pw_multi_aff_get_space( + __isl_keep isl_pw_multi_aff *pma); + __isl_give isl_space *isl_union_pw_aff_get_space( + __isl_keep isl_union_pw_aff *upa); + __isl_give isl_space *isl_union_pw_multi_aff_get_space( + __isl_keep isl_union_pw_multi_aff *upma); + __isl_give isl_space *isl_multi_pw_aff_get_domain_space( + __isl_keep isl_multi_pw_aff *mpa); + __isl_give isl_space *isl_multi_pw_aff_get_space( + __isl_keep isl_multi_pw_aff *mpa); + __isl_give isl_space * + isl_multi_union_pw_aff_get_domain_space( + __isl_keep isl_multi_union_pw_aff *mupa); + __isl_give isl_space * + isl_multi_union_pw_aff_get_space( + __isl_keep isl_multi_union_pw_aff *mupa); + + #include + __isl_give isl_space *isl_point_get_space( + __isl_keep isl_point *pnt); + +The number of dimensions of a given type of space +may be read off from a space or an object that lives +in a space using the following functions. +In case of C, type may be +C, C (only for relations), +C (only for relations), C +(only for sets) or C. + + #include + unsigned isl_space_dim(__isl_keep isl_space *space, + enum isl_dim_type type); + + #include + int isl_local_space_dim(__isl_keep isl_local_space *ls, + enum isl_dim_type type); + + #include + unsigned isl_basic_set_dim(__isl_keep isl_basic_set *bset, + enum isl_dim_type type); + unsigned isl_set_dim(__isl_keep isl_set *set, + enum isl_dim_type type); + + #include + unsigned isl_union_set_dim(__isl_keep isl_union_set *uset, + enum isl_dim_type type); + + #include + unsigned isl_basic_map_dim(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type); + unsigned isl_map_dim(__isl_keep isl_map *map, + enum isl_dim_type type); + + #include + unsigned isl_union_map_dim(__isl_keep isl_union_map *umap, + enum isl_dim_type type); + + #include + unsigned isl_multi_val_dim(__isl_keep isl_multi_val *mv, + enum isl_dim_type type); + + #include + int isl_aff_dim(__isl_keep isl_aff *aff, + enum isl_dim_type type); + unsigned isl_multi_aff_dim(__isl_keep isl_multi_aff *maff, + enum isl_dim_type type); + unsigned isl_pw_aff_dim(__isl_keep isl_pw_aff *pwaff, + enum isl_dim_type type); + unsigned isl_pw_multi_aff_dim( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); + unsigned isl_multi_pw_aff_dim( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type); + unsigned isl_union_pw_aff_dim( + __isl_keep isl_union_pw_aff *upa, + enum isl_dim_type type); + unsigned isl_union_pw_multi_aff_dim( + __isl_keep isl_union_pw_multi_aff *upma, + enum isl_dim_type type); + unsigned isl_multi_union_pw_aff_dim( + __isl_keep isl_multi_union_pw_aff *mupa, + enum isl_dim_type type); + + #include + unsigned isl_union_pw_qpolynomial_dim( + __isl_keep isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type); + unsigned isl_union_pw_qpolynomial_fold_dim( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type); + +Note that an C, an C, +an C, +an C and +an C +only have parameters. + +The identifiers or names of the individual dimensions of spaces +may be set or read off using the following functions on spaces +or objects that live in spaces. +These functions are mostly useful to obtain the identifiers, positions +or names of the parameters. Identifiers of individual dimensions are +essentially only useful for printing. They are ignored by all other +operations and may not be preserved across those operations. + + #include + __isl_give isl_space *isl_space_set_dim_id( + __isl_take isl_space *space, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + isl_bool isl_space_has_dim_id(__isl_keep isl_space *space, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_space_get_dim_id( + __isl_keep isl_space *space, + enum isl_dim_type type, unsigned pos); + __isl_give isl_space *isl_space_set_dim_name( + __isl_take isl_space *space, + enum isl_dim_type type, unsigned pos, + __isl_keep const char *name); + isl_bool isl_space_has_dim_name(__isl_keep isl_space *space, + enum isl_dim_type type, unsigned pos); + __isl_keep const char *isl_space_get_dim_name( + __isl_keep isl_space *space, + enum isl_dim_type type, unsigned pos); + + #include + __isl_give isl_local_space *isl_local_space_set_dim_id( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + isl_bool isl_local_space_has_dim_id( + __isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_local_space_get_dim_id( + __isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); + __isl_give isl_local_space *isl_local_space_set_dim_name( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, const char *s); + isl_bool isl_local_space_has_dim_name( + __isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos) + const char *isl_local_space_get_dim_name( + __isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); + + #include + const char *isl_constraint_get_dim_name( + __isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos); + + #include + __isl_give isl_id *isl_basic_set_get_dim_id( + __isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos); + __isl_give isl_set *isl_set_set_dim_id( + __isl_take isl_set *set, enum isl_dim_type type, + unsigned pos, __isl_take isl_id *id); + isl_bool isl_set_has_dim_id(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_set_get_dim_id( + __isl_keep isl_set *set, enum isl_dim_type type, + unsigned pos); + const char *isl_basic_set_get_dim_name( + __isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos); + isl_bool isl_set_has_dim_name(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + const char *isl_set_get_dim_name( + __isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + + #include + __isl_give isl_map *isl_map_set_dim_id( + __isl_take isl_map *map, enum isl_dim_type type, + unsigned pos, __isl_take isl_id *id); + isl_bool isl_basic_map_has_dim_id( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos); + isl_bool isl_map_has_dim_id(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_map_get_dim_id( + __isl_keep isl_map *map, enum isl_dim_type type, + unsigned pos); + __isl_give isl_id *isl_union_map_get_dim_id( + __isl_keep isl_union_map *umap, + enum isl_dim_type type, unsigned pos); + const char *isl_basic_map_get_dim_name( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos); + isl_bool isl_map_has_dim_name(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); + const char *isl_map_get_dim_name( + __isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); + + #include + __isl_give isl_multi_val *isl_multi_val_set_dim_id( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + __isl_give isl_id *isl_multi_val_get_dim_id( + __isl_keep isl_multi_val *mv, + enum isl_dim_type type, unsigned pos); + __isl_give isl_multi_val *isl_multi_val_set_dim_name( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, unsigned pos, const char *s); + + #include + __isl_give isl_aff *isl_aff_set_dim_id( + __isl_take isl_aff *aff, enum isl_dim_type type, + unsigned pos, __isl_take isl_id *id); + __isl_give isl_multi_aff *isl_multi_aff_set_dim_id( + __isl_take isl_multi_aff *maff, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + __isl_give isl_pw_aff *isl_pw_aff_set_dim_id( + __isl_take isl_pw_aff *pma, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_set_dim_id( + __isl_take isl_multi_pw_aff *mpa, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_set_dim_id( + __isl_take isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, unsigned pos, + __isl_take isl_id *id); + __isl_give isl_id *isl_multi_aff_get_dim_id( + __isl_keep isl_multi_aff *ma, + enum isl_dim_type type, unsigned pos); + isl_bool isl_pw_aff_has_dim_id(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_pw_aff_get_dim_id( + __isl_keep isl_pw_aff *pa, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_pw_multi_aff_get_dim_id( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_multi_pw_aff_get_dim_id( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type, unsigned pos); + __isl_give isl_id *isl_multi_union_pw_aff_get_dim_id( + __isl_keep isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, unsigned pos); + __isl_give isl_aff *isl_aff_set_dim_name( + __isl_take isl_aff *aff, enum isl_dim_type type, + unsigned pos, const char *s); + __isl_give isl_multi_aff *isl_multi_aff_set_dim_name( + __isl_take isl_multi_aff *maff, + enum isl_dim_type type, unsigned pos, const char *s); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_set_dim_name( + __isl_take isl_multi_pw_aff *mpa, + enum isl_dim_type type, unsigned pos, const char *s); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_set_dim_name( + __isl_take isl_union_pw_aff *upa, + enum isl_dim_type type, unsigned pos, + const char *s); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_set_dim_name( + __isl_take isl_union_pw_multi_aff *upma, + enum isl_dim_type type, unsigned pos, + const char *s); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_set_dim_name( + __isl_take isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, unsigned pos, + const char *isl_aff_get_dim_name(__isl_keep isl_aff *aff, + enum isl_dim_type type, unsigned pos); + const char *isl_pw_aff_get_dim_name( + __isl_keep isl_pw_aff *pa, + enum isl_dim_type type, unsigned pos); + const char *isl_pw_multi_aff_get_dim_name( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned pos); + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_set_dim_name( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned pos, + const char *s); + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_set_dim_name( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned pos, + const char *s); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_set_dim_name( + __isl_take isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type, unsigned pos, + const char *s); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_set_dim_name( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type, unsigned pos, + const char *s); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_set_dim_name( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type, unsigned pos, + const char *s); + +Note that C returns a pointer to some internal +data structure, so the result can only be used while the +corresponding C is alive. +Also note that every function that operates on two sets or relations +requires that both arguments have the same parameters. This also +means that if one of the arguments has named parameters, then the +other needs to have named parameters too and the names need to match. +Pairs of C, C, C and/or C +arguments may have different parameters (as long as they are named), +in which case the result will have as parameters the union of the parameters of +the arguments. + +Given the identifier or name of a dimension (typically a parameter), +its position can be obtained from the following functions. + + #include + int isl_space_find_dim_by_id(__isl_keep isl_space *space, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_space_find_dim_by_name(__isl_keep isl_space *space, + enum isl_dim_type type, const char *name); + + #include + int isl_local_space_find_dim_by_name( + __isl_keep isl_local_space *ls, + enum isl_dim_type type, const char *name); + + #include + int isl_multi_val_find_dim_by_id( + __isl_keep isl_multi_val *mv, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_multi_val_find_dim_by_name( + __isl_keep isl_multi_val *mv, + enum isl_dim_type type, const char *name); + + #include + int isl_set_find_dim_by_id(__isl_keep isl_set *set, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_set_find_dim_by_name(__isl_keep isl_set *set, + enum isl_dim_type type, const char *name); + + #include + int isl_map_find_dim_by_id(__isl_keep isl_map *map, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_basic_map_find_dim_by_name( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, const char *name); + int isl_map_find_dim_by_name(__isl_keep isl_map *map, + enum isl_dim_type type, const char *name); + int isl_union_map_find_dim_by_name( + __isl_keep isl_union_map *umap, + enum isl_dim_type type, const char *name); + + #include + int isl_multi_aff_find_dim_by_id( + __isl_keep isl_multi_aff *ma, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_multi_pw_aff_find_dim_by_id( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_multi_union_pw_aff_find_dim_by_id( + __isl_keep isl_union_multi_pw_aff *mupa, + enum isl_dim_type type, __isl_keep isl_id *id); + int isl_aff_find_dim_by_name(__isl_keep isl_aff *aff, + enum isl_dim_type type, const char *name); + int isl_multi_aff_find_dim_by_name( + __isl_keep isl_multi_aff *ma, + enum isl_dim_type type, const char *name); + int isl_pw_aff_find_dim_by_name(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type, const char *name); + int isl_multi_pw_aff_find_dim_by_name( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type, const char *name); + int isl_pw_multi_aff_find_dim_by_name( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type, const char *name); + int isl_union_pw_aff_find_dim_by_name( + __isl_keep isl_union_pw_aff *upa, + enum isl_dim_type type, const char *name); + int isl_union_pw_multi_aff_find_dim_by_name( + __isl_keep isl_union_pw_multi_aff *upma, + enum isl_dim_type type, const char *name); + int isl_multi_union_pw_aff_find_dim_by_name( + __isl_keep isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, const char *name); + + #include + int isl_pw_qpolynomial_find_dim_by_name( + __isl_keep isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, const char *name); + int isl_pw_qpolynomial_fold_find_dim_by_name( + __isl_keep isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type, const char *name); + int isl_union_pw_qpolynomial_find_dim_by_name( + __isl_keep isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type, const char *name); + int isl_union_pw_qpolynomial_fold_find_dim_by_name( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type, const char *name); + +The identifiers or names of entire spaces may be set or read off +using the following functions. + + #include + __isl_give isl_space *isl_space_set_tuple_id( + __isl_take isl_space *space, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_space *isl_space_reset_tuple_id( + __isl_take isl_space *space, enum isl_dim_type type); + isl_bool isl_space_has_tuple_id( + __isl_keep isl_space *space, + enum isl_dim_type type); + __isl_give isl_id *isl_space_get_tuple_id( + __isl_keep isl_space *space, enum isl_dim_type type); + __isl_give isl_space *isl_space_set_tuple_name( + __isl_take isl_space *space, + enum isl_dim_type type, const char *s); + isl_bool isl_space_has_tuple_name( + __isl_keep isl_space *space, + enum isl_dim_type type); + const char *isl_space_get_tuple_name(__isl_keep isl_space *space, + enum isl_dim_type type); + + #include + __isl_give isl_local_space *isl_local_space_set_tuple_id( + __isl_take isl_local_space *ls, + enum isl_dim_type type, __isl_take isl_id *id); + + #include + __isl_give isl_basic_set *isl_basic_set_set_tuple_id( + __isl_take isl_basic_set *bset, + __isl_take isl_id *id); + __isl_give isl_set *isl_set_set_tuple_id( + __isl_take isl_set *set, __isl_take isl_id *id); + __isl_give isl_set *isl_set_reset_tuple_id( + __isl_take isl_set *set); + isl_bool isl_set_has_tuple_id(__isl_keep isl_set *set); + __isl_give isl_id *isl_set_get_tuple_id( + __isl_keep isl_set *set); + __isl_give isl_basic_set *isl_basic_set_set_tuple_name( + __isl_take isl_basic_set *set, const char *s); + __isl_give isl_set *isl_set_set_tuple_name( + __isl_take isl_set *set, const char *s); + const char *isl_basic_set_get_tuple_name( + __isl_keep isl_basic_set *bset); + isl_bool isl_set_has_tuple_name(__isl_keep isl_set *set); + const char *isl_set_get_tuple_name( + __isl_keep isl_set *set); + + #include + __isl_give isl_basic_map *isl_basic_map_set_tuple_id( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_map *isl_map_set_tuple_id( + __isl_take isl_map *map, enum isl_dim_type type, + __isl_take isl_id *id); + __isl_give isl_map *isl_map_reset_tuple_id( + __isl_take isl_map *map, enum isl_dim_type type); + isl_bool isl_map_has_tuple_id(__isl_keep isl_map *map, + enum isl_dim_type type); + __isl_give isl_id *isl_map_get_tuple_id( + __isl_keep isl_map *map, enum isl_dim_type type); + __isl_give isl_map *isl_map_set_tuple_name( + __isl_take isl_map *map, + enum isl_dim_type type, const char *s); + const char *isl_basic_map_get_tuple_name( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type); + __isl_give isl_basic_map *isl_basic_map_set_tuple_name( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, const char *s); + isl_bool isl_map_has_tuple_name(__isl_keep isl_map *map, + enum isl_dim_type type); + const char *isl_map_get_tuple_name( + __isl_keep isl_map *map, + enum isl_dim_type type); + + #include + __isl_give isl_multi_val *isl_multi_val_set_tuple_id( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_multi_val *isl_multi_val_reset_tuple_id( + __isl_take isl_multi_val *mv, + enum isl_dim_type type); + isl_bool isl_multi_val_has_tuple_id( + __isl_keep isl_multi_val *mv, + enum isl_dim_type type); + __isl_give isl_id *isl_multi_val_get_tuple_id( + __isl_keep isl_multi_val *mv, + enum isl_dim_type type); + __isl_give isl_multi_val *isl_multi_val_set_tuple_name( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, const char *s); + const char *isl_multi_val_get_tuple_name( + __isl_keep isl_multi_val *mv, + enum isl_dim_type type); + + #include + __isl_give isl_aff *isl_aff_set_tuple_id( + __isl_take isl_aff *aff, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_multi_aff *isl_multi_aff_set_tuple_id( + __isl_take isl_multi_aff *maff, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_pw_aff *isl_pw_aff_set_tuple_id( + __isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_tuple_id( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_set_tuple_id( + __isl_take isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, __isl_take isl_id *id); + __isl_give isl_multi_aff *isl_multi_aff_reset_tuple_id( + __isl_take isl_multi_aff *ma, + enum isl_dim_type type); + __isl_give isl_pw_aff *isl_pw_aff_reset_tuple_id( + __isl_take isl_pw_aff *pa, + enum isl_dim_type type); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_reset_tuple_id( + __isl_take isl_multi_pw_aff *mpa, + enum isl_dim_type type); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_reset_tuple_id( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_reset_tuple_id( + __isl_take isl_multi_union_pw_aff *mupa, + enum isl_dim_type type); + isl_bool isl_multi_aff_has_tuple_id( + __isl_keep isl_multi_aff *ma, + enum isl_dim_type type); + __isl_give isl_id *isl_multi_aff_get_tuple_id( + __isl_keep isl_multi_aff *ma, + enum isl_dim_type type); + isl_bool isl_pw_aff_has_tuple_id(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type); + __isl_give isl_id *isl_pw_aff_get_tuple_id( + __isl_keep isl_pw_aff *pa, + enum isl_dim_type type); + isl_bool isl_pw_multi_aff_has_tuple_id( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); + __isl_give isl_id *isl_pw_multi_aff_get_tuple_id( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); + isl_bool isl_multi_pw_aff_has_tuple_id( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type); + __isl_give isl_id *isl_multi_pw_aff_get_tuple_id( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type); + isl_bool isl_multi_union_pw_aff_has_tuple_id( + __isl_keep isl_multi_union_pw_aff *mupa, + enum isl_dim_type type); + __isl_give isl_id *isl_multi_union_pw_aff_get_tuple_id( + __isl_keep isl_multi_union_pw_aff *mupa, + enum isl_dim_type type); + __isl_give isl_multi_aff *isl_multi_aff_set_tuple_name( + __isl_take isl_multi_aff *maff, + enum isl_dim_type type, const char *s); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_set_tuple_name( + __isl_take isl_multi_pw_aff *mpa, + enum isl_dim_type type, const char *s); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_set_tuple_name( + __isl_take isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, const char *s); + const char *isl_multi_aff_get_tuple_name( + __isl_keep isl_multi_aff *multi, + enum isl_dim_type type); + isl_bool isl_pw_multi_aff_has_tuple_name( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); + const char *isl_pw_multi_aff_get_tuple_name( + __isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); + const char *isl_multi_union_pw_aff_get_tuple_name( + __isl_keep isl_multi_union_pw_aff *mupa, + enum isl_dim_type type); + +The C argument needs to be one of C, C +or C. As with C, +the C function returns a pointer to some internal +data structure. +Binary operations require the corresponding spaces of their arguments +to have the same name. + +To keep the names of all parameters and tuples, but reset the user pointers +of all the corresponding identifiers, use the following function. + + #include + __isl_give isl_space *isl_space_reset_user( + __isl_take isl_space *space); + + #include + __isl_give isl_set *isl_set_reset_user( + __isl_take isl_set *set); + + #include + __isl_give isl_map *isl_map_reset_user( + __isl_take isl_map *map); + + #include + __isl_give isl_union_set *isl_union_set_reset_user( + __isl_take isl_union_set *uset); + + #include + __isl_give isl_union_map *isl_union_map_reset_user( + __isl_take isl_union_map *umap); + + #include + __isl_give isl_multi_val *isl_multi_val_reset_user( + __isl_take isl_multi_val *mv); + + #include + __isl_give isl_multi_aff *isl_multi_aff_reset_user( + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_aff *isl_pw_aff_reset_user( + __isl_take isl_pw_aff *pa); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_reset_user( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_user( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_union_pw_aff *isl_union_pw_aff_reset_user( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_reset_user( + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_reset_user( + __isl_take isl_union_pw_multi_aff *upma); + + #include + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_reset_user( + __isl_take isl_pw_qpolynomial *pwqp); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_reset_user( + __isl_take isl_union_pw_qpolynomial *upwqp); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_reset_user( + __isl_take isl_pw_qpolynomial_fold *pwf); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_reset_user( + __isl_take isl_union_pw_qpolynomial_fold *upwf); + +Spaces can be nested. In particular, the domain of a set or +the domain or range of a relation can be a nested relation. +This process is also called I. +The functions for detecting, constructing and deconstructing +such nested spaces can be found in the wrapping properties +of L, the wrapping operations +of L and the Cartesian product operations +of L. + +Spaces can be created from other spaces +using the functions described in L +and L. + +=head2 Local Spaces + +A local space is essentially a space with +zero or more existentially quantified variables. +The local space of various objects can be obtained +using the following functions. + + #include + __isl_give isl_local_space *isl_constraint_get_local_space( + __isl_keep isl_constraint *constraint); + + #include + __isl_give isl_local_space *isl_basic_set_get_local_space( + __isl_keep isl_basic_set *bset); + + #include + __isl_give isl_local_space *isl_basic_map_get_local_space( + __isl_keep isl_basic_map *bmap); + + #include + __isl_give isl_local_space *isl_aff_get_domain_local_space( + __isl_keep isl_aff *aff); + __isl_give isl_local_space *isl_aff_get_local_space( + __isl_keep isl_aff *aff); + +A new local space can be created from a space using + + #include + __isl_give isl_local_space *isl_local_space_from_space( + __isl_take isl_space *space); + +They can be inspected, modified, copied and freed using the following functions. + + #include + isl_bool isl_local_space_is_params( + __isl_keep isl_local_space *ls); + isl_bool isl_local_space_is_set( + __isl_keep isl_local_space *ls); + __isl_give isl_space *isl_local_space_get_space( + __isl_keep isl_local_space *ls); + __isl_give isl_aff *isl_local_space_get_div( + __isl_keep isl_local_space *ls, int pos); + __isl_give isl_local_space *isl_local_space_copy( + __isl_keep isl_local_space *ls); + __isl_null isl_local_space *isl_local_space_free( + __isl_take isl_local_space *ls); + +Note that C can only be used on local spaces +of sets. + +Two local spaces can be compared using + + isl_bool isl_local_space_is_equal( + __isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2); + +Local spaces can be created from other local spaces +using the functions described in L +and L. + +=head2 Creating New Sets and Relations + +C has functions for creating some standard sets and relations. + +=over + +=item * Empty sets and relations + + __isl_give isl_basic_set *isl_basic_set_empty( + __isl_take isl_space *space); + __isl_give isl_basic_map *isl_basic_map_empty( + __isl_take isl_space *space); + __isl_give isl_set *isl_set_empty( + __isl_take isl_space *space); + __isl_give isl_map *isl_map_empty( + __isl_take isl_space *space); + __isl_give isl_union_set *isl_union_set_empty( + __isl_take isl_space *space); + __isl_give isl_union_map *isl_union_map_empty( + __isl_take isl_space *space); + +For Cs and Cs, the space +is only used to specify the parameters. + +=item * Universe sets and relations + + __isl_give isl_basic_set *isl_basic_set_universe( + __isl_take isl_space *space); + __isl_give isl_basic_map *isl_basic_map_universe( + __isl_take isl_space *space); + __isl_give isl_set *isl_set_universe( + __isl_take isl_space *space); + __isl_give isl_map *isl_map_universe( + __isl_take isl_space *space); + __isl_give isl_union_set *isl_union_set_universe( + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_universe( + __isl_take isl_union_map *umap); + +The sets and relations constructed by the functions above +contain all integer values, while those constructed by the +functions below only contain non-negative values. + + __isl_give isl_basic_set *isl_basic_set_nat_universe( + __isl_take isl_space *space); + __isl_give isl_basic_map *isl_basic_map_nat_universe( + __isl_take isl_space *space); + __isl_give isl_set *isl_set_nat_universe( + __isl_take isl_space *space); + __isl_give isl_map *isl_map_nat_universe( + __isl_take isl_space *space); + +=item * Identity relations + + __isl_give isl_basic_map *isl_basic_map_identity( + __isl_take isl_space *space); + __isl_give isl_map *isl_map_identity( + __isl_take isl_space *space); + +The number of input and output dimensions in C needs +to be the same. + +=item * Lexicographic order + + __isl_give isl_map *isl_map_lex_lt( + __isl_take isl_space *set_space); + __isl_give isl_map *isl_map_lex_le( + __isl_take isl_space *set_space); + __isl_give isl_map *isl_map_lex_gt( + __isl_take isl_space *set_space); + __isl_give isl_map *isl_map_lex_ge( + __isl_take isl_space *set_space); + __isl_give isl_map *isl_map_lex_lt_first( + __isl_take isl_space *space, unsigned n); + __isl_give isl_map *isl_map_lex_le_first( + __isl_take isl_space *space, unsigned n); + __isl_give isl_map *isl_map_lex_gt_first( + __isl_take isl_space *space, unsigned n); + __isl_give isl_map *isl_map_lex_ge_first( + __isl_take isl_space *space, unsigned n); + +The first four functions take a space for a B +and return relations that express that the elements in the domain +are lexicographically less +(C), less or equal (C), +greater (C) or greater or equal (C) +than the elements in the range. +The last four functions take a space for a map +and return relations that express that the first C dimensions +in the domain are lexicographically less +(C), less or equal (C), +greater (C) or greater or equal (C) +than the first C dimensions in the range. + +=back + +A basic set or relation can be converted to a set or relation +using the following functions. + + __isl_give isl_set *isl_set_from_basic_set( + __isl_take isl_basic_set *bset); + __isl_give isl_map *isl_map_from_basic_map( + __isl_take isl_basic_map *bmap); + +Sets and relations can be converted to union sets and relations +using the following functions. + + __isl_give isl_union_set *isl_union_set_from_basic_set( + __isl_take isl_basic_set *bset); + __isl_give isl_union_map *isl_union_map_from_basic_map( + __isl_take isl_basic_map *bmap); + __isl_give isl_union_set *isl_union_set_from_set( + __isl_take isl_set *set); + __isl_give isl_union_map *isl_union_map_from_map( + __isl_take isl_map *map); + +The inverse conversions below can only be used if the input +union set or relation is known to contain elements in exactly one +space. + + __isl_give isl_set *isl_set_from_union_set( + __isl_take isl_union_set *uset); + __isl_give isl_map *isl_map_from_union_map( + __isl_take isl_union_map *umap); + +Sets and relations can be copied and freed again using the following +functions. + + __isl_give isl_basic_set *isl_basic_set_copy( + __isl_keep isl_basic_set *bset); + __isl_give isl_set *isl_set_copy(__isl_keep isl_set *set); + __isl_give isl_union_set *isl_union_set_copy( + __isl_keep isl_union_set *uset); + __isl_give isl_basic_map *isl_basic_map_copy( + __isl_keep isl_basic_map *bmap); + __isl_give isl_map *isl_map_copy(__isl_keep isl_map *map); + __isl_give isl_union_map *isl_union_map_copy( + __isl_keep isl_union_map *umap); + __isl_null isl_basic_set *isl_basic_set_free( + __isl_take isl_basic_set *bset); + __isl_null isl_set *isl_set_free(__isl_take isl_set *set); + __isl_null isl_union_set *isl_union_set_free( + __isl_take isl_union_set *uset); + __isl_null isl_basic_map *isl_basic_map_free( + __isl_take isl_basic_map *bmap); + __isl_null isl_map *isl_map_free(__isl_take isl_map *map); + __isl_null isl_union_map *isl_union_map_free( + __isl_take isl_union_map *umap); + +Other sets and relations can be constructed by starting +from a universe set or relation, adding equality and/or +inequality constraints and then projecting out the +existentially quantified variables, if any. +Constraints can be constructed, manipulated and +added to (or removed from) (basic) sets and relations +using the following functions. + + #include + __isl_give isl_constraint *isl_constraint_alloc_equality( + __isl_take isl_local_space *ls); + __isl_give isl_constraint *isl_constraint_alloc_inequality( + __isl_take isl_local_space *ls); + __isl_give isl_constraint *isl_constraint_set_constant_si( + __isl_take isl_constraint *constraint, int v); + __isl_give isl_constraint *isl_constraint_set_constant_val( + __isl_take isl_constraint *constraint, + __isl_take isl_val *v); + __isl_give isl_constraint *isl_constraint_set_coefficient_si( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, int v); + __isl_give isl_constraint * + isl_constraint_set_coefficient_val( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, + __isl_take isl_val *v); + __isl_give isl_basic_map *isl_basic_map_add_constraint( + __isl_take isl_basic_map *bmap, + __isl_take isl_constraint *constraint); + __isl_give isl_basic_set *isl_basic_set_add_constraint( + __isl_take isl_basic_set *bset, + __isl_take isl_constraint *constraint); + __isl_give isl_map *isl_map_add_constraint( + __isl_take isl_map *map, + __isl_take isl_constraint *constraint); + __isl_give isl_set *isl_set_add_constraint( + __isl_take isl_set *set, + __isl_take isl_constraint *constraint); + +For example, to create a set containing the even integers +between 10 and 42, you would use the following code. + + isl_space *space; + isl_local_space *ls; + isl_constraint *c; + isl_basic_set *bset; + + space = isl_space_set_alloc(ctx, 0, 2); + bset = isl_basic_set_universe(isl_space_copy(space)); + ls = isl_local_space_from_space(space); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + c = isl_constraint_set_coefficient_si(c, isl_dim_set, 0, -1); + c = isl_constraint_set_coefficient_si(c, isl_dim_set, 1, 2); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_inequality(isl_local_space_copy(ls)); + c = isl_constraint_set_constant_si(c, -10); + c = isl_constraint_set_coefficient_si(c, isl_dim_set, 0, 1); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_inequality(ls); + c = isl_constraint_set_constant_si(c, 42); + c = isl_constraint_set_coefficient_si(c, isl_dim_set, 0, -1); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 1, 1); + +Or, alternatively, + + isl_basic_set *bset; + bset = isl_basic_set_read_from_str(ctx, + "{[i] : exists (a : i = 2a and i >= 10 and i <= 42)}"); + +A basic set or relation can also be constructed from two matrices +describing the equalities and the inequalities. + + __isl_give isl_basic_set *isl_basic_set_from_constraint_matrices( + __isl_take isl_space *space, + __isl_take isl_mat *eq, __isl_take isl_mat *ineq, + enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4); + __isl_give isl_basic_map *isl_basic_map_from_constraint_matrices( + __isl_take isl_space *space, + __isl_take isl_mat *eq, __isl_take isl_mat *ineq, + enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5); + +The C arguments indicate the order in which +different kinds of variables appear in the input matrices +and should be a permutation of C, C, +C and C for sets and +of C, C, +C, C and C for relations. + +A (basic or union) set or relation can also be constructed from a +(union) (piecewise) (multiple) affine expression +or a list of affine expressions +(See L), provided these affine expressions do not +involve any NaN. + + __isl_give isl_basic_map *isl_basic_map_from_aff( + __isl_take isl_aff *aff); + __isl_give isl_map *isl_map_from_aff( + __isl_take isl_aff *aff); + __isl_give isl_set *isl_set_from_pw_aff( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_map *isl_map_from_pw_aff( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_basic_map *isl_basic_map_from_aff_list( + __isl_take isl_space *domain_space, + __isl_take isl_aff_list *list); + __isl_give isl_basic_map *isl_basic_map_from_multi_aff( + __isl_take isl_multi_aff *maff) + __isl_give isl_map *isl_map_from_multi_aff( + __isl_take isl_multi_aff *maff) + __isl_give isl_set *isl_set_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_map *isl_map_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_set *isl_set_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_map *isl_map_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_union_map *isl_union_map_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_map * + isl_union_map_from_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_union_map * + isl_union_map_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa); + +The C argument describes the domain of the resulting +basic relation. It is required because the C may consist +of zero affine expressions. +The C passed to C +is not allowed to be zero-dimensional. The domain of the result +is the shared domain of the union piecewise affine elements. + +=head2 Inspecting Sets and Relations + +Usually, the user should not have to care about the actual constraints +of the sets and maps, but should instead apply the abstract operations +explained in the following sections. +Occasionally, however, it may be required to inspect the individual +coefficients of the constraints. This section explains how to do so. +In these cases, it may also be useful to have C compute +an explicit representation of the existentially quantified variables. + + __isl_give isl_set *isl_set_compute_divs( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_compute_divs( + __isl_take isl_map *map); + __isl_give isl_union_set *isl_union_set_compute_divs( + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_compute_divs( + __isl_take isl_union_map *umap); + +This explicit representation defines the existentially quantified +variables as integer divisions of the other variables, possibly +including earlier existentially quantified variables. +An explicitly represented existentially quantified variable therefore +has a unique value when the values of the other variables are known. +If, furthermore, the same existentials, i.e., existentials +with the same explicit representations, should appear in the +same order in each of the disjuncts of a set or map, then the user should call +either of the following functions. + + __isl_give isl_set *isl_set_align_divs( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_align_divs( + __isl_take isl_map *map); + +Alternatively, the existentially quantified variables can be removed +using the following functions, which compute an overapproximation. + + __isl_give isl_basic_set *isl_basic_set_remove_divs( + __isl_take isl_basic_set *bset); + __isl_give isl_basic_map *isl_basic_map_remove_divs( + __isl_take isl_basic_map *bmap); + __isl_give isl_set *isl_set_remove_divs( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_remove_divs( + __isl_take isl_map *map); + +It is also possible to only remove those divs that are defined +in terms of a given range of dimensions or only those for which +no explicit representation is known. + + __isl_give isl_basic_set * + isl_basic_set_remove_divs_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_basic_map * + isl_basic_map_remove_divs_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_set *isl_set_remove_divs_involving_dims( + __isl_take isl_set *set, enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_map *isl_map_remove_divs_involving_dims( + __isl_take isl_map *map, enum isl_dim_type type, + unsigned first, unsigned n); + + __isl_give isl_basic_set * + isl_basic_set_remove_unknown_divs( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_remove_unknown_divs( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_remove_unknown_divs( + __isl_take isl_map *map); + +To iterate over all the sets or maps in a union set or map, use + + isl_stat isl_union_set_foreach_set( + __isl_keep isl_union_set *uset, + isl_stat (*fn)(__isl_take isl_set *set, void *user), + void *user); + isl_stat isl_union_map_foreach_map( + __isl_keep isl_union_map *umap, + isl_stat (*fn)(__isl_take isl_map *map, void *user), + void *user); + +These functions call the callback function once for each +(pair of) space(s) for which there are elements in the input. +The argument to the callback contains all elements in the input +with that (pair of) space(s). + +The number of sets or maps in a union set or map can be obtained +from + + int isl_union_set_n_set(__isl_keep isl_union_set *uset); + int isl_union_map_n_map(__isl_keep isl_union_map *umap); + +To extract the set or map in a given space from a union, use + + __isl_give isl_set *isl_union_set_extract_set( + __isl_keep isl_union_set *uset, + __isl_take isl_space *space); + __isl_give isl_map *isl_union_map_extract_map( + __isl_keep isl_union_map *umap, + __isl_take isl_space *space); + +To iterate over all the basic sets or maps in a set or map, use + + isl_stat isl_set_foreach_basic_set(__isl_keep isl_set *set, + isl_stat (*fn)(__isl_take isl_basic_set *bset, + void *user), + void *user); + isl_stat isl_map_foreach_basic_map(__isl_keep isl_map *map, + isl_stat (*fn)(__isl_take isl_basic_map *bmap, + void *user), + void *user); + +The callback function C should return 0 if successful and +-1 if an error occurs. In the latter case, or if any other error +occurs, the above functions will return -1. + +It should be noted that C does not guarantee that +the basic sets or maps passed to C are disjoint. +If this is required, then the user should call one of +the following functions first. + + __isl_give isl_set *isl_set_make_disjoint( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_make_disjoint( + __isl_take isl_map *map); + +The number of basic sets in a set can be obtained +or the number of basic maps in a map can be obtained +from + + #include + int isl_set_n_basic_set(__isl_keep isl_set *set); + + #include + int isl_map_n_basic_map(__isl_keep isl_map *map); + +It is also possible to obtain a list of basic sets from a set + + #include + __isl_give isl_basic_set_list *isl_set_get_basic_set_list( + __isl_keep isl_set *set); + +The returned list can be manipulated using the functions in L<"Lists">. + +To iterate over the constraints of a basic set or map, use + + #include + + int isl_basic_set_n_constraint( + __isl_keep isl_basic_set *bset); + isl_stat isl_basic_set_foreach_constraint( + __isl_keep isl_basic_set *bset, + isl_stat (*fn)(__isl_take isl_constraint *c, + void *user), + void *user); + int isl_basic_map_n_constraint( + __isl_keep isl_basic_map *bmap); + isl_stat isl_basic_map_foreach_constraint( + __isl_keep isl_basic_map *bmap, + isl_stat (*fn)(__isl_take isl_constraint *c, + void *user), + void *user); + __isl_null isl_constraint *isl_constraint_free( + __isl_take isl_constraint *c); + +Again, the callback function C should return 0 if successful and +-1 if an error occurs. In the latter case, or if any other error +occurs, the above functions will return -1. +The constraint C represents either an equality or an inequality. +Use the following function to find out whether a constraint +represents an equality. If not, it represents an inequality. + + isl_bool isl_constraint_is_equality( + __isl_keep isl_constraint *constraint); + +It is also possible to obtain a list of constraints from a basic +map or set + + #include + __isl_give isl_constraint_list * + isl_basic_map_get_constraint_list( + __isl_keep isl_basic_map *bmap); + __isl_give isl_constraint_list * + isl_basic_set_get_constraint_list( + __isl_keep isl_basic_set *bset); + +These functions require that all existentially quantified variables +have an explicit representation. +The returned list can be manipulated using the functions in L<"Lists">. + +The coefficients of the constraints can be inspected using +the following functions. + + isl_bool isl_constraint_is_lower_bound( + __isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos); + isl_bool isl_constraint_is_upper_bound( + __isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos); + __isl_give isl_val *isl_constraint_get_constant_val( + __isl_keep isl_constraint *constraint); + __isl_give isl_val *isl_constraint_get_coefficient_val( + __isl_keep isl_constraint *constraint, + enum isl_dim_type type, int pos); + +The explicit representations of the existentially quantified +variables can be inspected using the following function. +Note that the user is only allowed to use this function +if the inspected set or map is the result of a call +to C or C. +The existentially quantified variable is equal to the floor +of the returned affine expression. The affine expression +itself can be inspected using the functions in +L. + + __isl_give isl_aff *isl_constraint_get_div( + __isl_keep isl_constraint *constraint, int pos); + +To obtain the constraints of a basic set or map in matrix +form, use the following functions. + + __isl_give isl_mat *isl_basic_set_equalities_matrix( + __isl_keep isl_basic_set *bset, + enum isl_dim_type c1, enum isl_dim_type c2, + enum isl_dim_type c3, enum isl_dim_type c4); + __isl_give isl_mat *isl_basic_set_inequalities_matrix( + __isl_keep isl_basic_set *bset, + enum isl_dim_type c1, enum isl_dim_type c2, + enum isl_dim_type c3, enum isl_dim_type c4); + __isl_give isl_mat *isl_basic_map_equalities_matrix( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5); + __isl_give isl_mat *isl_basic_map_inequalities_matrix( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5); + +The C arguments dictate the order in which +different kinds of variables appear in the resulting matrix. +For set inputs, they should be a permutation of +C, C, C and C. +For map inputs, they should be a permutation of +C, C, +C, C and C. + +=head2 Points + +Points are elements of a set. They can be used to construct +simple sets (boxes) or they can be used to represent the +individual elements of a set. +The zero point (the origin) can be created using + + __isl_give isl_point *isl_point_zero(__isl_take isl_space *space); + +The coordinates of a point can be inspected, set and changed +using + + __isl_give isl_val *isl_point_get_coordinate_val( + __isl_keep isl_point *pnt, + enum isl_dim_type type, int pos); + __isl_give isl_point *isl_point_set_coordinate_val( + __isl_take isl_point *pnt, + enum isl_dim_type type, int pos, + __isl_take isl_val *v); + + __isl_give isl_point *isl_point_add_ui( + __isl_take isl_point *pnt, + enum isl_dim_type type, int pos, unsigned val); + __isl_give isl_point *isl_point_sub_ui( + __isl_take isl_point *pnt, + enum isl_dim_type type, int pos, unsigned val); + +Points can be copied or freed using + + __isl_give isl_point *isl_point_copy( + __isl_keep isl_point *pnt); + void isl_point_free(__isl_take isl_point *pnt); + +A singleton set can be created from a point using + + __isl_give isl_basic_set *isl_basic_set_from_point( + __isl_take isl_point *pnt); + __isl_give isl_set *isl_set_from_point( + __isl_take isl_point *pnt); + __isl_give isl_union_set *isl_union_set_from_point( + __isl_take isl_point *pnt); + +and a box can be created from two opposite extremal points using + + __isl_give isl_basic_set *isl_basic_set_box_from_points( + __isl_take isl_point *pnt1, + __isl_take isl_point *pnt2); + __isl_give isl_set *isl_set_box_from_points( + __isl_take isl_point *pnt1, + __isl_take isl_point *pnt2); + +All elements of a B (union) set can be enumerated using +the following functions. + + isl_stat isl_set_foreach_point(__isl_keep isl_set *set, + isl_stat (*fn)(__isl_take isl_point *pnt, + void *user), + void *user); + isl_stat isl_union_set_foreach_point( + __isl_keep isl_union_set *uset, + isl_stat (*fn)(__isl_take isl_point *pnt, + void *user), + void *user); + +The function C is called for each integer point in +C with as second argument the last argument of +the C call. The function C +should return C<0> on success and C<-1> on failure. +In the latter case, C will stop +enumerating and return C<-1> as well. +If the enumeration is performed successfully and to completion, +then C returns C<0>. + +To obtain a single point of a (basic or union) set, use + + __isl_give isl_point *isl_basic_set_sample_point( + __isl_take isl_basic_set *bset); + __isl_give isl_point *isl_set_sample_point( + __isl_take isl_set *set); + __isl_give isl_point *isl_union_set_sample_point( + __isl_take isl_union_set *uset); + +If C does not contain any (integer) points, then the +resulting point will be ``void'', a property that can be +tested using + + isl_bool isl_point_is_void(__isl_keep isl_point *pnt); + +=head2 Functions + +Besides sets and relation, C also supports various types of functions. +Each of these types is derived from the value type (see L) +or from one of two primitive function types +through the application of zero or more type constructors. +We first describe the primitive type and then we describe +the types derived from these primitive types. + +=head3 Primitive Functions + +C support two primitive function types, quasi-affine +expressions and quasipolynomials. +A quasi-affine expression is defined either over a parameter +space or over a set and is composed of integer constants, +parameters and set variables, addition, subtraction and +integer division by an integer constant. +For example, the quasi-affine expression + + [n] -> { [x] -> [2*floor((4 n + x)/9] } + +maps C to C<2*floor((4 n + x)/9>. +A quasipolynomial is a polynomial expression in quasi-affine +expression. That is, it additionally allows for multiplication. +Note, though, that it is not allowed to construct an integer +division of an expression involving multiplications. +Here is an example of a quasipolynomial that is not +quasi-affine expression + + [n] -> { [x] -> (n*floor((4 n + x)/9) } + +Note that the external representations of quasi-affine expressions +and quasipolynomials are different. Quasi-affine expressions +use a notation with square brackets just like binary relations, +while quasipolynomials do not. This might change at some point. + +If a primitive function is defined over a parameter space, +then the space of the function itself is that of a set. +If it is defined over a set, then the space of the function +is that of a relation. In both cases, the set space (or +the output space) is single-dimensional, anonymous and unstructured. +To create functions with multiple dimensions or with other kinds +of set or output spaces, use multiple expressions +(see L). + +=over + +=item * Quasi-affine Expressions + +Besides the expressions described above, a quasi-affine +expression can also be set to NaN. Such expressions +typically represent a failure to represent a result +as a quasi-affine expression. + +The zero quasi affine expression or the quasi affine expression +that is equal to a given value or +a specified dimension on a given domain can be created using + + #include + __isl_give isl_aff *isl_aff_zero_on_domain( + __isl_take isl_local_space *ls); + __isl_give isl_aff *isl_aff_val_on_domain( + __isl_take isl_local_space *ls, + __isl_take isl_val *val); + __isl_give isl_aff *isl_aff_var_on_domain( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos); + __isl_give isl_aff *isl_aff_nan_on_domain( + __isl_take isl_local_space *ls); + +Quasi affine expressions can be copied and freed using + + #include + __isl_give isl_aff *isl_aff_copy( + __isl_keep isl_aff *aff); + __isl_null isl_aff *isl_aff_free( + __isl_take isl_aff *aff); + +A (rational) bound on a dimension can be extracted from an C +using the following function. The constraint is required to have +a non-zero coefficient for the specified dimension. + + #include + __isl_give isl_aff *isl_constraint_get_bound( + __isl_keep isl_constraint *constraint, + enum isl_dim_type type, int pos); + +The entire affine expression of the constraint can also be extracted +using the following function. + + #include + __isl_give isl_aff *isl_constraint_get_aff( + __isl_keep isl_constraint *constraint); + +Conversely, an equality constraint equating +the affine expression to zero or an inequality constraint enforcing +the affine expression to be non-negative, can be constructed using + + __isl_give isl_constraint *isl_equality_from_aff( + __isl_take isl_aff *aff); + __isl_give isl_constraint *isl_inequality_from_aff( + __isl_take isl_aff *aff); + +The coefficients and the integer divisions of an affine expression +can be inspected using the following functions. + + #include + __isl_give isl_val *isl_aff_get_constant_val( + __isl_keep isl_aff *aff); + __isl_give isl_val *isl_aff_get_coefficient_val( + __isl_keep isl_aff *aff, + enum isl_dim_type type, int pos); + int isl_aff_coefficient_sgn(__isl_keep isl_aff *aff, + enum isl_dim_type type, int pos); + __isl_give isl_val *isl_aff_get_denominator_val( + __isl_keep isl_aff *aff); + __isl_give isl_aff *isl_aff_get_div( + __isl_keep isl_aff *aff, int pos); + +They can be modified using the following functions. + + #include + __isl_give isl_aff *isl_aff_set_constant_si( + __isl_take isl_aff *aff, int v); + __isl_give isl_aff *isl_aff_set_constant_val( + __isl_take isl_aff *aff, __isl_take isl_val *v); + __isl_give isl_aff *isl_aff_set_coefficient_si( + __isl_take isl_aff *aff, + enum isl_dim_type type, int pos, int v); + __isl_give isl_aff *isl_aff_set_coefficient_val( + __isl_take isl_aff *aff, + enum isl_dim_type type, int pos, + __isl_take isl_val *v); + + __isl_give isl_aff *isl_aff_add_constant_si( + __isl_take isl_aff *aff, int v); + __isl_give isl_aff *isl_aff_add_constant_val( + __isl_take isl_aff *aff, __isl_take isl_val *v); + __isl_give isl_aff *isl_aff_add_constant_num_si( + __isl_take isl_aff *aff, int v); + __isl_give isl_aff *isl_aff_add_coefficient_si( + __isl_take isl_aff *aff, + enum isl_dim_type type, int pos, int v); + __isl_give isl_aff *isl_aff_add_coefficient_val( + __isl_take isl_aff *aff, + enum isl_dim_type type, int pos, + __isl_take isl_val *v); + +Note that C and C +set the I of the constant or coefficient, while +C and C set +the constant or coefficient as a whole. +The C and C functions add an integer +or rational value to +the possibly rational constant or coefficient. +The C functions add an integer value to +the numerator. + +=item * Quasipolynomials + +Some simple quasipolynomials can be created using the following functions. + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_zero_on_domain( + __isl_take isl_space *domain); + __isl_give isl_qpolynomial *isl_qpolynomial_one_on_domain( + __isl_take isl_space *domain); + __isl_give isl_qpolynomial *isl_qpolynomial_infty_on_domain( + __isl_take isl_space *domain); + __isl_give isl_qpolynomial *isl_qpolynomial_neginfty_on_domain( + __isl_take isl_space *domain); + __isl_give isl_qpolynomial *isl_qpolynomial_nan_on_domain( + __isl_take isl_space *domain); + __isl_give isl_qpolynomial *isl_qpolynomial_val_on_domain( + __isl_take isl_space *domain, + __isl_take isl_val *val); + __isl_give isl_qpolynomial *isl_qpolynomial_var_on_domain( + __isl_take isl_space *domain, + enum isl_dim_type type, unsigned pos); + __isl_give isl_qpolynomial *isl_qpolynomial_from_aff( + __isl_take isl_aff *aff); + +Recall that the space in which a quasipolynomial lives is a map space +with a one-dimensional range. The C argument in some of +the functions above corresponds to the domain of this map space. + +Quasipolynomials can be copied and freed again using the following +functions. + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_copy( + __isl_keep isl_qpolynomial *qp); + __isl_null isl_qpolynomial *isl_qpolynomial_free( + __isl_take isl_qpolynomial *qp); + +The constant term of a quasipolynomial can be extracted using + + __isl_give isl_val *isl_qpolynomial_get_constant_val( + __isl_keep isl_qpolynomial *qp); + +To iterate over all terms in a quasipolynomial, +use + + isl_stat isl_qpolynomial_foreach_term( + __isl_keep isl_qpolynomial *qp, + isl_stat (*fn)(__isl_take isl_term *term, + void *user), void *user); + +The terms themselves can be inspected and freed using +these functions + + unsigned isl_term_dim(__isl_keep isl_term *term, + enum isl_dim_type type); + __isl_give isl_val *isl_term_get_coefficient_val( + __isl_keep isl_term *term); + int isl_term_get_exp(__isl_keep isl_term *term, + enum isl_dim_type type, unsigned pos); + __isl_give isl_aff *isl_term_get_div( + __isl_keep isl_term *term, unsigned pos); + void isl_term_free(__isl_take isl_term *term); + +Each term is a product of parameters, set variables and +integer divisions. The function C +returns the exponent of a given dimensions in the given term. + +=back + +=head3 Reductions + +A reduction represents a maximum or a minimum of its +base expressions. +The only reduction type defined by C is +C. + +There are currently no functions to directly create such +objects, but they do appear in the piecewise quasipolynomial +reductions returned by the C function. +See +L. + +Reductions can be copied and freed using +the following functions. + + #include + __isl_give isl_qpolynomial_fold * + isl_qpolynomial_fold_copy( + __isl_keep isl_qpolynomial_fold *fold); + void isl_qpolynomial_fold_free( + __isl_take isl_qpolynomial_fold *fold); + +To iterate over all quasipolynomials in a reduction, use + + isl_stat isl_qpolynomial_fold_foreach_qpolynomial( + __isl_keep isl_qpolynomial_fold *fold, + isl_stat (*fn)(__isl_take isl_qpolynomial *qp, + void *user), void *user); + +=head3 Multiple Expressions + +A multiple expression represents a sequence of zero or +more base expressions, all defined on the same domain space. +The domain space of the multiple expression is the same +as that of the base expressions, but the range space +can be any space. In case the base expressions have +a set space, the corresponding multiple expression +also has a set space. +Objects of the value type do not have an associated space. +The space of a multiple value is therefore always a set space. +Similarly, the space of a multiple union piecewise +affine expression is always a set space. + +The multiple expression types defined by C +are C, C, C, +C. + +A multiple expression with the value zero for +each output (or set) dimension can be created +using the following functions. + + #include + __isl_give isl_multi_val *isl_multi_val_zero( + __isl_take isl_space *space); + + #include + __isl_give isl_multi_aff *isl_multi_aff_zero( + __isl_take isl_space *space); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_zero( + __isl_take isl_space *space); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_zero( + __isl_take isl_space *space); + +Since there is no canonical way of representing a zero +value of type C, the space passed +to C needs to be zero-dimensional. + +An identity function can be created using the following +functions. The space needs to be that of a relation +with the same number of input and output dimensions. + + #include + __isl_give isl_multi_aff *isl_multi_aff_identity( + __isl_take isl_space *space); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_identity( + __isl_take isl_space *space); + +A function that performs a projection on a universe +relation or set can be created using the following functions. +See also the corresponding +projection operations in L. + + #include + __isl_give isl_multi_aff *isl_multi_aff_domain_map( + __isl_take isl_space *space); + __isl_give isl_multi_aff *isl_multi_aff_range_map( + __isl_take isl_space *space); + __isl_give isl_multi_aff *isl_multi_aff_project_out_map( + __isl_take isl_space *space, + enum isl_dim_type type, + unsigned first, unsigned n); + +A multiple expression can be created from a single +base expression using the following functions. +The space of the created multiple expression is the same +as that of the base expression, except for +C where the input +lives in a parameter space and the output lives +in a single-dimensional set space. + + #include + __isl_give isl_multi_aff *isl_multi_aff_from_aff( + __isl_take isl_aff *aff); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_aff( + __isl_take isl_pw_aff *pa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa); + +A multiple expression can be created from a list +of base expression in a specified space. +The domain of this space needs to be the same +as the domains of the base expressions in the list. +If the base expressions have a set space (or no associated space), +then this space also needs to be a set space. + + #include + __isl_give isl_multi_val *isl_multi_val_from_val_list( + __isl_take isl_space *space, + __isl_take isl_val_list *list); + + #include + __isl_give isl_multi_aff *isl_multi_aff_from_aff_list( + __isl_take isl_space *space, + __isl_take isl_aff_list *list); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_from_pw_aff_list( + __isl_take isl_space *space, + __isl_take isl_pw_aff_list *list); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_union_pw_aff_list( + __isl_take isl_space *space, + __isl_take isl_union_pw_aff_list *list); + +As a convenience, a multiple piecewise expression can +also be created from a multiple expression. +Each piecewise expression in the result has a single +universe cell. + + #include + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_from_multi_aff( + __isl_take isl_multi_aff *ma); + +Similarly, a multiple union expression can be +created from a multiple expression. + + #include + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_multi_aff( + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa); + +A multiple quasi-affine expression can be created from +a multiple value with a given domain space using the following +function. + + #include + __isl_give isl_multi_aff * + isl_multi_aff_multi_val_on_space( + __isl_take isl_space *space, + __isl_take isl_multi_val *mv); + +Similarly, +a multiple union piecewise affine expression can be created from +a multiple value with a given domain or +a multiple affine expression with a given domain +using the following functions. + + #include + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_multi_val_on_domain( + __isl_take isl_union_set *domain, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_multi_aff_on_domain( + __isl_take isl_union_set *domain, + __isl_take isl_multi_aff *ma); + +Multiple expressions can be copied and freed using +the following functions. + + #include + __isl_give isl_multi_val *isl_multi_val_copy( + __isl_keep isl_multi_val *mv); + __isl_null isl_multi_val *isl_multi_val_free( + __isl_take isl_multi_val *mv); + + #include + __isl_give isl_multi_aff *isl_multi_aff_copy( + __isl_keep isl_multi_aff *maff); + __isl_null isl_multi_aff *isl_multi_aff_free( + __isl_take isl_multi_aff *maff); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_copy( + __isl_keep isl_multi_pw_aff *mpa); + __isl_null isl_multi_pw_aff *isl_multi_pw_aff_free( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_copy( + __isl_keep isl_multi_union_pw_aff *mupa); + __isl_null isl_multi_union_pw_aff * + isl_multi_union_pw_aff_free( + __isl_take isl_multi_union_pw_aff *mupa); + +The base expression at a given position of a multiple +expression can be extracted using the following functions. + + #include + __isl_give isl_val *isl_multi_val_get_val( + __isl_keep isl_multi_val *mv, int pos); + + #include + __isl_give isl_aff *isl_multi_aff_get_aff( + __isl_keep isl_multi_aff *multi, int pos); + __isl_give isl_pw_aff *isl_multi_pw_aff_get_pw_aff( + __isl_keep isl_multi_pw_aff *mpa, int pos); + __isl_give isl_union_pw_aff * + isl_multi_union_pw_aff_get_union_pw_aff( + __isl_keep isl_multi_union_pw_aff *mupa, int pos); + +It can be replaced using the following functions. + + #include + __isl_give isl_multi_val *isl_multi_val_set_val( + __isl_take isl_multi_val *mv, int pos, + __isl_take isl_val *val); + + #include + __isl_give isl_multi_aff *isl_multi_aff_set_aff( + __isl_take isl_multi_aff *multi, int pos, + __isl_take isl_aff *aff); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_set_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa, int pos, + __isl_take isl_union_pw_aff *upa); + +As a convenience, a sequence of base expressions that have +their domains in a given space can be extracted from a sequence +of union expressions using the following function. + + #include + __isl_give isl_multi_pw_aff * + isl_multi_union_pw_aff_extract_multi_pw_aff( + __isl_keep isl_multi_union_pw_aff *mupa, + __isl_take isl_space *space); + +Note that there is a difference between C +and C objects. The first is a sequence +of unions of piecewise expressions, while the second is a union +of piecewise sequences. In particular, multiple affine expressions +in an C may live in different spaces, +while there is only a single multiple expression in +an C, which can therefore only live +in a single space. This means that not every +C can be converted to +an C. Conversely, a zero-dimensional +C carries no information +about any possible domain and therefore cannot be converted +to an C. Moreover, the elements +of an C may be defined over different domains, +while each multiple expression inside an C +has a single domain. The conversion of an C +of dimension greater than one may therefore not be exact. +The following functions can +be used to perform these conversions when they are possible. + + #include + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa); + +=head3 Piecewise Expressions + +A piecewise expression is an expression that is described +using zero or more base expression defined over the same +number of cells in the domain space of the base expressions. +All base expressions are defined over the same +domain space and the cells are disjoint. +The space of a piecewise expression is the same as +that of the base expressions. +If the union of the cells is a strict subset of the domain +space, then the value of the piecewise expression outside +this union is different for types derived from quasi-affine +expressions and those derived from quasipolynomials. +Piecewise expressions derived from quasi-affine expressions +are considered to be undefined outside the union of their cells. +Piecewise expressions derived from quasipolynomials +are considered to be zero outside the union of their cells. + +Piecewise quasipolynomials are mainly used by the C +library for representing the number of elements in a parametric set or map. +For example, the piecewise quasipolynomial + + [n] -> { [x] -> ((1 + n) - x) : x <= n and x >= 0 } + +represents the number of points in the map + + [n] -> { [x] -> [y] : x,y >= 0 and 0 <= x + y <= n } + +The piecewise expression types defined by C +are C, C, +C and C. + +A piecewise expression with no cells can be created using +the following functions. + + #include + __isl_give isl_pw_aff *isl_pw_aff_empty( + __isl_take isl_space *space); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_empty( + __isl_take isl_space *space); + +A piecewise expression with a single universe cell can be +created using the following functions. + + #include + __isl_give isl_pw_aff *isl_pw_aff_from_aff( + __isl_take isl_aff *aff); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_from_multi_aff( + __isl_take isl_multi_aff *ma); + + #include + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_from_qpolynomial( + __isl_take isl_qpolynomial *qp); + +A piecewise expression with a single specified cell can be +created using the following functions. + + #include + __isl_give isl_pw_aff *isl_pw_aff_alloc( + __isl_take isl_set *set, __isl_take isl_aff *aff); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_alloc( + __isl_take isl_set *set, + __isl_take isl_multi_aff *maff); + + #include + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_alloc( + __isl_take isl_set *set, + __isl_take isl_qpolynomial *qp); + +The following convenience functions first create a base expression and +then create a piecewise expression over a universe domain. + + #include + __isl_give isl_pw_aff *isl_pw_aff_zero_on_domain( + __isl_take isl_local_space *ls); + __isl_give isl_pw_aff *isl_pw_aff_var_on_domain( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos); + __isl_give isl_pw_aff *isl_pw_aff_nan_on_domain( + __isl_take isl_local_space *ls); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_zero( + __isl_take isl_space *space); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity( + __isl_take isl_space *space); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_map( + __isl_take isl_space *space); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_project_out_map( + __isl_take isl_space *space, + enum isl_dim_type type, + unsigned first, unsigned n); + + #include + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_zero( + __isl_take isl_space *space); + +The following convenience functions first create a base expression and +then create a piecewise expression over a given domain. + + #include + __isl_give isl_pw_aff *isl_pw_aff_val_on_domain( + __isl_take isl_set *domain, + __isl_take isl_val *v); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_multi_val_on_domain( + __isl_take isl_set *domain, + __isl_take isl_multi_val *mv); + +As a convenience, a piecewise multiple expression can +also be created from a piecewise expression. +Each multiple expression in the result is derived +from the corresponding base expression. + + #include + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_pw_aff( + __isl_take isl_pw_aff *pa); + +Similarly, a piecewise quasipolynomial can be +created from a piecewise quasi-affine expression using +the following function. + + #include + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_from_pw_aff( + __isl_take isl_pw_aff *pwaff); + +Piecewise expressions can be copied and freed using the following functions. + + #include + __isl_give isl_pw_aff *isl_pw_aff_copy( + __isl_keep isl_pw_aff *pwaff); + __isl_null isl_pw_aff *isl_pw_aff_free( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_copy( + __isl_keep isl_pw_multi_aff *pma); + __isl_null isl_pw_multi_aff *isl_pw_multi_aff_free( + __isl_take isl_pw_multi_aff *pma); + + #include + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_copy( + __isl_keep isl_pw_qpolynomial *pwqp); + __isl_null isl_pw_qpolynomial *isl_pw_qpolynomial_free( + __isl_take isl_pw_qpolynomial *pwqp); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_copy( + __isl_keep isl_pw_qpolynomial_fold *pwf); + __isl_null isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_free( + __isl_take isl_pw_qpolynomial_fold *pwf); + +To iterate over the different cells of a piecewise expression, +use the following functions. + + #include + isl_bool isl_pw_aff_is_empty(__isl_keep isl_pw_aff *pwaff); + int isl_pw_aff_n_piece(__isl_keep isl_pw_aff *pwaff); + isl_stat isl_pw_aff_foreach_piece( + __isl_keep isl_pw_aff *pwaff, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_aff *aff, + void *user), void *user); + isl_stat isl_pw_multi_aff_foreach_piece( + __isl_keep isl_pw_multi_aff *pma, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_multi_aff *maff, + void *user), void *user); + + #include + isl_stat isl_pw_qpolynomial_foreach_piece( + __isl_keep isl_pw_qpolynomial *pwqp, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp, + void *user), void *user); + isl_stat isl_pw_qpolynomial_foreach_lifted_piece( + __isl_keep isl_pw_qpolynomial *pwqp, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp, + void *user), void *user); + isl_stat isl_pw_qpolynomial_fold_foreach_piece( + __isl_keep isl_pw_qpolynomial_fold *pwf, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_qpolynomial_fold *fold, + void *user), void *user); + isl_stat isl_pw_qpolynomial_fold_foreach_lifted_piece( + __isl_keep isl_pw_qpolynomial_fold *pwf, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_qpolynomial_fold *fold, + void *user), void *user); + +As usual, the function C should return C<0> on success +and C<-1> on failure. The difference between +C and +C is that +C will first +compute unique representations for all existentially quantified +variables and then turn these existentially quantified variables +into extra set variables, adapting the associated quasipolynomial +accordingly. This means that the C passed to C +will not have any existentially quantified variables, but that +the dimensions of the sets may be different for different +invocations of C. +Similarly for C +and C. + +A piecewise expression consisting of the expressions at a given +position of a piecewise multiple expression can be extracted +using the following function. + + #include + __isl_give isl_pw_aff *isl_pw_multi_aff_get_pw_aff( + __isl_keep isl_pw_multi_aff *pma, int pos); + +These expressions can be replaced using the following function. + + #include + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_pw_aff( + __isl_take isl_pw_multi_aff *pma, unsigned pos, + __isl_take isl_pw_aff *pa); + +Note that there is a difference between C and +C objects. The first is a sequence of piecewise +affine expressions, while the second is a piecewise sequence +of affine expressions. In particular, each of the piecewise +affine expressions in an C may have a different +domain, while all multiple expressions associated to a cell +in an C have the same domain. +It is possible to convert between the two, but when converting +an C to an C, the domain +of the result is the intersection of the domains of the input. +The reverse conversion is exact. + + #include + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma); + +=head3 Union Expressions + +A union expression collects base expressions defined +over different domains. The space of a union expression +is that of the shared parameter space. + +The union expression types defined by C +are C, C, +C and C. +In case of +C, +C and C, +there can be at most one base expression for a given domain space. +In case of +C, +there can be multiple such expressions for a given domain space, +but the domains of these expressions need to be disjoint. + +An empty union expression can be created using the following functions. + + #include + __isl_give isl_union_pw_aff *isl_union_pw_aff_empty( + __isl_take isl_space *space); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_empty( + __isl_take isl_space *space); + + #include + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_zero( + __isl_take isl_space *space); + +A union expression containing a single base expression +can be created using the following functions. + + #include + __isl_give isl_union_pw_aff * + isl_union_pw_aff_from_pw_aff( + __isl_take isl_pw_aff *pa); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_aff( + __isl_take isl_aff *aff); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma); + + #include + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_from_pw_qpolynomial( + __isl_take isl_pw_qpolynomial *pwqp); + +The following functions create a base expression on each +of the sets in the union set and collect the results. + + #include + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_pw_aff * + isl_union_pw_multi_aff_get_union_pw_aff( + __isl_keep isl_union_pw_multi_aff *upma, int pos); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_val_on_domain( + __isl_take isl_union_set *domain, + __isl_take isl_val *v); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_multi_val_on_domain( + __isl_take isl_union_set *domain, + __isl_take isl_multi_val *mv); + +An C that is equal to a (parametric) affine +expression on a given domain can be created using the following +function. + + #include + __isl_give isl_union_pw_aff * + isl_union_pw_aff_aff_on_domain( + __isl_take isl_union_set *domain, + __isl_take isl_aff *aff); + +A base expression can be added to a union expression using +the following functions. + + #include + __isl_give isl_union_pw_aff * + isl_union_pw_aff_add_pw_aff( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_pw_aff *pa); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_add_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_pw_multi_aff *pma); + + #include + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_add_pw_qpolynomial( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_pw_qpolynomial *pwqp); + +Union expressions can be copied and freed using +the following functions. + + #include + __isl_give isl_union_pw_aff *isl_union_pw_aff_copy( + __isl_keep isl_union_pw_aff *upa); + __isl_null isl_union_pw_aff *isl_union_pw_aff_free( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_copy( + __isl_keep isl_union_pw_multi_aff *upma); + __isl_null isl_union_pw_multi_aff * + isl_union_pw_multi_aff_free( + __isl_take isl_union_pw_multi_aff *upma); + + #include + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_copy( + __isl_keep isl_union_pw_qpolynomial *upwqp); + __isl_null isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_free( + __isl_take isl_union_pw_qpolynomial *upwqp); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_copy( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + __isl_null isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_free( + __isl_take isl_union_pw_qpolynomial_fold *upwf); + +To iterate over the base expressions in a union expression, +use the following functions. + + #include + int isl_union_pw_aff_n_pw_aff( + __isl_keep isl_union_pw_aff *upa); + isl_stat isl_union_pw_aff_foreach_pw_aff( + __isl_keep isl_union_pw_aff *upa, + isl_stat (*fn)(__isl_take isl_pw_aff *pa, + void *user), void *user); + int isl_union_pw_multi_aff_n_pw_multi_aff( + __isl_keep isl_union_pw_multi_aff *upma); + isl_stat isl_union_pw_multi_aff_foreach_pw_multi_aff( + __isl_keep isl_union_pw_multi_aff *upma, + isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, + void *user), void *user); + + #include + int isl_union_pw_qpolynomial_n_pw_qpolynomial( + __isl_keep isl_union_pw_qpolynomial *upwqp); + isl_stat isl_union_pw_qpolynomial_foreach_pw_qpolynomial( + __isl_keep isl_union_pw_qpolynomial *upwqp, + isl_stat (*fn)(__isl_take isl_pw_qpolynomial *pwqp, + void *user), void *user); + int isl_union_pw_qpolynomial_fold_n_pw_qpolynomial_fold( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + isl_stat isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, + isl_stat (*fn)(__isl_take isl_pw_qpolynomial_fold *pwf, + void *user), void *user); + +To extract the base expression in a given space from a union, use +the following functions. + + #include + __isl_give isl_pw_aff *isl_union_pw_aff_extract_pw_aff( + __isl_keep isl_union_pw_aff *upa, + __isl_take isl_space *space); + __isl_give isl_pw_multi_aff * + isl_union_pw_multi_aff_extract_pw_multi_aff( + __isl_keep isl_union_pw_multi_aff *upma, + __isl_take isl_space *space); + + #include + __isl_give isl_pw_qpolynomial * + isl_union_pw_qpolynomial_extract_pw_qpolynomial( + __isl_keep isl_union_pw_qpolynomial *upwqp, + __isl_take isl_space *space); + +=head2 Input and Output + +For set and relation, +C supports its own input/output format, which is similar +to the C format, but also supports the C format +in some cases. +For other object types, typically only an C format is supported. + +=head3 C format + +The C format is similar to that of C, but has a different +syntax for describing the parameters and allows for the definition +of an existentially quantified variable as the integer division +of an affine expression. +For example, the set of integers C between C<0> and C +such that C can be described as + + [n] -> { [i] : exists (a = [i/10] : 0 <= i and i <= n and + i - 10 a <= 6) } + +A set or relation can have several disjuncts, separated +by the keyword C. Each disjunct is either a conjunction +of constraints or a projection (C) of a conjunction +of constraints. The constraints are separated by the keyword +C. + +=head3 C format + +If the represented set is a union, then the first line +contains a single number representing the number of disjuncts. +Otherwise, a line containing the number C<1> is optional. + +Each disjunct is represented by a matrix of constraints. +The first line contains two numbers representing +the number of rows and columns, +where the number of rows is equal to the number of constraints +and the number of columns is equal to two plus the number of variables. +The following lines contain the actual rows of the constraint matrix. +In each row, the first column indicates whether the constraint +is an equality (C<0>) or inequality (C<1>). The final column +corresponds to the constant term. + +If the set is parametric, then the coefficients of the parameters +appear in the last columns before the constant column. +The coefficients of any existentially quantified variables appear +between those of the set variables and those of the parameters. + +=head3 Extended C format + +The extended C format is nearly identical to the +C format. The only difference is that the line +containing the number of rows and columns of a constraint matrix +also contains four additional numbers: +the number of output dimensions, the number of input dimensions, +the number of local dimensions (i.e., the number of existentially +quantified variables) and the number of parameters. +For sets, the number of ``output'' dimensions is equal +to the number of set dimensions, while the number of ``input'' +dimensions is zero. + +=head3 Input + +Objects can be read from input using the following functions. + + #include + __isl_give isl_val *isl_val_read_from_str(isl_ctx *ctx, + const char *str); + __isl_give isl_multi_val *isl_multi_val_read_from_str( + isl_ctx *ctx, const char *str); + + #include + __isl_give isl_basic_set *isl_basic_set_read_from_file( + isl_ctx *ctx, FILE *input); + __isl_give isl_basic_set *isl_basic_set_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_set *isl_set_read_from_file(isl_ctx *ctx, + FILE *input); + __isl_give isl_set *isl_set_read_from_str(isl_ctx *ctx, + const char *str); + + #include + __isl_give isl_basic_map *isl_basic_map_read_from_file( + isl_ctx *ctx, FILE *input); + __isl_give isl_basic_map *isl_basic_map_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_map *isl_map_read_from_file( + isl_ctx *ctx, FILE *input); + __isl_give isl_map *isl_map_read_from_str(isl_ctx *ctx, + const char *str); + + #include + __isl_give isl_union_set *isl_union_set_read_from_file( + isl_ctx *ctx, FILE *input); + __isl_give isl_union_set *isl_union_set_read_from_str( + isl_ctx *ctx, const char *str); + + #include + __isl_give isl_union_map *isl_union_map_read_from_file( + isl_ctx *ctx, FILE *input); + __isl_give isl_union_map *isl_union_map_read_from_str( + isl_ctx *ctx, const char *str); + + #include + __isl_give isl_aff *isl_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_multi_aff *isl_multi_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_pw_aff *isl_pw_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_read_from_str( + isl_ctx *ctx, const char *str); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_read_from_str( + isl_ctx *ctx, const char *str); + + #include + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_read_from_str( + isl_ctx *ctx, const char *str); + +For sets and relations, +the input format is autodetected and may be either the C format +or the C format. + +=head3 Output + +Before anything can be printed, an C needs to +be created. + + __isl_give isl_printer *isl_printer_to_file(isl_ctx *ctx, + FILE *file); + __isl_give isl_printer *isl_printer_to_str(isl_ctx *ctx); + __isl_null isl_printer *isl_printer_free( + __isl_take isl_printer *printer); + +C prints to the given file, while +C prints to a string that can be extracted +using the following function. + + #include + __isl_give char *isl_printer_get_str( + __isl_keep isl_printer *printer); + +The printer can be inspected using the following functions. + + FILE *isl_printer_get_file( + __isl_keep isl_printer *printer); + int isl_printer_get_output_format( + __isl_keep isl_printer *p); + int isl_printer_get_yaml_style(__isl_keep isl_printer *p); + +The behavior of the printer can be modified in various ways + + __isl_give isl_printer *isl_printer_set_output_format( + __isl_take isl_printer *p, int output_format); + __isl_give isl_printer *isl_printer_set_indent( + __isl_take isl_printer *p, int indent); + __isl_give isl_printer *isl_printer_set_indent_prefix( + __isl_take isl_printer *p, const char *prefix); + __isl_give isl_printer *isl_printer_indent( + __isl_take isl_printer *p, int indent); + __isl_give isl_printer *isl_printer_set_prefix( + __isl_take isl_printer *p, const char *prefix); + __isl_give isl_printer *isl_printer_set_suffix( + __isl_take isl_printer *p, const char *suffix); + __isl_give isl_printer *isl_printer_set_yaml_style( + __isl_take isl_printer *p, int yaml_style); + +The C may be either C, C, +C, C or C +and defaults to C. +Each line in the output is prefixed by C, +indented by C (set by C) spaces +(default: 0), prefixed by C and suffixed by C. +In the C format output, +the coefficients of the existentially quantified variables +appear between those of the set variables and those +of the parameters. +The function C increases the indentation +by the specified amount (which may be negative). +The YAML style may be either C or +C and when we are printing something +in YAML format. + +To actually print something, use + + #include + __isl_give isl_printer *isl_printer_print_double( + __isl_take isl_printer *p, double d); + + #include + __isl_give isl_printer *isl_printer_print_val( + __isl_take isl_printer *p, __isl_keep isl_val *v); + + #include + __isl_give isl_printer *isl_printer_print_basic_set( + __isl_take isl_printer *printer, + __isl_keep isl_basic_set *bset); + __isl_give isl_printer *isl_printer_print_set( + __isl_take isl_printer *printer, + __isl_keep isl_set *set); + + #include + __isl_give isl_printer *isl_printer_print_basic_map( + __isl_take isl_printer *printer, + __isl_keep isl_basic_map *bmap); + __isl_give isl_printer *isl_printer_print_map( + __isl_take isl_printer *printer, + __isl_keep isl_map *map); + + #include + __isl_give isl_printer *isl_printer_print_union_set( + __isl_take isl_printer *p, + __isl_keep isl_union_set *uset); + + #include + __isl_give isl_printer *isl_printer_print_union_map( + __isl_take isl_printer *p, + __isl_keep isl_union_map *umap); + + #include + __isl_give isl_printer *isl_printer_print_multi_val( + __isl_take isl_printer *p, + __isl_keep isl_multi_val *mv); + + #include + __isl_give isl_printer *isl_printer_print_aff( + __isl_take isl_printer *p, __isl_keep isl_aff *aff); + __isl_give isl_printer *isl_printer_print_multi_aff( + __isl_take isl_printer *p, + __isl_keep isl_multi_aff *maff); + __isl_give isl_printer *isl_printer_print_pw_aff( + __isl_take isl_printer *p, + __isl_keep isl_pw_aff *pwaff); + __isl_give isl_printer *isl_printer_print_pw_multi_aff( + __isl_take isl_printer *p, + __isl_keep isl_pw_multi_aff *pma); + __isl_give isl_printer *isl_printer_print_multi_pw_aff( + __isl_take isl_printer *p, + __isl_keep isl_multi_pw_aff *mpa); + __isl_give isl_printer *isl_printer_print_union_pw_aff( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_aff *upa); + __isl_give isl_printer *isl_printer_print_union_pw_multi_aff( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_multi_aff *upma); + __isl_give isl_printer * + isl_printer_print_multi_union_pw_aff( + __isl_take isl_printer *p, + __isl_keep isl_multi_union_pw_aff *mupa); + + #include + __isl_give isl_printer *isl_printer_print_qpolynomial( + __isl_take isl_printer *p, + __isl_keep isl_qpolynomial *qp); + __isl_give isl_printer *isl_printer_print_pw_qpolynomial( + __isl_take isl_printer *p, + __isl_keep isl_pw_qpolynomial *pwqp); + __isl_give isl_printer *isl_printer_print_union_pw_qpolynomial( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_qpolynomial *upwqp); + + __isl_give isl_printer * + isl_printer_print_pw_qpolynomial_fold( + __isl_take isl_printer *p, + __isl_keep isl_pw_qpolynomial_fold *pwf); + __isl_give isl_printer * + isl_printer_print_union_pw_qpolynomial_fold( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + +For C, +C and +C, +the output format of the printer +needs to be set to either C or C. +For C and +C, only C +is supported. +In case of printing in C, the user may want +to set the names of all dimensions first. + +C also provides limited support for printing YAML documents, +just enough for the internal use for printing such documents. + + #include + __isl_give isl_printer *isl_printer_yaml_start_mapping( + __isl_take isl_printer *p); + __isl_give isl_printer *isl_printer_yaml_end_mapping( + __isl_take isl_printer *p); + __isl_give isl_printer *isl_printer_yaml_start_sequence( + __isl_take isl_printer *p); + __isl_give isl_printer *isl_printer_yaml_end_sequence( + __isl_take isl_printer *p); + __isl_give isl_printer *isl_printer_yaml_next( + __isl_take isl_printer *p); + +A document is started by a call to either +C or C. +Anything printed to the printer after such a call belong to the +first key of the mapping or the first element in the sequence. +The function C moves to the value if +we are currently printing a mapping key, the next key if we +are printing a value or the next element if we are printing +an element in a sequence. +Nested mappings and sequences are initiated by the same +C or C. +Each call to these functions needs to have a corresponding call to +C or C. + +When called on a file printer, the following function flushes +the file. When called on a string printer, the buffer is cleared. + + __isl_give isl_printer *isl_printer_flush( + __isl_take isl_printer *p); + +The following functions allow the user to attach +notes to a printer in order to keep track of additional state. + + #include + isl_bool isl_printer_has_note(__isl_keep isl_printer *p, + __isl_keep isl_id *id); + __isl_give isl_id *isl_printer_get_note( + __isl_keep isl_printer *p, __isl_take isl_id *id); + __isl_give isl_printer *isl_printer_set_note( + __isl_take isl_printer *p, + __isl_take isl_id *id, __isl_take isl_id *note); + +C associates the given note to the given +identifier in the printer. +C retrieves a note associated to an +identifier, while +C checks if there is such a note. +C fails if the requested note does not exist. + +Alternatively, a string representation can be obtained +directly using the following functions, which always print +in isl format. + + #include + __isl_give char *isl_space_to_str( + __isl_keep isl_space *space); + + #include + __isl_give char *isl_val_to_str(__isl_keep isl_val *v); + __isl_give char *isl_multi_val_to_str( + __isl_keep isl_multi_val *mv); + + #include + __isl_give char *isl_set_to_str( + __isl_keep isl_set *set); + + #include + __isl_give char *isl_union_set_to_str( + __isl_keep isl_union_set *uset); + + #include + __isl_give char *isl_map_to_str( + __isl_keep isl_map *map); + + #include + __isl_give char *isl_union_map_to_str( + __isl_keep isl_union_map *umap); + + #include + __isl_give char *isl_aff_to_str(__isl_keep isl_aff *aff); + __isl_give char *isl_pw_aff_to_str( + __isl_keep isl_pw_aff *pa); + __isl_give char *isl_multi_aff_to_str( + __isl_keep isl_multi_aff *ma); + __isl_give char *isl_pw_multi_aff_to_str( + __isl_keep isl_pw_multi_aff *pma); + __isl_give char *isl_multi_pw_aff_to_str( + __isl_keep isl_multi_pw_aff *mpa); + __isl_give char *isl_union_pw_aff_to_str( + __isl_keep isl_union_pw_aff *upa); + __isl_give char *isl_union_pw_multi_aff_to_str( + __isl_keep isl_union_pw_multi_aff *upma); + __isl_give char *isl_multi_union_pw_aff_to_str( + __isl_keep isl_multi_union_pw_aff *mupa); + +=head2 Properties + +=head3 Unary Properties + +=over + +=item * Emptiness + +The following functions test whether the given set or relation +contains any integer points. The ``plain'' variants do not perform +any computations, but simply check if the given set or relation +is already known to be empty. + + isl_bool isl_basic_set_plain_is_empty( + __isl_keep isl_basic_set *bset); + isl_bool isl_basic_set_is_empty( + __isl_keep isl_basic_set *bset); + isl_bool isl_set_plain_is_empty( + __isl_keep isl_set *set); + isl_bool isl_set_is_empty(__isl_keep isl_set *set); + isl_bool isl_union_set_is_empty( + __isl_keep isl_union_set *uset); + isl_bool isl_basic_map_plain_is_empty( + __isl_keep isl_basic_map *bmap); + isl_bool isl_basic_map_is_empty( + __isl_keep isl_basic_map *bmap); + isl_bool isl_map_plain_is_empty( + __isl_keep isl_map *map); + isl_bool isl_map_is_empty(__isl_keep isl_map *map); + isl_bool isl_union_map_is_empty( + __isl_keep isl_union_map *umap); + +=item * Universality + + isl_bool isl_basic_set_plain_is_universe( + __isl_keep isl_basic_set *bset); + isl_bool isl_basic_set_is_universe( + __isl_keep isl_basic_set *bset); + isl_bool isl_basic_map_plain_is_universe( + __isl_keep isl_basic_map *bmap); + isl_bool isl_basic_map_is_universe( + __isl_keep isl_basic_map *bmap); + isl_bool isl_set_plain_is_universe( + __isl_keep isl_set *set); + isl_bool isl_map_plain_is_universe( + __isl_keep isl_map *map); + +=item * Single-valuedness + + #include + isl_bool isl_set_is_singleton(__isl_keep isl_set *set); + + #include + isl_bool isl_basic_map_is_single_valued( + __isl_keep isl_basic_map *bmap); + isl_bool isl_map_plain_is_single_valued( + __isl_keep isl_map *map); + isl_bool isl_map_is_single_valued(__isl_keep isl_map *map); + + #include + isl_bool isl_union_map_is_single_valued( + __isl_keep isl_union_map *umap); + +=item * Injectivity + + isl_bool isl_map_plain_is_injective( + __isl_keep isl_map *map); + isl_bool isl_map_is_injective( + __isl_keep isl_map *map); + isl_bool isl_union_map_plain_is_injective( + __isl_keep isl_union_map *umap); + isl_bool isl_union_map_is_injective( + __isl_keep isl_union_map *umap); + +=item * Bijectivity + + isl_bool isl_map_is_bijective( + __isl_keep isl_map *map); + isl_bool isl_union_map_is_bijective( + __isl_keep isl_union_map *umap); + +=item * Identity + +The following functions test whether the given relation +only maps elements to themselves. + + #include + isl_bool isl_map_is_identity( + __isl_keep isl_map *map); + + #include + isl_bool isl_union_map_is_identity( + __isl_keep isl_union_map *umap); + +=item * Position + + __isl_give isl_val * + isl_basic_map_plain_get_val_if_fixed( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos); + __isl_give isl_val *isl_set_plain_get_val_if_fixed( + __isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + __isl_give isl_val *isl_map_plain_get_val_if_fixed( + __isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); + +If the set or relation obviously lies on a hyperplane where the given dimension +has a fixed value, then return that value. +Otherwise return NaN. + +=item * Stride + + isl_stat isl_set_dim_residue_class_val( + __isl_keep isl_set *set, + int pos, __isl_give isl_val **modulo, + __isl_give isl_val **residue); + +Check if the values of the given set dimension are equal to a fixed +value modulo some integer value. If so, assign the modulo to C<*modulo> +and the fixed value to C<*residue>. If the given dimension attains only +a single value, then assign C<0> to C<*modulo> and the fixed value to +C<*residue>. +If the dimension does not attain only a single value and if no modulo +can be found then assign C<1> to C<*modulo> and C<1> to C<*residue>. + +=item * Dependence + +To check whether the description of a set, relation or function depends +on one or more given dimensions, +the following functions can be used. + + #include + isl_bool isl_constraint_involves_dims( + __isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + isl_bool isl_basic_set_involves_dims( + __isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); + isl_bool isl_set_involves_dims(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + isl_bool isl_basic_map_involves_dims( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); + isl_bool isl_map_involves_dims(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + isl_bool isl_union_map_involves_dims( + __isl_keep isl_union_map *umap, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + isl_bool isl_aff_involves_dims(__isl_keep isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n); + isl_bool isl_pw_aff_involves_dims( + __isl_keep isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned first, unsigned n); + isl_bool isl_multi_aff_involves_dims( + __isl_keep isl_multi_aff *ma, + enum isl_dim_type type, unsigned first, unsigned n); + isl_bool isl_multi_pw_aff_involves_dims( + __isl_keep isl_multi_pw_aff *mpa, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + isl_bool isl_qpolynomial_involves_dims( + __isl_keep isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n); + +Similarly, the following functions can be used to check whether +a given dimension is involved in any lower or upper bound. + + #include + isl_bool isl_set_dim_has_any_lower_bound( + __isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + isl_bool isl_set_dim_has_any_upper_bound( + __isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + +Note that these functions return true even if there is a bound on +the dimension on only some of the basic sets of C. +To check if they have a bound for all of the basic sets in C, +use the following functions instead. + + #include + isl_bool isl_set_dim_has_lower_bound( + __isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + isl_bool isl_set_dim_has_upper_bound( + __isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + +=item * Space + +To check whether a set is a parameter domain, use this function: + + isl_bool isl_set_is_params(__isl_keep isl_set *set); + isl_bool isl_union_set_is_params( + __isl_keep isl_union_set *uset); + +=item * Wrapping + +The following functions check whether the space of the given +(basic) set or relation range is a wrapped relation. + + #include + isl_bool isl_space_is_wrapping( + __isl_keep isl_space *space); + isl_bool isl_space_domain_is_wrapping( + __isl_keep isl_space *space); + isl_bool isl_space_range_is_wrapping( + __isl_keep isl_space *space); + + #include + isl_bool isl_basic_set_is_wrapping( + __isl_keep isl_basic_set *bset); + isl_bool isl_set_is_wrapping(__isl_keep isl_set *set); + + #include + isl_bool isl_map_domain_is_wrapping( + __isl_keep isl_map *map); + isl_bool isl_map_range_is_wrapping( + __isl_keep isl_map *map); + + #include + isl_bool isl_multi_val_range_is_wrapping( + __isl_keep isl_multi_val *mv); + + #include + isl_bool isl_multi_aff_range_is_wrapping( + __isl_keep isl_multi_aff *ma); + isl_bool isl_multi_pw_aff_range_is_wrapping( + __isl_keep isl_multi_pw_aff *mpa); + isl_bool isl_multi_union_pw_aff_range_is_wrapping( + __isl_keep isl_multi_union_pw_aff *mupa); + +The input to C should +be the space of a set, while that of +C and +C should be the space of a relation. + +=item * Internal Product + + isl_bool isl_basic_map_can_zip( + __isl_keep isl_basic_map *bmap); + isl_bool isl_map_can_zip(__isl_keep isl_map *map); + +Check whether the product of domain and range of the given relation +can be computed, +i.e., whether both domain and range are nested relations. + +=item * Currying + + #include + isl_bool isl_space_can_curry( + __isl_keep isl_space *space); + + #include + isl_bool isl_basic_map_can_curry( + __isl_keep isl_basic_map *bmap); + isl_bool isl_map_can_curry(__isl_keep isl_map *map); + +Check whether the domain of the (basic) relation is a wrapped relation. + + #include + __isl_give isl_space *isl_space_uncurry( + __isl_take isl_space *space); + + #include + isl_bool isl_basic_map_can_uncurry( + __isl_keep isl_basic_map *bmap); + isl_bool isl_map_can_uncurry(__isl_keep isl_map *map); + +Check whether the range of the (basic) relation is a wrapped relation. + + #include + isl_bool isl_space_can_range_curry( + __isl_keep isl_space *space); + + #include + isl_bool isl_map_can_range_curry( + __isl_keep isl_map *map); + +Check whether the domain of the relation wrapped in the range of +the input is itself a wrapped relation. + +=item * Special Values + + #include + isl_bool isl_aff_is_cst(__isl_keep isl_aff *aff); + isl_bool isl_pw_aff_is_cst(__isl_keep isl_pw_aff *pwaff); + isl_bool isl_multi_pw_aff_is_cst( + __isl_keep isl_multi_pw_aff *mpa); + +Check whether the given expression is a constant. + + #include + isl_bool isl_aff_is_nan(__isl_keep isl_aff *aff); + isl_bool isl_pw_aff_involves_nan( + __isl_keep isl_pw_aff *pa); + + #include + isl_bool isl_qpolynomial_fold_is_nan( + __isl_keep isl_qpolynomial_fold *fold); + +Check whether the given expression is equal to or involves NaN. + + #include + isl_bool isl_aff_plain_is_zero( + __isl_keep isl_aff *aff); + +Check whether the affine expression is obviously zero. + +=back + +=head3 Binary Properties + +=over + +=item * Equality + +The following functions check whether two objects +represent the same set, relation or function. +The C variants only return true if the objects +are obviously the same. That is, they may return false +even if the objects are the same, but they will never +return true if the objects are not the same. + + #include + isl_bool isl_basic_set_plain_is_equal( + __isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); + isl_bool isl_basic_set_is_equal( + __isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); + isl_bool isl_set_plain_is_equal( + __isl_keep isl_set *set1, + __isl_keep isl_set *set2); + isl_bool isl_set_is_equal(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); + + #include + isl_bool isl_basic_map_is_equal( + __isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); + isl_bool isl_map_is_equal(__isl_keep isl_map *map1, + __isl_keep isl_map *map2); + isl_bool isl_map_plain_is_equal( + __isl_keep isl_map *map1, + __isl_keep isl_map *map2); + + #include + isl_bool isl_union_set_is_equal( + __isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); + + #include + isl_bool isl_union_map_is_equal( + __isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); + + #include + isl_bool isl_aff_plain_is_equal( + __isl_keep isl_aff *aff1, + __isl_keep isl_aff *aff2); + isl_bool isl_multi_aff_plain_is_equal( + __isl_keep isl_multi_aff *maff1, + __isl_keep isl_multi_aff *maff2); + isl_bool isl_pw_aff_plain_is_equal( + __isl_keep isl_pw_aff *pwaff1, + __isl_keep isl_pw_aff *pwaff2); + isl_bool isl_pw_multi_aff_plain_is_equal( + __isl_keep isl_pw_multi_aff *pma1, + __isl_keep isl_pw_multi_aff *pma2); + isl_bool isl_multi_pw_aff_plain_is_equal( + __isl_keep isl_multi_pw_aff *mpa1, + __isl_keep isl_multi_pw_aff *mpa2); + isl_bool isl_multi_pw_aff_is_equal( + __isl_keep isl_multi_pw_aff *mpa1, + __isl_keep isl_multi_pw_aff *mpa2); + isl_bool isl_union_pw_aff_plain_is_equal( + __isl_keep isl_union_pw_aff *upa1, + __isl_keep isl_union_pw_aff *upa2); + isl_bool isl_union_pw_multi_aff_plain_is_equal( + __isl_keep isl_union_pw_multi_aff *upma1, + __isl_keep isl_union_pw_multi_aff *upma2); + isl_bool isl_multi_union_pw_aff_plain_is_equal( + __isl_keep isl_multi_union_pw_aff *mupa1, + __isl_keep isl_multi_union_pw_aff *mupa2); + + #include + isl_bool isl_union_pw_qpolynomial_plain_is_equal( + __isl_keep isl_union_pw_qpolynomial *upwqp1, + __isl_keep isl_union_pw_qpolynomial *upwqp2); + isl_bool isl_union_pw_qpolynomial_fold_plain_is_equal( + __isl_keep isl_union_pw_qpolynomial_fold *upwf1, + __isl_keep isl_union_pw_qpolynomial_fold *upwf2); + +=item * Disjointness + + #include + isl_bool isl_basic_set_is_disjoint( + __isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); + isl_bool isl_set_plain_is_disjoint( + __isl_keep isl_set *set1, + __isl_keep isl_set *set2); + isl_bool isl_set_is_disjoint(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); + + #include + isl_bool isl_basic_map_is_disjoint( + __isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); + isl_bool isl_map_is_disjoint(__isl_keep isl_map *map1, + __isl_keep isl_map *map2); + + #include + isl_bool isl_union_set_is_disjoint( + __isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); + + #include + isl_bool isl_union_map_is_disjoint( + __isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); + +=item * Subset + + isl_bool isl_basic_set_is_subset( + __isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); + isl_bool isl_set_is_subset(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); + isl_bool isl_set_is_strict_subset( + __isl_keep isl_set *set1, + __isl_keep isl_set *set2); + isl_bool isl_union_set_is_subset( + __isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); + isl_bool isl_union_set_is_strict_subset( + __isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); + isl_bool isl_basic_map_is_subset( + __isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); + isl_bool isl_basic_map_is_strict_subset( + __isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); + isl_bool isl_map_is_subset( + __isl_keep isl_map *map1, + __isl_keep isl_map *map2); + isl_bool isl_map_is_strict_subset( + __isl_keep isl_map *map1, + __isl_keep isl_map *map2); + isl_bool isl_union_map_is_subset( + __isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); + isl_bool isl_union_map_is_strict_subset( + __isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); + +Check whether the first argument is a (strict) subset of the +second argument. + +=item * Order + +Every comparison function returns a negative value if the first +argument is considered smaller than the second, a positive value +if the first argument is considered greater and zero if the two +constraints are considered the same by the comparison criterion. + + #include + int isl_constraint_plain_cmp( + __isl_keep isl_constraint *c1, + __isl_keep isl_constraint *c2); + +This function is useful for sorting Cs. +The order depends on the internal representation of the inputs. +The order is fixed over different calls to the function (assuming +the internal representation of the inputs has not changed), but may +change over different versions of C. + + #include + int isl_constraint_cmp_last_non_zero( + __isl_keep isl_constraint *c1, + __isl_keep isl_constraint *c2); + +This function can be used to sort constraints that live in the same +local space. Constraints that involve ``earlier'' dimensions or +that have a smaller coefficient for the shared latest dimension +are considered smaller than other constraints. +This function only defines a B order. + + #include + int isl_set_plain_cmp(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); + +This function is useful for sorting Cs. +The order depends on the internal representation of the inputs. +The order is fixed over different calls to the function (assuming +the internal representation of the inputs has not changed), but may +change over different versions of C. + + #include + int isl_pw_aff_plain_cmp(__isl_keep isl_pw_aff *pa1, + __isl_keep isl_pw_aff *pa2); + +The function C can be used to sort +Cs. The order is not strictly defined. +The current order sorts expressions that only involve +earlier dimensions before those that involve later dimensions. + +=back + +=head2 Unary Operations + +=over + +=item * Complement + + __isl_give isl_set *isl_set_complement( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_complement( + __isl_take isl_map *map); + +=item * Inverse map + + #include + __isl_give isl_space *isl_space_reverse( + __isl_take isl_space *space); + + #include + __isl_give isl_basic_map *isl_basic_map_reverse( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_reverse( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_map_reverse( + __isl_take isl_union_map *umap); + +=item * Projection + + #include + __isl_give isl_space *isl_space_domain( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_range( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_params( + __isl_take isl_space *space); + + #include + __isl_give isl_local_space *isl_local_space_domain( + __isl_take isl_local_space *ls); + __isl_give isl_local_space *isl_local_space_range( + __isl_take isl_local_space *ls); + + #include + __isl_give isl_basic_set *isl_basic_set_project_out( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_set *isl_set_project_out(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_map *isl_set_project_onto_map( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned first, + unsigned n); + __isl_give isl_basic_set *isl_basic_set_params( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_params(__isl_take isl_set *set); + +The function C returns a relation +that projects the input set onto the given set dimensions. + + #include + __isl_give isl_basic_map *isl_basic_map_project_out( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_map *isl_map_project_out(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_basic_set *isl_basic_map_domain( + __isl_take isl_basic_map *bmap); + __isl_give isl_basic_set *isl_basic_map_range( + __isl_take isl_basic_map *bmap); + __isl_give isl_set *isl_map_params(__isl_take isl_map *map); + __isl_give isl_set *isl_map_domain( + __isl_take isl_map *bmap); + __isl_give isl_set *isl_map_range( + __isl_take isl_map *map); + + #include + __isl_give isl_union_set *isl_union_set_project_out( + __isl_take isl_union_set *uset, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_set *isl_union_set_params( + __isl_take isl_union_set *uset); + +The function C can only project out +parameters. + + #include + __isl_give isl_union_map *isl_union_map_project_out( + __isl_take isl_union_map *umap, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_set *isl_union_map_params( + __isl_take isl_union_map *umap); + __isl_give isl_union_set *isl_union_map_domain( + __isl_take isl_union_map *umap); + __isl_give isl_union_set *isl_union_map_range( + __isl_take isl_union_map *umap); + +The function C can only project out +parameters. + + #include + __isl_give isl_aff *isl_aff_project_domain_on_params( + __isl_take isl_aff *aff); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_project_domain_on_params( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_set *isl_pw_aff_domain( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_set *isl_pw_multi_aff_domain( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_set *isl_multi_pw_aff_domain( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_union_set *isl_union_pw_aff_domain( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_set *isl_union_pw_multi_aff_domain( + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_union_set * + isl_multi_union_pw_aff_domain( + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_set *isl_pw_aff_params( + __isl_take isl_pw_aff *pwa); + +The function C requires its +input to have at least one set dimension. + + #include + __isl_give isl_qpolynomial * + isl_qpolynomial_project_domain_on_params( + __isl_take isl_qpolynomial *qp); + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_project_domain_on_params( + __isl_take isl_pw_qpolynomial *pwqp); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_project_domain_on_params( + __isl_take isl_pw_qpolynomial_fold *pwf); + __isl_give isl_set *isl_pw_qpolynomial_domain( + __isl_take isl_pw_qpolynomial *pwqp); + __isl_give isl_union_set *isl_union_pw_qpolynomial_fold_domain( + __isl_take isl_union_pw_qpolynomial_fold *upwf); + __isl_give isl_union_set *isl_union_pw_qpolynomial_domain( + __isl_take isl_union_pw_qpolynomial *upwqp); + + #include + __isl_give isl_space *isl_space_domain_map( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_range_map( + __isl_take isl_space *space); + + #include + __isl_give isl_map *isl_set_wrapped_domain_map( + __isl_take isl_set *set); + __isl_give isl_basic_map *isl_basic_map_domain_map( + __isl_take isl_basic_map *bmap); + __isl_give isl_basic_map *isl_basic_map_range_map( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_domain_map(__isl_take isl_map *map); + __isl_give isl_map *isl_map_range_map(__isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_map_domain_map( + __isl_take isl_union_map *umap); + __isl_give isl_union_pw_multi_aff * + isl_union_map_domain_map_union_pw_multi_aff( + __isl_take isl_union_map *umap); + __isl_give isl_union_map *isl_union_map_range_map( + __isl_take isl_union_map *umap); + __isl_give isl_union_map * + isl_union_set_wrapped_domain_map( + __isl_take isl_union_set *uset); + +The functions above construct a (basic, regular or union) relation +that maps (a wrapped version of) the input relation to its domain or range. +C maps the input set to the domain +of its wrapped relation. + +=item * Elimination + + __isl_give isl_basic_set *isl_basic_set_eliminate( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_set *isl_set_eliminate( + __isl_take isl_set *set, enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_basic_map *isl_basic_map_eliminate( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_map *isl_map_eliminate( + __isl_take isl_map *map, enum isl_dim_type type, + unsigned first, unsigned n); + +Eliminate the coefficients for the given dimensions from the constraints, +without removing the dimensions. + +=item * Constructing a set from a parameter domain + +A zero-dimensional space or (basic) set can be constructed +on a given parameter domain using the following functions. + + #include + __isl_give isl_space *isl_space_set_from_params( + __isl_take isl_space *space); + + #include + __isl_give isl_basic_set *isl_basic_set_from_params( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_from_params( + __isl_take isl_set *set); + +=item * Constructing a relation from one or two sets + +Create a relation with the given set(s) as domain and/or range. +If only the domain or the range is specified, then +the range or domain of the created relation is a zero-dimensional +flat anonymous space. + + #include + __isl_give isl_space *isl_space_from_domain( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_from_range( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_map_from_set( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_map_from_domain_and_range( + __isl_take isl_space *domain, + __isl_take isl_space *range); + + #include + __isl_give isl_local_space *isl_local_space_from_domain( + __isl_take isl_local_space *ls); + + #include + __isl_give isl_map *isl_map_from_domain( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_from_range( + __isl_take isl_set *set); + + #include + __isl_give isl_union_map * + isl_union_map_from_domain_and_range( + __isl_take isl_union_set *domain, + __isl_take isl_union_set *range); + + #include + __isl_give isl_multi_val *isl_multi_val_from_range( + __isl_take isl_multi_val *mv); + + #include + __isl_give isl_multi_aff *isl_multi_aff_from_range( + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_aff *isl_pw_aff_from_range( + __isl_take isl_pw_aff *pwa); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_range( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_range( + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_domain( + __isl_take isl_set *set); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_domain( + __isl_take isl_union_set *uset); + +=item * Slicing + + #include + __isl_give isl_basic_set *isl_basic_set_fix_si( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_basic_set *isl_basic_set_fix_val( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, + __isl_take isl_val *v); + __isl_give isl_set *isl_set_fix_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_set *isl_set_fix_val( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, + __isl_take isl_val *v); + + #include + __isl_give isl_basic_map *isl_basic_map_fix_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_basic_map *isl_basic_map_fix_val( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, + __isl_take isl_val *v); + __isl_give isl_map *isl_map_fix_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_map *isl_map_fix_val( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, + __isl_take isl_val *v); + + #include + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_fix_si( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned pos, int value); + + #include + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_fix_val( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned n, + __isl_take isl_val *v); + +Intersect the set, relation or function domain +with the hyperplane where the given +dimension has the fixed given value. + + __isl_give isl_basic_map *isl_basic_map_lower_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_basic_map *isl_basic_map_upper_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_set *isl_set_lower_bound_si( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_set *isl_set_lower_bound_val( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, + __isl_take isl_val *value); + __isl_give isl_map *isl_map_lower_bound_si( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_set *isl_set_upper_bound_si( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value); + __isl_give isl_set *isl_set_upper_bound_val( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, + __isl_take isl_val *value); + __isl_give isl_map *isl_map_upper_bound_si( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value); + +Intersect the set or relation with the half-space where the given +dimension has a value bounded by the fixed given integer value. + + __isl_give isl_set *isl_set_equate(__isl_take isl_set *set, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_basic_map *isl_basic_map_equate( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_map *isl_map_equate(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + +Intersect the set or relation with the hyperplane where the given +dimensions are equal to each other. + + __isl_give isl_map *isl_map_oppose(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + +Intersect the relation with the hyperplane where the given +dimensions have opposite values. + + __isl_give isl_map *isl_map_order_le( + __isl_take isl_map *map, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_basic_map *isl_basic_map_order_ge( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_map *isl_map_order_ge( + __isl_take isl_map *map, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_map *isl_map_order_lt(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_basic_map *isl_basic_map_order_gt( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + __isl_give isl_map *isl_map_order_gt(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2); + +Intersect the relation with the half-space where the given +dimensions satisfy the given ordering. + +=item * Locus + + #include + __isl_give isl_basic_set *isl_aff_zero_basic_set( + __isl_take isl_aff *aff); + __isl_give isl_basic_set *isl_aff_neg_basic_set( + __isl_take isl_aff *aff); + __isl_give isl_set *isl_pw_aff_pos_set( + __isl_take isl_pw_aff *pa); + __isl_give isl_set *isl_pw_aff_nonneg_set( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_set *isl_pw_aff_zero_set( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_set *isl_pw_aff_non_zero_set( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_union_set * + isl_union_pw_aff_zero_union_set( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_set * + isl_multi_union_pw_aff_zero_union_set( + __isl_take isl_multi_union_pw_aff *mupa); + +The function C returns a basic set +containing those elements in the domain space +of C where C is negative. +The function C returns a set +containing those elements in the domain +of C where C is non-negative. +The function C +returns a union set containing those elements +in the domains of its elements where they are all zero. + +=item * Identity + + __isl_give isl_map *isl_set_identity( + __isl_take isl_set *set); + __isl_give isl_union_map *isl_union_set_identity( + __isl_take isl_union_set *uset); + __isl_give isl_union_pw_multi_aff * + isl_union_set_identity_union_pw_multi_aff( + __isl_take isl_union_set *uset); + +Construct an identity relation on the given (union) set. + +=item * Function Extraction + +A piecewise quasi affine expression that is equal to 1 on a set +and 0 outside the set can be created using the following function. + + #include + __isl_give isl_pw_aff *isl_set_indicator_function( + __isl_take isl_set *set); + +A piecewise multiple quasi affine expression can be extracted +from an C or C, provided the C is a singleton +and the C is single-valued. +In case of a conversion from an C +to an C, these properties need to hold +in each domain space. +A conversion to a C additionally +requires that the input is non-empty and involves only a single +range space. + + #include + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_set( + __isl_take isl_set *set); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_map( + __isl_take isl_map *map); + + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_union_set( + __isl_take isl_union_set *uset); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_from_union_map( + __isl_take isl_union_map *umap); + + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_from_union_map( + __isl_take isl_union_map *umap); + +=item * Deltas + + __isl_give isl_basic_set *isl_basic_map_deltas( + __isl_take isl_basic_map *bmap); + __isl_give isl_set *isl_map_deltas(__isl_take isl_map *map); + __isl_give isl_union_set *isl_union_map_deltas( + __isl_take isl_union_map *umap); + +These functions return a (basic) set containing the differences +between image elements and corresponding domain elements in the input. + + __isl_give isl_basic_map *isl_basic_map_deltas_map( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_deltas_map( + __isl_take isl_map *map); + __isl_give isl_union_map *isl_union_map_deltas_map( + __isl_take isl_union_map *umap); + +The functions above construct a (basic, regular or union) relation +that maps (a wrapped version of) the input relation to its delta set. + +=item * Coalescing + +Simplify the representation of a set, relation or functions by trying +to combine pairs of basic sets or relations into a single +basic set or relation. + + #include + __isl_give isl_set *isl_set_coalesce(__isl_take isl_set *set); + + #include + __isl_give isl_map *isl_map_coalesce(__isl_take isl_map *map); + + #include + __isl_give isl_union_set *isl_union_set_coalesce( + __isl_take isl_union_set *uset); + + #include + __isl_give isl_union_map *isl_union_map_coalesce( + __isl_take isl_union_map *umap); + + #include + __isl_give isl_pw_aff *isl_pw_aff_coalesce( + __isl_take isl_pw_aff *pwqp); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_coalesce( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_coalesce( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_union_pw_aff *isl_union_pw_aff_coalesce( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_coalesce( + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_coalesce( + __isl_take isl_multi_union_pw_aff *aff); + + #include + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_coalesce( + __isl_take isl_pw_qpolynomial_fold *pwf); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_coalesce( + __isl_take isl_union_pw_qpolynomial *upwqp); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_coalesce( + __isl_take isl_union_pw_qpolynomial_fold *upwf); + +One of the methods for combining pairs of basic sets or relations +can result in coefficients that are much larger than those that appear +in the constraints of the input. By default, the coefficients are +not allowed to grow larger, but this can be changed by unsetting +the following option. + + isl_stat isl_options_set_coalesce_bounded_wrapping( + isl_ctx *ctx, int val); + int isl_options_get_coalesce_bounded_wrapping( + isl_ctx *ctx); + +=item * Detecting equalities + + __isl_give isl_basic_set *isl_basic_set_detect_equalities( + __isl_take isl_basic_set *bset); + __isl_give isl_basic_map *isl_basic_map_detect_equalities( + __isl_take isl_basic_map *bmap); + __isl_give isl_set *isl_set_detect_equalities( + __isl_take isl_set *set); + __isl_give isl_map *isl_map_detect_equalities( + __isl_take isl_map *map); + __isl_give isl_union_set *isl_union_set_detect_equalities( + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_detect_equalities( + __isl_take isl_union_map *umap); + +Simplify the representation of a set or relation by detecting implicit +equalities. + +=item * Removing redundant constraints + + #include + __isl_give isl_basic_set *isl_basic_set_remove_redundancies( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_remove_redundancies( + __isl_take isl_set *set); + + #include + __isl_give isl_union_set * + isl_union_set_remove_redundancies( + __isl_take isl_union_set *uset); + + #include + __isl_give isl_basic_map *isl_basic_map_remove_redundancies( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_remove_redundancies( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map * + isl_union_map_remove_redundancies( + __isl_take isl_union_map *umap); + +=item * Convex hull + + __isl_give isl_basic_set *isl_set_convex_hull( + __isl_take isl_set *set); + __isl_give isl_basic_map *isl_map_convex_hull( + __isl_take isl_map *map); + +If the input set or relation has any existentially quantified +variables, then the result of these operations is currently undefined. + +=item * Simple hull + + #include + __isl_give isl_basic_set * + isl_set_unshifted_simple_hull( + __isl_take isl_set *set); + __isl_give isl_basic_set *isl_set_simple_hull( + __isl_take isl_set *set); + __isl_give isl_basic_set * + isl_set_plain_unshifted_simple_hull( + __isl_take isl_set *set); + __isl_give isl_basic_set * + isl_set_unshifted_simple_hull_from_set_list( + __isl_take isl_set *set, + __isl_take isl_set_list *list); + + #include + __isl_give isl_basic_map * + isl_map_unshifted_simple_hull( + __isl_take isl_map *map); + __isl_give isl_basic_map *isl_map_simple_hull( + __isl_take isl_map *map); + __isl_give isl_basic_map * + isl_map_plain_unshifted_simple_hull( + __isl_take isl_map *map); + __isl_give isl_basic_map * + isl_map_unshifted_simple_hull_from_map_list( + __isl_take isl_map *map, + __isl_take isl_map_list *list); + + #include + __isl_give isl_union_map *isl_union_map_simple_hull( + __isl_take isl_union_map *umap); + +These functions compute a single basic set or relation +that contains the whole input set or relation. +In particular, the output is described by translates +of the constraints describing the basic sets or relations in the input. +In case of C, only the original +constraints are used, without any translation. +In case of C and +C, the result is described +by original constraints that are obviously satisfied +by the entire input set or relation. +In case of C and +C, the +constraints are taken from the elements of the second argument. + +=begin latex + +(See \autoref{s:simple hull}.) + +=end latex + +=item * Affine hull + + __isl_give isl_basic_set *isl_basic_set_affine_hull( + __isl_take isl_basic_set *bset); + __isl_give isl_basic_set *isl_set_affine_hull( + __isl_take isl_set *set); + __isl_give isl_union_set *isl_union_set_affine_hull( + __isl_take isl_union_set *uset); + __isl_give isl_basic_map *isl_basic_map_affine_hull( + __isl_take isl_basic_map *bmap); + __isl_give isl_basic_map *isl_map_affine_hull( + __isl_take isl_map *map); + __isl_give isl_union_map *isl_union_map_affine_hull( + __isl_take isl_union_map *umap); + +In case of union sets and relations, the affine hull is computed +per space. + +=item * Polyhedral hull + + __isl_give isl_basic_set *isl_set_polyhedral_hull( + __isl_take isl_set *set); + __isl_give isl_basic_map *isl_map_polyhedral_hull( + __isl_take isl_map *map); + __isl_give isl_union_set *isl_union_set_polyhedral_hull( + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_polyhedral_hull( + __isl_take isl_union_map *umap); + +These functions compute a single basic set or relation +not involving any existentially quantified variables +that contains the whole input set or relation. +In case of union sets and relations, the polyhedral hull is computed +per space. + +=item * Other approximations + + #include + __isl_give isl_basic_set * + isl_basic_set_drop_constraints_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_basic_set * + isl_basic_set_drop_constraints_not_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_set * + isl_set_drop_constraints_involving_dims( + __isl_take isl_set *set, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_set * + isl_set_drop_constraints_not_involving_dims( + __isl_take isl_set *set, + enum isl_dim_type type, + unsigned first, unsigned n); + + #include + __isl_give isl_basic_map * + isl_basic_map_drop_constraints_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_basic_map * + isl_basic_map_drop_constraints_not_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_map * + isl_map_drop_constraints_involving_dims( + __isl_take isl_map *map, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_map * + isl_map_drop_constraints_not_involving_dims( + __isl_take isl_map *map, + enum isl_dim_type type, + unsigned first, unsigned n); + +These functions drop any constraints (not) involving the specified dimensions. +Note that the result depends on the representation of the input. + + #include + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_to_polynomial( + __isl_take isl_pw_qpolynomial *pwqp, int sign); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_to_polynomial( + __isl_take isl_union_pw_qpolynomial *upwqp, int sign); + +Approximate each quasipolynomial by a polynomial. If C is positive, +the polynomial will be an overapproximation. If C is negative, +it will be an underapproximation. If C is zero, the approximation +will lie somewhere in between. + +=item * Feasibility + + __isl_give isl_basic_set *isl_basic_set_sample( + __isl_take isl_basic_set *bset); + __isl_give isl_basic_set *isl_set_sample( + __isl_take isl_set *set); + __isl_give isl_basic_map *isl_basic_map_sample( + __isl_take isl_basic_map *bmap); + __isl_give isl_basic_map *isl_map_sample( + __isl_take isl_map *map); + +If the input (basic) set or relation is non-empty, then return +a singleton subset of the input. Otherwise, return an empty set. + +=item * Optimization + + #include + __isl_give isl_val *isl_basic_set_max_val( + __isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj); + __isl_give isl_val *isl_set_min_val( + __isl_keep isl_set *set, + __isl_keep isl_aff *obj); + __isl_give isl_val *isl_set_max_val( + __isl_keep isl_set *set, + __isl_keep isl_aff *obj); + __isl_give isl_multi_val * + isl_union_set_min_multi_union_pw_aff( + __isl_keep isl_union_set *set, + __isl_keep isl_multi_union_pw_aff *obj); + +Compute the minimum or maximum of the integer affine expression C +over the points in C, returning the result in C. +The result is C in case of an error, the optimal value in case +there is one, negative infinity or infinity if the problem is unbounded and +NaN if the problem is empty. + +=item * Parametric optimization + + __isl_give isl_pw_aff *isl_set_dim_min( + __isl_take isl_set *set, int pos); + __isl_give isl_pw_aff *isl_set_dim_max( + __isl_take isl_set *set, int pos); + __isl_give isl_pw_aff *isl_map_dim_max( + __isl_take isl_map *map, int pos); + +Compute the minimum or maximum of the given set or output dimension +as a function of the parameters (and input dimensions), but independently +of the other set or output dimensions. +For lexicographic optimization, see L<"Lexicographic Optimization">. + +=item * Dual + +The following functions compute either the set of (rational) coefficient +values of valid constraints for the given set or the set of (rational) +values satisfying the constraints with coefficients from the given set. +Internally, these two sets of functions perform essentially the +same operations, except that the set of coefficients is assumed to +be a cone, while the set of values may be any polyhedron. +The current implementation is based on the Farkas lemma and +Fourier-Motzkin elimination, but this may change or be made optional +in future. In particular, future implementations may use different +dualization algorithms or skip the elimination step. + + __isl_give isl_basic_set *isl_basic_set_coefficients( + __isl_take isl_basic_set *bset); + __isl_give isl_basic_set *isl_set_coefficients( + __isl_take isl_set *set); + __isl_give isl_union_set *isl_union_set_coefficients( + __isl_take isl_union_set *bset); + __isl_give isl_basic_set *isl_basic_set_solutions( + __isl_take isl_basic_set *bset); + __isl_give isl_basic_set *isl_set_solutions( + __isl_take isl_set *set); + __isl_give isl_union_set *isl_union_set_solutions( + __isl_take isl_union_set *bset); + +=item * Power + + __isl_give isl_map *isl_map_fixed_power_val( + __isl_take isl_map *map, + __isl_take isl_val *exp); + __isl_give isl_union_map * + isl_union_map_fixed_power_val( + __isl_take isl_union_map *umap, + __isl_take isl_val *exp); + +Compute the given power of C, where C is assumed to be non-zero. +If the exponent C is negative, then the -C th power of the inverse +of C is computed. + + __isl_give isl_map *isl_map_power(__isl_take isl_map *map, + int *exact); + __isl_give isl_union_map *isl_union_map_power( + __isl_take isl_union_map *umap, int *exact); + +Compute a parametric representation for all positive powers I of C. +The result maps I to a nested relation corresponding to the +Ith power of C. +The result may be an overapproximation. If the result is known to be exact, +then C<*exact> is set to C<1>. + +=item * Transitive closure + + __isl_give isl_map *isl_map_transitive_closure( + __isl_take isl_map *map, int *exact); + __isl_give isl_union_map *isl_union_map_transitive_closure( + __isl_take isl_union_map *umap, int *exact); + +Compute the transitive closure of C. +The result may be an overapproximation. If the result is known to be exact, +then C<*exact> is set to C<1>. + +=item * Reaching path lengths + + __isl_give isl_map *isl_map_reaching_path_lengths( + __isl_take isl_map *map, int *exact); + +Compute a relation that maps each element in the range of C +to the lengths of all paths composed of edges in C that +end up in the given element. +The result may be an overapproximation. If the result is known to be exact, +then C<*exact> is set to C<1>. +To compute the I path length, the resulting relation +should be postprocessed by C. +In particular, if the input relation is a dependence relation +(mapping sources to sinks), then the maximal path length corresponds +to the free schedule. +Note, however, that C expects the maximum to be +finite, so if the path lengths are unbounded (possibly due to +the overapproximation), then you will get an error message. + +=item * Wrapping + + #include + __isl_give isl_space *isl_space_wrap( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_unwrap( + __isl_take isl_space *space); + + #include + __isl_give isl_local_space *isl_local_space_wrap( + __isl_take isl_local_space *ls); + + #include + __isl_give isl_basic_map *isl_basic_set_unwrap( + __isl_take isl_basic_set *bset); + __isl_give isl_map *isl_set_unwrap( + __isl_take isl_set *set); + + #include + __isl_give isl_basic_set *isl_basic_map_wrap( + __isl_take isl_basic_map *bmap); + __isl_give isl_set *isl_map_wrap( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_set_unwrap( + __isl_take isl_union_set *uset); + + #include + __isl_give isl_union_set *isl_union_map_wrap( + __isl_take isl_union_map *umap); + +The input to C should +be the space of a set, while that of +C should be the space of a relation. +Conversely, the output of C is the space +of a relation, while that of C is the space of a set. + +=item * Flattening + +Remove any internal structure of domain (and range) of the given +set or relation. If there is any such internal structure in the input, +then the name of the space is also removed. + + #include + __isl_give isl_local_space * + isl_local_space_flatten_domain( + __isl_take isl_local_space *ls); + __isl_give isl_local_space * + isl_local_space_flatten_range( + __isl_take isl_local_space *ls); + + #include + __isl_give isl_basic_set *isl_basic_set_flatten( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_flatten( + __isl_take isl_set *set); + + #include + __isl_give isl_basic_map *isl_basic_map_flatten_domain( + __isl_take isl_basic_map *bmap); + __isl_give isl_basic_map *isl_basic_map_flatten_range( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_flatten_range( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_flatten_domain( + __isl_take isl_map *map); + __isl_give isl_basic_map *isl_basic_map_flatten( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_flatten( + __isl_take isl_map *map); + + #include + __isl_give isl_multi_val *isl_multi_val_flatten_range( + __isl_take isl_multi_val *mv); + + #include + __isl_give isl_multi_aff *isl_multi_aff_flatten_domain( + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_aff *isl_multi_aff_flatten_range( + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_flatten_range( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_flatten_range( + __isl_take isl_multi_union_pw_aff *mupa); + + #include + __isl_give isl_map *isl_set_flatten_map( + __isl_take isl_set *set); + +The function above constructs a relation +that maps the input set to a flattened version of the set. + +=item * Lifting + +Lift the input set to a space with extra dimensions corresponding +to the existentially quantified variables in the input. +In particular, the result lives in a wrapped map where the domain +is the original space and the range corresponds to the original +existentially quantified variables. + + #include + __isl_give isl_basic_set *isl_basic_set_lift( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_lift( + __isl_take isl_set *set); + __isl_give isl_union_set *isl_union_set_lift( + __isl_take isl_union_set *uset); + +Given a local space that contains the existentially quantified +variables of a set, a basic relation that, when applied to +a basic set, has essentially the same effect as C, +can be constructed using the following function. + + #include + __isl_give isl_basic_map *isl_local_space_lifting( + __isl_take isl_local_space *ls); + + #include + __isl_give isl_multi_aff *isl_multi_aff_lift( + __isl_take isl_multi_aff *maff, + __isl_give isl_local_space **ls); + +If the C argument of C is not C, +then it is assigned the local space that lies at the basis of +the lifting applied. + +=item * Internal Product + + #include + __isl_give isl_space *isl_space_zip( + __isl_take isl_space *space); + + #include + __isl_give isl_basic_map *isl_basic_map_zip( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_zip( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_map_zip( + __isl_take isl_union_map *umap); + +Given a relation with nested relations for domain and range, +interchange the range of the domain with the domain of the range. + +=item * Currying + + #include + __isl_give isl_space *isl_space_curry( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_uncurry( + __isl_take isl_space *space); + + #include + __isl_give isl_basic_map *isl_basic_map_curry( + __isl_take isl_basic_map *bmap); + __isl_give isl_basic_map *isl_basic_map_uncurry( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_curry( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_uncurry( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_map_curry( + __isl_take isl_union_map *umap); + __isl_give isl_union_map *isl_union_map_uncurry( + __isl_take isl_union_map *umap); + +Given a relation with a nested relation for domain, +the C functions +move the range of the nested relation out of the domain +and use it as the domain of a nested relation in the range, +with the original range as range of this nested relation. +The C functions perform the inverse operation. + + #include + __isl_give isl_space *isl_space_range_curry( + __isl_take isl_space *space); + + #include + __isl_give isl_map *isl_map_range_curry( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_map_range_curry( + __isl_take isl_union_map *umap); + +These functions apply the currying to the relation that +is nested inside the range of the input. + +=item * Aligning parameters + +Change the order of the parameters of the given set, relation +or function +such that the first parameters match those of C. +This may involve the introduction of extra parameters. +All parameters need to be named. + + #include + __isl_give isl_space *isl_space_align_params( + __isl_take isl_space *space1, + __isl_take isl_space *space2) + + #include + __isl_give isl_basic_set *isl_basic_set_align_params( + __isl_take isl_basic_set *bset, + __isl_take isl_space *model); + __isl_give isl_set *isl_set_align_params( + __isl_take isl_set *set, + __isl_take isl_space *model); + + #include + __isl_give isl_basic_map *isl_basic_map_align_params( + __isl_take isl_basic_map *bmap, + __isl_take isl_space *model); + __isl_give isl_map *isl_map_align_params( + __isl_take isl_map *map, + __isl_take isl_space *model); + + #include + __isl_give isl_multi_val *isl_multi_val_align_params( + __isl_take isl_multi_val *mv, + __isl_take isl_space *model); + + #include + __isl_give isl_aff *isl_aff_align_params( + __isl_take isl_aff *aff, + __isl_take isl_space *model); + __isl_give isl_multi_aff *isl_multi_aff_align_params( + __isl_take isl_multi_aff *multi, + __isl_take isl_space *model); + __isl_give isl_pw_aff *isl_pw_aff_align_params( + __isl_take isl_pw_aff *pwaff, + __isl_take isl_space *model); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_align_params( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_space *model); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_align_params( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_space *model); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_align_params( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_space *model); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_align_params( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_space *model); + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_align_params( + __isl_take isl_qpolynomial *qp, + __isl_take isl_space *model); + +=item * Unary Arithmetic Operations + + #include + __isl_give isl_set *isl_set_neg( + __isl_take isl_set *set); + #include + __isl_give isl_map *isl_map_neg( + __isl_take isl_map *map); + +C constructs a set containing the opposites of +the elements in its argument. +The domain of the result of C is the same +as the domain of its argument. The corresponding range +elements are the opposites of the corresponding range +elements in the argument. + + #include + __isl_give isl_multi_val *isl_multi_val_neg( + __isl_take isl_multi_val *mv); + + #include + __isl_give isl_aff *isl_aff_neg( + __isl_take isl_aff *aff); + __isl_give isl_multi_aff *isl_multi_aff_neg( + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_aff *isl_pw_aff_neg( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_neg( + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_neg( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_union_pw_aff *isl_union_pw_aff_neg( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_neg( + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_neg( + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_aff *isl_aff_ceil( + __isl_take isl_aff *aff); + __isl_give isl_pw_aff *isl_pw_aff_ceil( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_aff *isl_aff_floor( + __isl_take isl_aff *aff); + __isl_give isl_multi_aff *isl_multi_aff_floor( + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_aff *isl_pw_aff_floor( + __isl_take isl_pw_aff *pwaff); + __isl_give isl_union_pw_aff *isl_union_pw_aff_floor( + __isl_take isl_union_pw_aff *upa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_floor( + __isl_take isl_multi_union_pw_aff *mupa); + + #include + __isl_give isl_pw_aff *isl_pw_aff_list_min( + __isl_take isl_pw_aff_list *list); + __isl_give isl_pw_aff *isl_pw_aff_list_max( + __isl_take isl_pw_aff_list *list); + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_neg( + __isl_take isl_qpolynomial *qp); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_neg( + __isl_take isl_pw_qpolynomial *pwqp); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_neg( + __isl_take isl_union_pw_qpolynomial *upwqp); + __isl_give isl_qpolynomial *isl_qpolynomial_pow( + __isl_take isl_qpolynomial *qp, + unsigned exponent); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_pow( + __isl_take isl_pw_qpolynomial *pwqp, + unsigned exponent); + +=item * Evaluation + +The following functions evaluate a function in a point. + + #include + __isl_give isl_val *isl_pw_qpolynomial_eval( + __isl_take isl_pw_qpolynomial *pwqp, + __isl_take isl_point *pnt); + __isl_give isl_val *isl_pw_qpolynomial_fold_eval( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_point *pnt); + __isl_give isl_val *isl_union_pw_qpolynomial_eval( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_point *pnt); + __isl_give isl_val *isl_union_pw_qpolynomial_fold_eval( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_point *pnt); + +=item * Dimension manipulation + +It is usually not advisable to directly change the (input or output) +space of a set or a relation as this removes the name and the internal +structure of the space. However, the functions below can be useful +to add new parameters, assuming +C and C +are not sufficient. + + #include + __isl_give isl_space *isl_space_add_dims( + __isl_take isl_space *space, + enum isl_dim_type type, unsigned n); + __isl_give isl_space *isl_space_insert_dims( + __isl_take isl_space *space, + enum isl_dim_type type, unsigned pos, unsigned n); + __isl_give isl_space *isl_space_drop_dims( + __isl_take isl_space *space, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_space *isl_space_move_dims( + __isl_take isl_space *space, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + + #include + __isl_give isl_local_space *isl_local_space_add_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned n); + __isl_give isl_local_space *isl_local_space_insert_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_local_space *isl_local_space_drop_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + __isl_give isl_basic_set *isl_basic_set_add_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned n); + __isl_give isl_set *isl_set_add_dims( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned n); + __isl_give isl_basic_set *isl_basic_set_insert_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, + unsigned n); + __isl_give isl_set *isl_set_insert_dims( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, unsigned n); + __isl_give isl_basic_set *isl_basic_set_move_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + __isl_give isl_set *isl_set_move_dims( + __isl_take isl_set *set, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + + #include + __isl_give isl_basic_map *isl_basic_map_add_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned n); + __isl_give isl_map *isl_map_add_dims( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned n); + __isl_give isl_basic_map *isl_basic_map_insert_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, + unsigned n); + __isl_give isl_map *isl_map_insert_dims( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, unsigned n); + __isl_give isl_basic_map *isl_basic_map_move_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + __isl_give isl_map *isl_map_move_dims( + __isl_take isl_map *map, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + + #include + __isl_give isl_multi_val *isl_multi_val_insert_dims( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_multi_val *isl_multi_val_add_dims( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, unsigned n); + __isl_give isl_multi_val *isl_multi_val_drop_dims( + __isl_take isl_multi_val *mv, + enum isl_dim_type type, unsigned first, unsigned n); + + #include + __isl_give isl_aff *isl_aff_insert_dims( + __isl_take isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_multi_aff *isl_multi_aff_insert_dims( + __isl_take isl_multi_aff *ma, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_pw_aff *isl_pw_aff_insert_dims( + __isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_insert_dims( + __isl_take isl_multi_pw_aff *mpa, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_aff *isl_aff_add_dims( + __isl_take isl_aff *aff, + enum isl_dim_type type, unsigned n); + __isl_give isl_multi_aff *isl_multi_aff_add_dims( + __isl_take isl_multi_aff *ma, + enum isl_dim_type type, unsigned n); + __isl_give isl_pw_aff *isl_pw_aff_add_dims( + __isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned n); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_add_dims( + __isl_take isl_multi_pw_aff *mpa, + enum isl_dim_type type, unsigned n); + __isl_give isl_aff *isl_aff_drop_dims( + __isl_take isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_multi_aff *isl_multi_aff_drop_dims( + __isl_take isl_multi_aff *maff, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_pw_aff *isl_pw_aff_drop_dims( + __isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_drop_dims( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_union_pw_aff *isl_union_pw_aff_drop_dims( + __isl_take isl_union_pw_aff *upa, + enum isl_dim_type type, unsigned first, unsigned n); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_drop_dims( + __isl_take isl_union_pw_multi_aff *upma, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_drop_dims( + __isl_take isl_multi_union_pw_aff *mupa, + enum isl_dim_type type, unsigned first, + unsigned n); + __isl_give isl_aff *isl_aff_move_dims( + __isl_take isl_aff *aff, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + __isl_give isl_multi_aff *isl_multi_aff_move_dims( + __isl_take isl_multi_aff *ma, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + __isl_give isl_pw_aff *isl_pw_aff_move_dims( + __isl_take isl_pw_aff *pa, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_move_dims( + __isl_take isl_multi_pw_aff *pma, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, + unsigned n); + + #include + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_drop_dims( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type, + unsigned first, unsigned n); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_drop_dims( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type, + unsigned first, unsigned n); + +The operations on union expressions can only manipulate parameters. + +=back + +=head2 Binary Operations + +The two arguments of a binary operation not only need to live +in the same C, they currently also need to have +the same (number of) parameters. + +=head3 Basic Operations + +=over + +=item * Intersection + + #include + __isl_give isl_local_space *isl_local_space_intersect( + __isl_take isl_local_space *ls1, + __isl_take isl_local_space *ls2); + + #include + __isl_give isl_basic_set *isl_basic_set_intersect_params( + __isl_take isl_basic_set *bset1, + __isl_take isl_basic_set *bset2); + __isl_give isl_basic_set *isl_basic_set_intersect( + __isl_take isl_basic_set *bset1, + __isl_take isl_basic_set *bset2); + __isl_give isl_basic_set *isl_basic_set_list_intersect( + __isl_take struct isl_basic_set_list *list); + __isl_give isl_set *isl_set_intersect_params( + __isl_take isl_set *set, + __isl_take isl_set *params); + __isl_give isl_set *isl_set_intersect( + __isl_take isl_set *set1, + __isl_take isl_set *set2); + + #include + __isl_give isl_basic_map *isl_basic_map_intersect_domain( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *bset); + __isl_give isl_basic_map *isl_basic_map_intersect_range( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *bset); + __isl_give isl_basic_map *isl_basic_map_intersect( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_basic_map *isl_basic_map_list_intersect( + __isl_take isl_basic_map_list *list); + __isl_give isl_map *isl_map_intersect_params( + __isl_take isl_map *map, + __isl_take isl_set *params); + __isl_give isl_map *isl_map_intersect_domain( + __isl_take isl_map *map, + __isl_take isl_set *set); + __isl_give isl_map *isl_map_intersect_range( + __isl_take isl_map *map, + __isl_take isl_set *set); + __isl_give isl_map *isl_map_intersect( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + + #include + __isl_give isl_union_set *isl_union_set_intersect_params( + __isl_take isl_union_set *uset, + __isl_take isl_set *set); + __isl_give isl_union_set *isl_union_set_intersect( + __isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2); + + #include + __isl_give isl_union_map *isl_union_map_intersect_params( + __isl_take isl_union_map *umap, + __isl_take isl_set *set); + __isl_give isl_union_map *isl_union_map_intersect_domain( + __isl_take isl_union_map *umap, + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_intersect_range( + __isl_take isl_union_map *umap, + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_intersect( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + + #include + __isl_give isl_pw_aff *isl_pw_aff_intersect_domain( + __isl_take isl_pw_aff *pa, + __isl_take isl_set *set); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_intersect_domain( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_set *domain); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_domain( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_set *set); + __isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_union_set *uset); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_intersect_domain( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_union_set *uset); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_intersect_domain( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_union_set *uset); + __isl_give isl_pw_aff *isl_pw_aff_intersect_params( + __isl_take isl_pw_aff *pa, + __isl_take isl_set *set); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_intersect_params( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_set *set); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_params( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_set *set); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_intersect_params( + __isl_take isl_union_pw_aff *upa, + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_intersect_params( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_set *set); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_intersect_params( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_set *params); + isl_multi_union_pw_aff_intersect_range( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_set *set); + + #include + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_intersect_domain( + __isl_take isl_pw_qpolynomial *pwpq, + __isl_take isl_set *set); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_intersect_domain( + __isl_take isl_union_pw_qpolynomial *upwpq, + __isl_take isl_union_set *uset); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_intersect_domain( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_set *uset); + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_intersect_params( + __isl_take isl_pw_qpolynomial *pwpq, + __isl_take isl_set *set); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_intersect_params( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_set *set); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_intersect_params( + __isl_take isl_union_pw_qpolynomial *upwpq, + __isl_take isl_set *set); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_intersect_params( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_set *set); + +The second argument to the C<_params> functions needs to be +a parametric (basic) set. For the other functions, a parametric set +for either argument is only allowed if the other argument is +a parametric set as well. +The list passed to C needs to have +at least one element and all elements need to live in the same space. +The function C +restricts the input function to those shared domain elements +that map to the specified range. + +=item * Union + + #include + __isl_give isl_set *isl_basic_set_union( + __isl_take isl_basic_set *bset1, + __isl_take isl_basic_set *bset2); + __isl_give isl_set *isl_set_union( + __isl_take isl_set *set1, + __isl_take isl_set *set2); + __isl_give isl_set *isl_set_list_union( + __isl_take isl_set_list *list); + + #include + __isl_give isl_map *isl_basic_map_union( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_map *isl_map_union( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + + #include + __isl_give isl_union_set *isl_union_set_union( + __isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2); + __isl_give isl_union_set *isl_union_set_list_union( + __isl_take isl_union_set_list *list); + + #include + __isl_give isl_union_map *isl_union_map_union( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + +The list passed to C needs to have +at least one element and all elements need to live in the same space. + +=item * Set difference + + #include + __isl_give isl_set *isl_set_subtract( + __isl_take isl_set *set1, + __isl_take isl_set *set2); + + #include + __isl_give isl_map *isl_map_subtract( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + __isl_give isl_map *isl_map_subtract_domain( + __isl_take isl_map *map, + __isl_take isl_set *dom); + __isl_give isl_map *isl_map_subtract_range( + __isl_take isl_map *map, + __isl_take isl_set *dom); + + #include + __isl_give isl_union_set *isl_union_set_subtract( + __isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2); + + #include + __isl_give isl_union_map *isl_union_map_subtract( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + __isl_give isl_union_map *isl_union_map_subtract_domain( + __isl_take isl_union_map *umap, + __isl_take isl_union_set *dom); + __isl_give isl_union_map *isl_union_map_subtract_range( + __isl_take isl_union_map *umap, + __isl_take isl_union_set *dom); + + #include + __isl_give isl_pw_aff *isl_pw_aff_subtract_domain( + __isl_take isl_pw_aff *pa, + __isl_take isl_set *set); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_subtract_domain( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_set *set); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_subtract_domain( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_union_set *uset); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_subtract_domain( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_set *set); + + #include + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_subtract_domain( + __isl_take isl_pw_qpolynomial *pwpq, + __isl_take isl_set *set); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_subtract_domain( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_set *set); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_subtract_domain( + __isl_take isl_union_pw_qpolynomial *upwpq, + __isl_take isl_union_set *uset); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_subtract_domain( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_set *uset); + +=item * Application + + #include + __isl_give isl_space *isl_space_join( + __isl_take isl_space *left, + __isl_take isl_space *right); + + #include + __isl_give isl_basic_set *isl_basic_set_apply( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_map *bmap); + __isl_give isl_set *isl_set_apply( + __isl_take isl_set *set, + __isl_take isl_map *map); + __isl_give isl_union_set *isl_union_set_apply( + __isl_take isl_union_set *uset, + __isl_take isl_union_map *umap); + __isl_give isl_basic_map *isl_basic_map_apply_domain( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_basic_map *isl_basic_map_apply_range( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_map *isl_map_apply_domain( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + __isl_give isl_map *isl_map_apply_range( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + + #include + __isl_give isl_union_map *isl_union_map_apply_domain( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + __isl_give isl_union_map *isl_union_map_apply_range( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + + #include + __isl_give isl_union_pw_aff * + isl_multi_union_pw_aff_apply_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_aff *aff); + __isl_give isl_union_pw_aff * + isl_multi_union_pw_aff_apply_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_pw_aff *pa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_apply_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_apply_pw_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_pw_multi_aff *pma); + +The result of C is defined +over the shared domain of the elements of the input. The dimension is +required to be greater than zero. +The C argument of +C is allowed to be zero-dimensional, +but only if the range of the C argument +is also zero-dimensional. +Similarly for C. + + #include + __isl_give isl_pw_qpolynomial_fold * + isl_set_apply_pw_qpolynomial_fold( + __isl_take isl_set *set, + __isl_take isl_pw_qpolynomial_fold *pwf, + int *tight); + __isl_give isl_pw_qpolynomial_fold * + isl_map_apply_pw_qpolynomial_fold( + __isl_take isl_map *map, + __isl_take isl_pw_qpolynomial_fold *pwf, + int *tight); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_set_apply_union_pw_qpolynomial_fold( + __isl_take isl_union_set *uset, + __isl_take isl_union_pw_qpolynomial_fold *upwf, + int *tight); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_map_apply_union_pw_qpolynomial_fold( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_qpolynomial_fold *upwf, + int *tight); + +The functions taking a map +compose the given map with the given piecewise quasipolynomial reduction. +That is, compute a bound (of the same type as C or C itself) +over all elements in the intersection of the range of the map +and the domain of the piecewise quasipolynomial reduction +as a function of an element in the domain of the map. +The functions taking a set compute a bound over all elements in the +intersection of the set and the domain of the +piecewise quasipolynomial reduction. + +=item * Preimage + + #include + __isl_give isl_basic_set * + isl_basic_set_preimage_multi_aff( + __isl_take isl_basic_set *bset, + __isl_take isl_multi_aff *ma); + __isl_give isl_set *isl_set_preimage_multi_aff( + __isl_take isl_set *set, + __isl_take isl_multi_aff *ma); + __isl_give isl_set *isl_set_preimage_pw_multi_aff( + __isl_take isl_set *set, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_set *isl_set_preimage_multi_pw_aff( + __isl_take isl_set *set, + __isl_take isl_multi_pw_aff *mpa); + + #include + __isl_give isl_union_set * + isl_union_set_preimage_multi_aff( + __isl_take isl_union_set *uset, + __isl_take isl_multi_aff *ma); + __isl_give isl_union_set * + isl_union_set_preimage_pw_multi_aff( + __isl_take isl_union_set *uset, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_union_set * + isl_union_set_preimage_union_pw_multi_aff( + __isl_take isl_union_set *uset, + __isl_take isl_union_pw_multi_aff *upma); + + #include + __isl_give isl_basic_map * + isl_basic_map_preimage_domain_multi_aff( + __isl_take isl_basic_map *bmap, + __isl_take isl_multi_aff *ma); + __isl_give isl_map *isl_map_preimage_domain_multi_aff( + __isl_take isl_map *map, + __isl_take isl_multi_aff *ma); + __isl_give isl_map *isl_map_preimage_range_multi_aff( + __isl_take isl_map *map, + __isl_take isl_multi_aff *ma); + __isl_give isl_map * + isl_map_preimage_domain_pw_multi_aff( + __isl_take isl_map *map, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_map * + isl_map_preimage_range_pw_multi_aff( + __isl_take isl_map *map, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_map * + isl_map_preimage_domain_multi_pw_aff( + __isl_take isl_map *map, + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_basic_map * + isl_basic_map_preimage_range_multi_aff( + __isl_take isl_basic_map *bmap, + __isl_take isl_multi_aff *ma); + + #include + __isl_give isl_union_map * + isl_union_map_preimage_domain_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_aff *ma); + __isl_give isl_union_map * + isl_union_map_preimage_range_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_aff *ma); + __isl_give isl_union_map * + isl_union_map_preimage_domain_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_union_map * + isl_union_map_preimage_range_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_union_map * + isl_union_map_preimage_domain_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_union_map * + isl_union_map_preimage_range_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma); + +These functions compute the preimage of the given set or map domain/range under +the given function. In other words, the expression is plugged +into the set description or into the domain/range of the map. + +=item * Pullback + + #include + __isl_give isl_aff *isl_aff_pullback_aff( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_aff *isl_aff_pullback_multi_aff( + __isl_take isl_aff *aff, + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_aff *isl_pw_aff_pullback_multi_aff( + __isl_take isl_pw_aff *pa, + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_aff *isl_pw_aff_pullback_pw_multi_aff( + __isl_take isl_pw_aff *pa, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff( + __isl_take isl_pw_aff *pa, + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_aff *isl_multi_aff_pullback_multi_aff( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_pullback_multi_aff( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_pullback_multi_aff( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_multi_aff *ma); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_pullback_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_pullback_pw_multi_aff( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_pullback_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_union_pw_aff * + isl_union_pw_aff_pullback_union_pw_multi_aff( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_union_pw_multi_aff *upma); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_pullback_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_pullback_union_pw_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_union_pw_multi_aff *upma); + +These functions precompose the first expression by the second function. +In other words, the second function is plugged +into the first expression. + +=item * Locus + + #include + __isl_give isl_basic_set *isl_aff_eq_basic_set( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_set *isl_aff_eq_set( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_basic_set *isl_aff_le_basic_set( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_set *isl_aff_le_set( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_basic_set *isl_aff_ge_basic_set( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_set *isl_aff_ge_set( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_set *isl_pw_aff_eq_set( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_set *isl_pw_aff_ne_set( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_set *isl_pw_aff_le_set( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_set *isl_pw_aff_lt_set( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_set *isl_pw_aff_ge_set( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_set *isl_pw_aff_gt_set( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + + __isl_give isl_set *isl_multi_aff_lex_le_set( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_set *isl_multi_aff_lex_lt_set( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_set *isl_multi_aff_lex_ge_set( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_set *isl_multi_aff_lex_gt_set( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + + __isl_give isl_set *isl_pw_aff_list_eq_set( + __isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + __isl_give isl_set *isl_pw_aff_list_ne_set( + __isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + __isl_give isl_set *isl_pw_aff_list_le_set( + __isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + __isl_give isl_set *isl_pw_aff_list_lt_set( + __isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + __isl_give isl_set *isl_pw_aff_list_ge_set( + __isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + __isl_give isl_set *isl_pw_aff_list_gt_set( + __isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + +The function C returns a basic set +containing those elements in the shared space +of C and C where C is greater than or equal to C. +The function C returns a set +containing those elements in the shared domain +of C and C where C is +greater than or equal to C. +The function C returns a set +containing those elements in the shared domain space +where C is lexicographically smaller than or +equal to C. +The functions operating on C apply the corresponding +C function to each pair of elements in the two lists. + + #include + __isl_give isl_map *isl_pw_aff_eq_map( + __isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + __isl_give isl_map *isl_pw_aff_lt_map( + __isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + __isl_give isl_map *isl_pw_aff_gt_map( + __isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + + __isl_give isl_map *isl_multi_pw_aff_eq_map( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_map *isl_multi_pw_aff_lex_lt_map( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_map *isl_multi_pw_aff_lex_gt_map( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + +These functions return a map between domain elements of the arguments +where the function values satisfy the given relation. + + #include + __isl_give isl_union_map * + isl_union_map_eq_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_union_map * + isl_union_map_lex_lt_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_union_map * + isl_union_map_lex_gt_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa); + +These functions select the subset of elements in the union map +that have an equal or lexicographically smaller function value. + +=item * Cartesian Product + + #include + __isl_give isl_space *isl_space_product( + __isl_take isl_space *space1, + __isl_take isl_space *space2); + __isl_give isl_space *isl_space_domain_product( + __isl_take isl_space *space1, + __isl_take isl_space *space2); + __isl_give isl_space *isl_space_range_product( + __isl_take isl_space *space1, + __isl_take isl_space *space2); + +The functions +C, C +and C take pairs or relation spaces and +produce a single relations space, where either the domain, the range +or both domain and range are wrapped spaces of relations between +the domains and/or ranges of the input spaces. +If the product is only constructed over the domain or the range +then the ranges or the domains of the inputs should be the same. +The function C also accepts a pair of set spaces, +in which case it returns a wrapped space of a relation between the +two input spaces. + + #include + __isl_give isl_set *isl_set_product( + __isl_take isl_set *set1, + __isl_take isl_set *set2); + + #include + __isl_give isl_basic_map *isl_basic_map_domain_product( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_basic_map *isl_basic_map_range_product( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_basic_map *isl_basic_map_product( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_map *isl_map_domain_product( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + __isl_give isl_map *isl_map_range_product( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + __isl_give isl_map *isl_map_product( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + + #include + __isl_give isl_union_set *isl_union_set_product( + __isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2); + + #include + __isl_give isl_union_map *isl_union_map_domain_product( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + __isl_give isl_union_map *isl_union_map_range_product( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + __isl_give isl_union_map *isl_union_map_product( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + + #include + __isl_give isl_multi_val *isl_multi_val_range_product( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + __isl_give isl_multi_val *isl_multi_val_product( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + + #include + __isl_give isl_multi_aff *isl_multi_aff_range_product( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_multi_aff *isl_multi_aff_product( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_range_product( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_product( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_range_product( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_product( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_range_product( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2); + +The above functions compute the cross product of the given +sets, relations or functions. The domains and ranges of the results +are wrapped maps between domains and ranges of the inputs. +To obtain a ``flat'' product, use the following functions +instead. + + #include + __isl_give isl_basic_set *isl_basic_set_flat_product( + __isl_take isl_basic_set *bset1, + __isl_take isl_basic_set *bset2); + __isl_give isl_set *isl_set_flat_product( + __isl_take isl_set *set1, + __isl_take isl_set *set2); + + #include + __isl_give isl_basic_map *isl_basic_map_flat_range_product( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_map *isl_map_flat_domain_product( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + __isl_give isl_map *isl_map_flat_range_product( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + __isl_give isl_basic_map *isl_basic_map_flat_product( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); + __isl_give isl_map *isl_map_flat_product( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + + #include + __isl_give isl_union_map * + isl_union_map_flat_domain_product( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + __isl_give isl_union_map * + isl_union_map_flat_range_product( + __isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); + + #include + __isl_give isl_multi_val *isl_multi_val_flat_range_product( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_aff *mv2); + + #include + __isl_give isl_multi_aff *isl_multi_aff_flat_range_product( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_flat_range_product( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_flat_range_product( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_flat_range_product( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_flat_range_product( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2); + + #include + __isl_give isl_space *isl_space_factor_domain( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_factor_range( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_domain_factor_domain( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_domain_factor_range( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_range_factor_domain( + __isl_take isl_space *space); + __isl_give isl_space *isl_space_range_factor_range( + __isl_take isl_space *space); + +The functions C and +C extract the two arguments from +the result of a call to C. + +The arguments of a call to a product can be extracted +from the result using the following functions. + + #include + __isl_give isl_map *isl_map_factor_domain( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_factor_range( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_domain_factor_domain( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_domain_factor_range( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_range_factor_domain( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_range_factor_range( + __isl_take isl_map *map); + + #include + __isl_give isl_union_map *isl_union_map_factor_domain( + __isl_take isl_union_map *umap); + __isl_give isl_union_map *isl_union_map_factor_range( + __isl_take isl_union_map *umap); + __isl_give isl_union_map * + isl_union_map_domain_factor_domain( + __isl_take isl_union_map *umap); + __isl_give isl_union_map * + isl_union_map_domain_factor_range( + __isl_take isl_union_map *umap); + __isl_give isl_union_map * + isl_union_map_range_factor_domain( + __isl_take isl_union_map *umap); + __isl_give isl_union_map * + isl_union_map_range_factor_range( + __isl_take isl_union_map *umap); + + #include + __isl_give isl_multi_val *isl_multi_val_factor_range( + __isl_take isl_multi_val *mv); + __isl_give isl_multi_val * + isl_multi_val_range_factor_domain( + __isl_take isl_multi_val *mv); + __isl_give isl_multi_val * + isl_multi_val_range_factor_range( + __isl_take isl_multi_val *mv); + + #include + __isl_give isl_multi_aff *isl_multi_aff_factor_range( + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_aff * + isl_multi_aff_range_factor_domain( + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_aff * + isl_multi_aff_range_factor_range( + __isl_take isl_multi_aff *ma); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_factor_range( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_range_factor_domain( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_range_factor_range( + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_factor_range( + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_range_factor_domain( + __isl_take isl_multi_union_pw_aff *mupa); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_range_factor_range( + __isl_take isl_multi_union_pw_aff *mupa); + +The splice functions are a generalization of the flat product functions, +where the second argument may be inserted at any position inside +the first argument rather than being placed at the end. +The functions C, +C, +C and +C +take functions that live in a set space. + + #include + __isl_give isl_multi_val *isl_multi_val_range_splice( + __isl_take isl_multi_val *mv1, unsigned pos, + __isl_take isl_multi_val *mv2); + + #include + __isl_give isl_multi_aff *isl_multi_aff_range_splice( + __isl_take isl_multi_aff *ma1, unsigned pos, + __isl_take isl_multi_aff *ma2); + __isl_give isl_multi_aff *isl_multi_aff_splice( + __isl_take isl_multi_aff *ma1, + unsigned in_pos, unsigned out_pos, + __isl_take isl_multi_aff *ma2); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_range_splice( + __isl_take isl_multi_pw_aff *mpa1, unsigned pos, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_splice( + __isl_take isl_multi_pw_aff *mpa1, + unsigned in_pos, unsigned out_pos, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_range_splice( + __isl_take isl_multi_union_pw_aff *mupa1, + unsigned pos, + __isl_take isl_multi_union_pw_aff *mupa2); + +=item * Simplification + +When applied to a set or relation, +the gist operation returns a set or relation that has the +same intersection with the context as the input set or relation. +Any implicit equality in the intersection is made explicit in the result, +while all inequalities that are redundant with respect to the intersection +are removed. +In case of union sets and relations, the gist operation is performed +per space. + +When applied to a function, +the gist operation applies the set gist operation to each of +the cells in the domain of the input piecewise expression. +The context is also exploited +to simplify the expression associated to each cell. + + #include + __isl_give isl_basic_set *isl_basic_set_gist( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_set *context); + __isl_give isl_set *isl_set_gist(__isl_take isl_set *set, + __isl_take isl_set *context); + __isl_give isl_set *isl_set_gist_params( + __isl_take isl_set *set, + __isl_take isl_set *context); + + #include + __isl_give isl_basic_map *isl_basic_map_gist( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_map *context); + __isl_give isl_basic_map *isl_basic_map_gist_domain( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *context); + __isl_give isl_map *isl_map_gist(__isl_take isl_map *map, + __isl_take isl_map *context); + __isl_give isl_map *isl_map_gist_params( + __isl_take isl_map *map, + __isl_take isl_set *context); + __isl_give isl_map *isl_map_gist_domain( + __isl_take isl_map *map, + __isl_take isl_set *context); + __isl_give isl_map *isl_map_gist_range( + __isl_take isl_map *map, + __isl_take isl_set *context); + + #include + __isl_give isl_union_set *isl_union_set_gist( + __isl_take isl_union_set *uset, + __isl_take isl_union_set *context); + __isl_give isl_union_set *isl_union_set_gist_params( + __isl_take isl_union_set *uset, + __isl_take isl_set *set); + + #include + __isl_give isl_union_map *isl_union_map_gist( + __isl_take isl_union_map *umap, + __isl_take isl_union_map *context); + __isl_give isl_union_map *isl_union_map_gist_params( + __isl_take isl_union_map *umap, + __isl_take isl_set *set); + __isl_give isl_union_map *isl_union_map_gist_domain( + __isl_take isl_union_map *umap, + __isl_take isl_union_set *uset); + __isl_give isl_union_map *isl_union_map_gist_range( + __isl_take isl_union_map *umap, + __isl_take isl_union_set *uset); + + #include + __isl_give isl_aff *isl_aff_gist_params( + __isl_take isl_aff *aff, + __isl_take isl_set *context); + __isl_give isl_aff *isl_aff_gist(__isl_take isl_aff *aff, + __isl_take isl_set *context); + __isl_give isl_multi_aff *isl_multi_aff_gist_params( + __isl_take isl_multi_aff *maff, + __isl_take isl_set *context); + __isl_give isl_multi_aff *isl_multi_aff_gist( + __isl_take isl_multi_aff *maff, + __isl_take isl_set *context); + __isl_give isl_pw_aff *isl_pw_aff_gist_params( + __isl_take isl_pw_aff *pwaff, + __isl_take isl_set *context); + __isl_give isl_pw_aff *isl_pw_aff_gist( + __isl_take isl_pw_aff *pwaff, + __isl_take isl_set *context); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_gist_params( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_set *set); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_gist( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_set *set); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_gist_params( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_set *set); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_gist( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_set *set); + __isl_give isl_union_pw_aff *isl_union_pw_aff_gist( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_union_set *context); + __isl_give isl_union_pw_aff *isl_union_pw_aff_gist_params( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_set *context); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_gist_params( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_set *context); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_gist( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_union_set *context); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_gist_params( + __isl_take isl_multi_union_pw_aff *aff, + __isl_take isl_set *context); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_gist( + __isl_take isl_multi_union_pw_aff *aff, + __isl_take isl_union_set *context); + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_gist_params( + __isl_take isl_qpolynomial *qp, + __isl_take isl_set *context); + __isl_give isl_qpolynomial *isl_qpolynomial_gist( + __isl_take isl_qpolynomial *qp, + __isl_take isl_set *context); + __isl_give isl_qpolynomial_fold * + isl_qpolynomial_fold_gist_params( + __isl_take isl_qpolynomial_fold *fold, + __isl_take isl_set *context); + __isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist( + __isl_take isl_qpolynomial_fold *fold, + __isl_take isl_set *context); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_gist_params( + __isl_take isl_pw_qpolynomial *pwqp, + __isl_take isl_set *context); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_gist( + __isl_take isl_pw_qpolynomial *pwqp, + __isl_take isl_set *context); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_gist( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_set *context); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_gist_params( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_set *context); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_gist_params( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_set *context); + __isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_gist( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_union_set *context); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_gist( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_set *context); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_gist_params( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_set *context); + +=item * Binary Arithmetic Operations + + #include + __isl_give isl_set *isl_set_sum( + __isl_take isl_set *set1, + __isl_take isl_set *set2); + #include + __isl_give isl_map *isl_map_sum( + __isl_take isl_map *map1, + __isl_take isl_map *map2); + +C computes the Minkowski sum of its two arguments, +i.e., the set containing the sums of pairs of elements from +C and C. +The domain of the result of C is the intersection +of the domains of its two arguments. The corresponding range +elements are the sums of the corresponding range elements +in the two arguments. + + #include + __isl_give isl_multi_val *isl_multi_val_add( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + __isl_give isl_multi_val *isl_multi_val_sub( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + + #include + __isl_give isl_aff *isl_aff_add( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_multi_aff *isl_multi_aff_add( + __isl_take isl_multi_aff *maff1, + __isl_take isl_multi_aff *maff2); + __isl_give isl_pw_aff *isl_pw_aff_add( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_add( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_add( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_union_pw_aff *isl_union_pw_aff_add( + __isl_take isl_union_pw_aff *upa1, + __isl_take isl_union_pw_aff *upa2); + __isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_add( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_add( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2); + __isl_give isl_pw_aff *isl_pw_aff_min( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_pw_aff *isl_pw_aff_max( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_aff *isl_aff_sub( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_multi_aff *isl_multi_aff_sub( + __isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + __isl_give isl_pw_aff *isl_pw_aff_sub( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_sub( + __isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_sub( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_union_pw_aff *isl_union_pw_aff_sub( + __isl_take isl_union_pw_aff *upa1, + __isl_take isl_union_pw_aff *upa2); + __isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_sub( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_sub( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2); + +C subtracts the second argument from the first. + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_add( + __isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_disjoint( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); + __isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add( + __isl_take isl_pw_qpolynomial_fold *pwf1, + __isl_take isl_pw_qpolynomial_fold *pwf2); + __isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_add( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2); + __isl_give isl_qpolynomial *isl_qpolynomial_sub( + __isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_sub( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); + __isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_sub( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2); + __isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fold( + __isl_take isl_pw_qpolynomial_fold *pwf1, + __isl_take isl_pw_qpolynomial_fold *pwf2); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_fold( + __isl_take isl_union_pw_qpolynomial_fold *upwf1, + __isl_take isl_union_pw_qpolynomial_fold *upwf2); + + #include + __isl_give isl_pw_aff *isl_pw_aff_union_add( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_add( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_union_pw_aff *isl_union_pw_aff_union_add( + __isl_take isl_union_pw_aff *upa1, + __isl_take isl_union_pw_aff *upa2); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_union_add( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_union_add( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2); + __isl_give isl_pw_aff *isl_pw_aff_union_min( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_pw_aff *isl_pw_aff_union_max( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + +The function C computes a piecewise quasi-affine +expression with a domain that is the union of those of C and +C and such that on each cell, the quasi-affine expression is +the maximum of those of C and C. If only one of +C or C is defined on a given cell, then the +associated expression is the defined one. +This in contrast to the C function, which is +only defined on the shared definition domain of the arguments. + + #include + __isl_give isl_multi_val *isl_multi_val_add_val( + __isl_take isl_multi_val *mv, + __isl_take isl_val *v); + __isl_give isl_multi_val *isl_multi_val_mod_val( + __isl_take isl_multi_val *mv, + __isl_take isl_val *v); + __isl_give isl_multi_val *isl_multi_val_scale_val( + __isl_take isl_multi_val *mv, + __isl_take isl_val *v); + __isl_give isl_multi_val *isl_multi_val_scale_down_val( + __isl_take isl_multi_val *mv, + __isl_take isl_val *v); + + #include + __isl_give isl_aff *isl_aff_mod_val(__isl_take isl_aff *aff, + __isl_take isl_val *mod); + __isl_give isl_pw_aff *isl_pw_aff_mod_val( + __isl_take isl_pw_aff *pa, + __isl_take isl_val *mod); + __isl_give isl_union_pw_aff *isl_union_pw_aff_mod_val( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_val *f); + __isl_give isl_aff *isl_aff_scale_val(__isl_take isl_aff *aff, + __isl_take isl_val *v); + __isl_give isl_multi_aff *isl_multi_aff_scale_val( + __isl_take isl_multi_aff *ma, + __isl_take isl_val *v); + __isl_give isl_pw_aff *isl_pw_aff_scale_val( + __isl_take isl_pw_aff *pa, __isl_take isl_val *v); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_scale_val( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_val *v); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_val( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_val *v); + __isl_give isl_union_pw_multi_aff * + __isl_give isl_union_pw_aff *isl_union_pw_aff_scale_val( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_val *f); + isl_union_pw_multi_aff_scale_val( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_val *val); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_scale_val( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_val *v); + __isl_give isl_aff *isl_aff_scale_down_ui( + __isl_take isl_aff *aff, unsigned f); + __isl_give isl_aff *isl_aff_scale_down_val( + __isl_take isl_aff *aff, __isl_take isl_val *v); + __isl_give isl_multi_aff *isl_multi_aff_scale_down_val( + __isl_take isl_multi_aff *ma, + __isl_take isl_val *v); + __isl_give isl_pw_aff *isl_pw_aff_scale_down_val( + __isl_take isl_pw_aff *pa, + __isl_take isl_val *f); + __isl_give isl_multi_pw_aff *isl_multi_pw_aff_scale_down_val( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_val *v); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_down_val( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_val *v); + __isl_give isl_union_pw_aff *isl_union_pw_aff_scale_down_val( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_val *v); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_scale_down_val( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_val *val); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_scale_down_val( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_val *v); + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_scale_val( + __isl_take isl_qpolynomial *qp, + __isl_take isl_val *v); + __isl_give isl_qpolynomial_fold * + isl_qpolynomial_fold_scale_val( + __isl_take isl_qpolynomial_fold *fold, + __isl_take isl_val *v); + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_scale_val( + __isl_take isl_pw_qpolynomial *pwqp, + __isl_take isl_val *v); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_scale_val( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_val *v); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_scale_val( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_val *v); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_scale_val( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_val *v); + __isl_give isl_qpolynomial * + isl_qpolynomial_scale_down_val( + __isl_take isl_qpolynomial *qp, + __isl_take isl_val *v); + __isl_give isl_qpolynomial_fold * + isl_qpolynomial_fold_scale_down_val( + __isl_take isl_qpolynomial_fold *fold, + __isl_take isl_val *v); + __isl_give isl_pw_qpolynomial * + isl_pw_qpolynomial_scale_down_val( + __isl_take isl_pw_qpolynomial *pwqp, + __isl_take isl_val *v); + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_fold_scale_down_val( + __isl_take isl_pw_qpolynomial_fold *pwf, + __isl_take isl_val *v); + __isl_give isl_union_pw_qpolynomial * + isl_union_pw_qpolynomial_scale_down_val( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_val *v); + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_scale_down_val( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_val *v); + + #include + __isl_give isl_multi_val *isl_multi_val_mod_multi_val( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + __isl_give isl_multi_val *isl_multi_val_scale_multi_val( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + __isl_give isl_multi_val * + isl_multi_val_scale_down_multi_val( + __isl_take isl_multi_val *mv1, + __isl_take isl_multi_val *mv2); + + #include + __isl_give isl_multi_aff *isl_multi_aff_mod_multi_val( + __isl_take isl_multi_aff *ma, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_mod_multi_val( + __isl_take isl_multi_union_pw_aff *upma, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_mod_multi_val( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_aff *isl_multi_aff_scale_multi_val( + __isl_take isl_multi_aff *ma, + __isl_take isl_multi_val *mv); + __isl_give isl_pw_multi_aff * + isl_pw_multi_aff_scale_multi_val( + __isl_take isl_pw_multi_aff *pma, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_scale_multi_val( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_scale_multi_val( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_multi_val *mv); + __isl_give isl_union_pw_multi_aff * + isl_union_pw_multi_aff_scale_multi_val( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_aff * + isl_multi_aff_scale_down_multi_val( + __isl_take isl_multi_aff *ma, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_pw_aff * + isl_multi_pw_aff_scale_down_multi_val( + __isl_take isl_multi_pw_aff *mpa, + __isl_take isl_multi_val *mv); + __isl_give isl_multi_union_pw_aff * + isl_multi_union_pw_aff_scale_down_multi_val( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_multi_val *mv); + +C scales the elements of C +by the corresponding elements of C. + + #include + __isl_give isl_aff *isl_aff_mul( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_aff *isl_aff_div( + __isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + __isl_give isl_pw_aff *isl_pw_aff_mul( + __isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + __isl_give isl_pw_aff *isl_pw_aff_div( + __isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + __isl_give isl_pw_aff *isl_pw_aff_tdiv_q( + __isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + __isl_give isl_pw_aff *isl_pw_aff_tdiv_r( + __isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + +When multiplying two affine expressions, at least one of the two needs +to be a constant. Similarly, when dividing an affine expression by another, +the second expression needs to be a constant. +C computes the quotient of an integer division with +rounding towards zero. C computes the corresponding +remainder. + + #include + __isl_give isl_qpolynomial *isl_qpolynomial_mul( + __isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); + __isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); + __isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2); + +=back + +=head3 Lexicographic Optimization + +Given a (basic) set C (or C) and a zero-dimensional domain C, +the following functions +compute a set that contains the lexicographic minimum or maximum +of the elements in C (or C) for those values of the parameters +that satisfy C. +If C is not C, then C<*empty> is assigned a set +that contains the parameter values in C for which C (or C) +has no elements. +In other words, the union of the parameter values +for which the result is non-empty and of C<*empty> +is equal to C. + + #include + __isl_give isl_set *isl_basic_set_partial_lexmin( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_set *isl_basic_set_partial_lexmax( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_set *isl_set_partial_lexmin( + __isl_take isl_set *set, __isl_take isl_set *dom, + __isl_give isl_set **empty); + __isl_give isl_set *isl_set_partial_lexmax( + __isl_take isl_set *set, __isl_take isl_set *dom, + __isl_give isl_set **empty); + +Given a (basic) set C (or C), the following functions simply +return a set containing the lexicographic minimum or maximum +of the elements in C (or C). +In case of union sets, the optimum is computed per space. + + #include + __isl_give isl_set *isl_basic_set_lexmin( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_basic_set_lexmax( + __isl_take isl_basic_set *bset); + __isl_give isl_set *isl_set_lexmin( + __isl_take isl_set *set); + __isl_give isl_set *isl_set_lexmax( + __isl_take isl_set *set); + __isl_give isl_union_set *isl_union_set_lexmin( + __isl_take isl_union_set *uset); + __isl_give isl_union_set *isl_union_set_lexmax( + __isl_take isl_union_set *uset); + +Given a (basic) relation C (or C) and a domain C, +the following functions +compute a relation that maps each element of C +to the single lexicographic minimum or maximum +of the elements that are associated to that same +element in C (or C). +If C is not C, then C<*empty> is assigned a set +that contains the elements in C that do not map +to any elements in C (or C). +In other words, the union of the domain of the result and of C<*empty> +is equal to C. + + #include + __isl_give isl_map *isl_basic_map_partial_lexmax( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_map *isl_basic_map_partial_lexmin( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_map *isl_map_partial_lexmax( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty); + __isl_give isl_map *isl_map_partial_lexmin( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty); + +Given a (basic) map C (or C), the following functions simply +return a map mapping each element in the domain of +C (or C) to the lexicographic minimum or maximum +of all elements associated to that element. +In case of union relations, the optimum is computed per space. + + #include + __isl_give isl_map *isl_basic_map_lexmin( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_basic_map_lexmax( + __isl_take isl_basic_map *bmap); + __isl_give isl_map *isl_map_lexmin( + __isl_take isl_map *map); + __isl_give isl_map *isl_map_lexmax( + __isl_take isl_map *map); + __isl_give isl_union_map *isl_union_map_lexmin( + __isl_take isl_union_map *umap); + __isl_give isl_union_map *isl_union_map_lexmax( + __isl_take isl_union_map *umap); + +The following functions return their result in the form of +a piecewise multi-affine expression, +but are otherwise equivalent to the corresponding functions +returning a basic set or relation. + + #include + __isl_give isl_pw_multi_aff * + isl_basic_set_partial_lexmin_pw_multi_aff( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_pw_multi_aff * + isl_basic_set_partial_lexmax_pw_multi_aff( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_pw_multi_aff *isl_set_lexmin_pw_multi_aff( + __isl_take isl_set *set); + __isl_give isl_pw_multi_aff *isl_set_lexmax_pw_multi_aff( + __isl_take isl_set *set); + + #include + __isl_give isl_pw_multi_aff * + isl_basic_map_lexmin_pw_multi_aff( + __isl_take isl_basic_map *bmap); + __isl_give isl_pw_multi_aff * + isl_basic_map_partial_lexmin_pw_multi_aff( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_pw_multi_aff * + isl_basic_map_partial_lexmax_pw_multi_aff( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); + __isl_give isl_pw_multi_aff *isl_map_lexmin_pw_multi_aff( + __isl_take isl_map *map); + __isl_give isl_pw_multi_aff *isl_map_lexmax_pw_multi_aff( + __isl_take isl_map *map); + +The following functions return the lexicographic minimum or maximum +on the shared domain of the inputs and the single defined function +on those parts of the domain where only a single function is defined. + + #include + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmin( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + __isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmax( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + +If the input to a lexicographic optimization problem has +multiple constraints with the same coefficients for the optimized +variables, then, by default, this symmetry is exploited by +replacing those constraints by a single constraint with +an abstract bound, which is in turn bounded by the corresponding terms +in the original constraints. +Without this optimization, the solver would typically consider +all possible orderings of those original bounds, resulting in a needless +decomposition of the domain. +However, the optimization can also result in slowdowns since +an extra parameter is introduced that may get used in additional +integer divisions. +The following option determines whether symmetry detection is applied +during lexicographic optimization. + + #include + isl_stat isl_options_set_pip_symmetry(isl_ctx *ctx, + int val); + int isl_options_get_pip_symmetry(isl_ctx *ctx); + +=begin latex + +See also \autoref{s:offline}. + +=end latex + +=head2 Ternary Operations + + #include + __isl_give isl_pw_aff *isl_pw_aff_cond( + __isl_take isl_pw_aff *cond, + __isl_take isl_pw_aff *pwaff_true, + __isl_take isl_pw_aff *pwaff_false); + +The function C performs a conditional operator +and returns an expression that is equal to C +for elements where C is non-zero and equal to C for elements +where C is zero. + +=head2 Lists + +Lists are defined over several element types, including +C, C, C, C, C, +C, C, +C, C, C, C, C, +C, C and C. +Here we take lists of Cs as an example. +Lists can be created, copied, modified and freed using the following functions. + + #include + __isl_give isl_set_list *isl_set_list_from_set( + __isl_take isl_set *el); + __isl_give isl_set_list *isl_set_list_alloc( + isl_ctx *ctx, int n); + __isl_give isl_set_list *isl_set_list_copy( + __isl_keep isl_set_list *list); + __isl_give isl_set_list *isl_set_list_insert( + __isl_take isl_set_list *list, unsigned pos, + __isl_take isl_set *el); + __isl_give isl_set_list *isl_set_list_add( + __isl_take isl_set_list *list, + __isl_take isl_set *el); + __isl_give isl_set_list *isl_set_list_drop( + __isl_take isl_set_list *list, + unsigned first, unsigned n); + __isl_give isl_set_list *isl_set_list_set_set( + __isl_take isl_set_list *list, int index, + __isl_take isl_set *set); + __isl_give isl_set_list *isl_set_list_concat( + __isl_take isl_set_list *list1, + __isl_take isl_set_list *list2); + __isl_give isl_set_list *isl_set_list_sort( + __isl_take isl_set_list *list, + int (*cmp)(__isl_keep isl_set *a, + __isl_keep isl_set *b, void *user), + void *user); + __isl_null isl_set_list *isl_set_list_free( + __isl_take isl_set_list *list); + +C creates an empty list with an initial capacity +for C elements. C and C +add elements to a list, increasing its capacity as needed. +C creates a list with a single element. + +Lists can be inspected using the following functions. + + #include + int isl_set_list_n_set(__isl_keep isl_set_list *list); + __isl_give isl_set *isl_set_list_get_set( + __isl_keep isl_set_list *list, int index); + isl_stat isl_set_list_foreach(__isl_keep isl_set_list *list, + isl_stat (*fn)(__isl_take isl_set *el, void *user), + void *user); + isl_stat isl_set_list_foreach_scc( + __isl_keep isl_set_list *list, + isl_bool (*follows)(__isl_keep isl_set *a, + __isl_keep isl_set *b, void *user), + void *follows_user, + isl_stat (*fn)(__isl_take isl_set *el, void *user), + void *fn_user); + +The function C calls C on each of the +strongly connected components of the graph with as vertices the elements +of C and a directed edge from vertex C to vertex C +iff C returns C. The callbacks C and +C should return C or C on error. + +Lists can be printed using + + #include + __isl_give isl_printer *isl_printer_print_set_list( + __isl_take isl_printer *p, + __isl_keep isl_set_list *list); + +=head2 Associative arrays + +Associative arrays map isl objects of a specific type to isl objects +of some (other) specific type. They are defined for several pairs +of types, including (C, C), +(C, C), +(C, C) and +(C, C). +Here, we take associative arrays that map Cs to Cs +as an example. + +Associative arrays can be created, copied and freed using +the following functions. + + #include + __isl_give isl_id_to_ast_expr *isl_id_to_ast_expr_alloc( + isl_ctx *ctx, int min_size); + __isl_give isl_id_to_ast_expr *isl_id_to_ast_expr_copy( + __isl_keep isl_id_to_ast_expr *id2expr); + __isl_null isl_id_to_ast_expr *isl_id_to_ast_expr_free( + __isl_take isl_id_to_ast_expr *id2expr); + +The C argument to C can be used +to specify the expected size of the associative array. +The associative array will be grown automatically as needed. + +Associative arrays can be inspected using the following functions. + + #include + __isl_give isl_maybe_isl_ast_expr + isl_id_to_ast_expr_try_get( + __isl_keep isl_id_to_ast_expr *id2expr, + __isl_keep isl_id *key); + isl_bool isl_id_to_ast_expr_has( + __isl_keep isl_id_to_ast_expr *id2expr, + __isl_keep isl_id *key); + __isl_give isl_ast_expr *isl_id_to_ast_expr_get( + __isl_keep isl_id_to_ast_expr *id2expr, + __isl_take isl_id *key); + isl_stat isl_id_to_ast_expr_foreach( + __isl_keep isl_id_to_ast_expr *id2expr, + isl_stat (*fn)(__isl_take isl_id *key, + __isl_take isl_ast_expr *val, void *user), + void *user); + +The function C returns a structure +containing two elements, C and C. +If there is a value associated to the key, then C +is set to C and C contains a copy of +the associated value. Otherwise C is C and +C may be C or C depending +on whether some error has occurred or there simply is no associated value. +The function C returns the C field +in the structure and +the function C returns the C field. + +Associative arrays can be modified using the following functions. + + #include + __isl_give isl_id_to_ast_expr *isl_id_to_ast_expr_set( + __isl_take isl_id_to_ast_expr *id2expr, + __isl_take isl_id *key, + __isl_take isl_ast_expr *val); + __isl_give isl_id_to_ast_expr *isl_id_to_ast_expr_drop( + __isl_take isl_id_to_ast_expr *id2expr, + __isl_take isl_id *key); + +Associative arrays can be printed using the following function. + + #include + __isl_give isl_printer *isl_printer_print_id_to_ast_expr( + __isl_take isl_printer *p, + __isl_keep isl_id_to_ast_expr *id2expr); + +=head2 Vectors + +Vectors can be created, copied and freed using the following functions. + + #include + __isl_give isl_vec *isl_vec_alloc(isl_ctx *ctx, + unsigned size); + __isl_give isl_vec *isl_vec_copy(__isl_keep isl_vec *vec); + __isl_null isl_vec *isl_vec_free(__isl_take isl_vec *vec); + +Note that the elements of a newly created vector may have arbitrary values. +The elements can be changed and inspected using the following functions. + + int isl_vec_size(__isl_keep isl_vec *vec); + __isl_give isl_val *isl_vec_get_element_val( + __isl_keep isl_vec *vec, int pos); + __isl_give isl_vec *isl_vec_set_element_si( + __isl_take isl_vec *vec, int pos, int v); + __isl_give isl_vec *isl_vec_set_element_val( + __isl_take isl_vec *vec, int pos, + __isl_take isl_val *v); + __isl_give isl_vec *isl_vec_set_si(__isl_take isl_vec *vec, + int v); + __isl_give isl_vec *isl_vec_set_val( + __isl_take isl_vec *vec, __isl_take isl_val *v); + int isl_vec_cmp_element(__isl_keep isl_vec *vec1, + __isl_keep isl_vec *vec2, int pos); + +C will return a negative value if anything went wrong. +In that case, the value of C<*v> is undefined. + +The following function can be used to concatenate two vectors. + + __isl_give isl_vec *isl_vec_concat(__isl_take isl_vec *vec1, + __isl_take isl_vec *vec2); + +=head2 Matrices + +Matrices can be created, copied and freed using the following functions. + + #include + __isl_give isl_mat *isl_mat_alloc(isl_ctx *ctx, + unsigned n_row, unsigned n_col); + __isl_give isl_mat *isl_mat_copy(__isl_keep isl_mat *mat); + __isl_null isl_mat *isl_mat_free(__isl_take isl_mat *mat); + +Note that the elements of a newly created matrix may have arbitrary values. +The elements can be changed and inspected using the following functions. + + int isl_mat_rows(__isl_keep isl_mat *mat); + int isl_mat_cols(__isl_keep isl_mat *mat); + __isl_give isl_val *isl_mat_get_element_val( + __isl_keep isl_mat *mat, int row, int col); + __isl_give isl_mat *isl_mat_set_element_si(__isl_take isl_mat *mat, + int row, int col, int v); + __isl_give isl_mat *isl_mat_set_element_val( + __isl_take isl_mat *mat, int row, int col, + __isl_take isl_val *v); + +C will return a negative value if anything went wrong. +In that case, the value of C<*v> is undefined. + +The following function can be used to compute the (right) inverse +of a matrix, i.e., a matrix such that the product of the original +and the inverse (in that order) is a multiple of the identity matrix. +The input matrix is assumed to be of full row-rank. + + __isl_give isl_mat *isl_mat_right_inverse(__isl_take isl_mat *mat); + +The following function can be used to compute the (right) kernel +(or null space) of a matrix, i.e., a matrix such that the product of +the original and the kernel (in that order) is the zero matrix. + + __isl_give isl_mat *isl_mat_right_kernel(__isl_take isl_mat *mat); + +=head2 Bounds on Piecewise Quasipolynomials and Piecewise Quasipolynomial Reductions + +The following functions determine +an upper or lower bound on a quasipolynomial over its domain. + + __isl_give isl_pw_qpolynomial_fold * + isl_pw_qpolynomial_bound( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_fold type, int *tight); + + __isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_bound( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_fold type, int *tight); + +The C argument may be either C or C. +If C is not C, then C<*tight> is set to C<1> +is the returned bound is known be tight, i.e., for each value +of the parameters there is at least +one element in the domain that reaches the bound. +If the domain of C is not wrapping, then the bound is computed +over all elements in that domain and the result has a purely parametric +domain. If the domain of C is wrapping, then the bound is +computed over the range of the wrapped relation. The domain of the +wrapped relation becomes the domain of the result. + +=head2 Parametric Vertex Enumeration + +The parametric vertex enumeration described in this section +is mainly intended to be used internally and by the C +library. + + #include + __isl_give isl_vertices *isl_basic_set_compute_vertices( + __isl_keep isl_basic_set *bset); + +The function C performs the +actual computation of the parametric vertices and the chamber +decomposition and store the result in an C object. +This information can be queried by either iterating over all +the vertices or iterating over all the chambers or cells +and then iterating over all vertices that are active on the chamber. + + isl_stat isl_vertices_foreach_vertex( + __isl_keep isl_vertices *vertices, + isl_stat (*fn)(__isl_take isl_vertex *vertex, + void *user), void *user); + + isl_stat isl_vertices_foreach_cell( + __isl_keep isl_vertices *vertices, + isl_stat (*fn)(__isl_take isl_cell *cell, + void *user), void *user); + isl_stat isl_cell_foreach_vertex(__isl_keep isl_cell *cell, + isl_stat (*fn)(__isl_take isl_vertex *vertex, + void *user), void *user); + +Other operations that can be performed on an C object are +the following. + + int isl_vertices_get_n_vertices( + __isl_keep isl_vertices *vertices); + void isl_vertices_free(__isl_take isl_vertices *vertices); + +Vertices can be inspected and destroyed using the following functions. + + int isl_vertex_get_id(__isl_keep isl_vertex *vertex); + __isl_give isl_basic_set *isl_vertex_get_domain( + __isl_keep isl_vertex *vertex); + __isl_give isl_multi_aff *isl_vertex_get_expr( + __isl_keep isl_vertex *vertex); + void isl_vertex_free(__isl_take isl_vertex *vertex); + +C returns a multiple quasi-affine expression +describing the vertex in terms of the parameters, +while C returns the activity domain +of the vertex. + +Chambers can be inspected and destroyed using the following functions. + + __isl_give isl_basic_set *isl_cell_get_domain( + __isl_keep isl_cell *cell); + void isl_cell_free(__isl_take isl_cell *cell); + +=head1 Polyhedral Compilation Library + +This section collects functionality in C that has been specifically +designed for use during polyhedral compilation. + +=head2 Schedule Trees + +A schedule tree is a structured representation of a schedule, +assigning a relative order to a set of domain elements. +The relative order expressed by the schedule tree is +defined recursively. In particular, the order between +two domain elements is determined by the node that is closest +to the root that refers to both elements and that orders them apart. +Each node in the tree is of one of several types. +The root node is always of type C +(or C) +and it describes the (extra) domain elements to which the schedule applies. +The other types of nodes are as follows. + +=over + +=item C + +A band of schedule dimensions. Each schedule dimension is represented +by a union piecewise quasi-affine expression. If this expression +assigns a different value to two domain elements, while all previous +schedule dimensions in the same band assign them the same value, +then the two domain elements are ordered according to these two +different values. +Each expression is required to be total in the domain elements +that reach the band node. + +=item C + +An expansion node maps each of the domain elements that reach the node +to one or more domain elements. The image of this mapping forms +the set of domain elements that reach the child of the expansion node. +The function that maps each of the expanded domain elements +to the original domain element from which it was expanded +is called the contraction. + +=item C + +A filter node does not impose any ordering, but rather intersects +the set of domain elements that the current subtree refers to +with a given union set. The subtree of the filter node only +refers to domain elements in the intersection. +A filter node is typically only used a child of a sequence or +set node. + +=item C + +A leaf of the schedule tree. Leaf nodes do not impose any ordering. + +=item C + +A mark node can be used to attach any kind of information to a subtree +of the schedule tree. + +=item C + +A sequence node has one or more children, each of which is a filter node. +The filters on these filter nodes form a partition of +the domain elements that the current subtree refers to. +If two domain elements appear in distinct filters then the sequence +node orders them according to the child positions of the corresponding +filter nodes. + +=item C + +A set node is similar to a sequence node, except that +it expresses that domain elements appearing in distinct filters +may have any order. The order of the children of a set node +is therefore also immaterial. + +=back + +The following node types are only supported by the AST generator. + +=over + +=item C + +The context describes constraints on the parameters and +the schedule dimensions of outer +bands that the AST generator may assume to hold. It is also the only +kind of node that may introduce additional parameters. +The space of the context is that of the flat product of the outer +band nodes. In particular, if there are no outer band nodes, then +this space is the unnamed zero-dimensional space. +Since a context node references the outer band nodes, any tree +containing a context node is considered to be anchored. + +=item C + +An extension node instructs the AST generator to add additional +domain elements that need to be scheduled. +The additional domain elements are described by the range of +the extension map in terms of the outer schedule dimensions, +i.e., the flat product of the outer band nodes. +Note that domain elements are added whenever the AST generator +reaches the extension node, meaning that there are still some +active domain elements for which an AST needs to be generated. +The conditions under which some domain elements are still active +may however not be completely described by the outer AST nodes +generated at that point. + +An extension node may also appear as the root of a schedule tree, +when it is intended to be inserted into another tree +using C or C. +In this case, the domain of the extension node should +correspond to the flat product of the outer band nodes +in this other schedule tree at the point where the extension tree +will be inserted. + +=item C + +The guard describes constraints on the parameters and +the schedule dimensions of outer +bands that need to be enforced by the outer nodes +in the generated AST. +The space of the guard is that of the flat product of the outer +band nodes. In particular, if there are no outer band nodes, then +this space is the unnamed zero-dimensional space. +Since a guard node references the outer band nodes, any tree +containing a guard node is considered to be anchored. + +=back + +Except for the C nodes, +none of the nodes may introduce any parameters that were not +already present in the root domain node. + +A schedule tree is encapsulated in an C object. +The simplest such objects, those with a tree consisting of single domain node, +can be created using the following functions with either an empty +domain or a given domain. + + #include + __isl_give isl_schedule *isl_schedule_empty( + __isl_take isl_space *space); + __isl_give isl_schedule *isl_schedule_from_domain( + __isl_take isl_union_set *domain); + +The function C described +in L can also be used to construct schedules. + +C objects may be copied and freed using the following functions. + + #include + __isl_give isl_schedule *isl_schedule_copy( + __isl_keep isl_schedule *sched); + __isl_null isl_schedule *isl_schedule_free( + __isl_take isl_schedule *sched); + +The following functions checks whether two C objects +are obviously the same. + + #include + isl_bool isl_schedule_plain_is_equal( + __isl_keep isl_schedule *schedule1, + __isl_keep isl_schedule *schedule2); + +The domain of the schedule, i.e., the domain described by the root node, +can be obtained using the following function. + + #include + __isl_give isl_union_set *isl_schedule_get_domain( + __isl_keep isl_schedule *schedule); + +An extra top-level band node (right underneath the domain node) can +be introduced into the schedule using the following function. +The schedule tree is assumed not to have any anchored nodes. + + #include + __isl_give isl_schedule * + isl_schedule_insert_partial_schedule( + __isl_take isl_schedule *schedule, + __isl_take isl_multi_union_pw_aff *partial); + +A top-level context node (right underneath the domain node) can +be introduced into the schedule using the following function. + + #include + __isl_give isl_schedule *isl_schedule_insert_context( + __isl_take isl_schedule *schedule, + __isl_take isl_set *context) + +A top-level guard node (right underneath the domain node) can +be introduced into the schedule using the following function. + + #include + __isl_give isl_schedule *isl_schedule_insert_guard( + __isl_take isl_schedule *schedule, + __isl_take isl_set *guard) + +A schedule that combines two schedules either in the given +order or in an arbitrary order, i.e., with an C +or an C node, +can be created using the following functions. + + #include + __isl_give isl_schedule *isl_schedule_sequence( + __isl_take isl_schedule *schedule1, + __isl_take isl_schedule *schedule2); + __isl_give isl_schedule *isl_schedule_set( + __isl_take isl_schedule *schedule1, + __isl_take isl_schedule *schedule2); + +The domains of the two input schedules need to be disjoint. + +The following function can be used to restrict the domain +of a schedule with a domain node as root to be a subset of the given union set. +This operation may remove nodes in the tree that have become +redundant. + + #include + __isl_give isl_schedule *isl_schedule_intersect_domain( + __isl_take isl_schedule *schedule, + __isl_take isl_union_set *domain); + +The following function can be used to simplify the domain +of a schedule with a domain node as root with respect to the given +parameter domain. + + #include + __isl_give isl_schedule *isl_schedule_gist_domain_params( + __isl_take isl_schedule *schedule, + __isl_take isl_set *context); + +The following function resets the user pointers on all parameter +and tuple identifiers referenced by the nodes of the given schedule. + + #include + __isl_give isl_schedule *isl_schedule_reset_user( + __isl_take isl_schedule *schedule); + +The following function aligns the parameters of all nodes +in the given schedule to the given space. + + #include + __isl_give isl_schedule *isl_schedule_align_params( + __isl_take isl_schedule *schedule, + __isl_take isl_space *space); + +The following function allows the user to plug in a given function +in the iteration domains. The input schedule is not allowed to contain +any expansion nodes. + + #include + __isl_give isl_schedule * + isl_schedule_pullback_union_pw_multi_aff( + __isl_take isl_schedule *schedule, + __isl_take isl_union_pw_multi_aff *upma); + +The following function can be used to plug in the schedule C +in the leaves of C, where C describes how +the domain elements of C map to the domain elements +at the original leaves of C. +The resulting schedule will contain expansion nodes, unless +C is an identity function. + + #include + __isl_give isl_schedule *isl_schedule_expand( + __isl_take isl_schedule *schedule, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_schedule *expansion); + +An C representation of the schedule can be obtained +from an C using the following function. + + #include + __isl_give isl_union_map *isl_schedule_get_map( + __isl_keep isl_schedule *sched); + +The resulting relation encodes the same relative ordering as +the schedule by mapping the domain elements to a common schedule space. +If the schedule_separate_components option is set, then the order +of the children of a set node is explicitly encoded in the result. +If the tree contains any expansion nodes, then the relation +is formulated in terms of the expanded domain elements. + +Schedules can be read from input using the following functions. + + #include + __isl_give isl_schedule *isl_schedule_read_from_file( + isl_ctx *ctx, FILE *input); + __isl_give isl_schedule *isl_schedule_read_from_str( + isl_ctx *ctx, const char *str); + +A representation of the schedule can be printed using + + #include + __isl_give isl_printer *isl_printer_print_schedule( + __isl_take isl_printer *p, + __isl_keep isl_schedule *schedule); + __isl_give char *isl_schedule_to_str( + __isl_keep isl_schedule *schedule); + +C prints the schedule in flow format. + +The schedule tree can be traversed through the use of +C objects that point to a particular +position in the schedule tree. Whenever a C +is use to modify a node in the schedule tree, the original schedule +tree is left untouched and the modifications are performed to a copy +of the tree. The returned C then points to +this modified copy of the tree. + +The root of the schedule tree can be obtained using the following function. + + #include + __isl_give isl_schedule_node *isl_schedule_get_root( + __isl_keep isl_schedule *schedule); + +A pointer to a newly created schedule tree with a single domain +node can be created using the following functions. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_from_domain( + __isl_take isl_union_set *domain); + __isl_give isl_schedule_node * + isl_schedule_node_from_extension( + __isl_take isl_union_map *extension); + +C creates a tree with an extension +node as root. + +Schedule nodes can be copied and freed using the following functions. + + #include + __isl_give isl_schedule_node *isl_schedule_node_copy( + __isl_keep isl_schedule_node *node); + __isl_null isl_schedule_node *isl_schedule_node_free( + __isl_take isl_schedule_node *node); + +The following functions can be used to check if two schedule +nodes point to the same position in the same schedule. + + #include + isl_bool isl_schedule_node_is_equal( + __isl_keep isl_schedule_node *node1, + __isl_keep isl_schedule_node *node2); + +The following properties can be obtained from a schedule node. + + #include + enum isl_schedule_node_type isl_schedule_node_get_type( + __isl_keep isl_schedule_node *node); + enum isl_schedule_node_type + isl_schedule_node_get_parent_type( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule *isl_schedule_node_get_schedule( + __isl_keep isl_schedule_node *node); + +The function C returns the type of +the node, while C returns +type of the parent of the node, which is required to exist. +The function C returns a copy +to the schedule to which the node belongs. + +The following functions can be used to move the schedule node +to a different position in the tree or to check if such a position +exists. + + #include + isl_bool isl_schedule_node_has_parent( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node *isl_schedule_node_parent( + __isl_take isl_schedule_node *node); + __isl_give isl_schedule_node *isl_schedule_node_root( + __isl_take isl_schedule_node *node); + __isl_give isl_schedule_node *isl_schedule_node_ancestor( + __isl_take isl_schedule_node *node, + int generation); + int isl_schedule_node_n_children( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node *isl_schedule_node_child( + __isl_take isl_schedule_node *node, int pos); + isl_bool isl_schedule_node_has_children( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node *isl_schedule_node_first_child( + __isl_take isl_schedule_node *node); + isl_bool isl_schedule_node_has_previous_sibling( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node * + isl_schedule_node_previous_sibling( + __isl_take isl_schedule_node *node); + isl_bool isl_schedule_node_has_next_sibling( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node * + isl_schedule_node_next_sibling( + __isl_take isl_schedule_node *node); + +For C, the ancestor of generation 0 +is the node itself, the ancestor of generation 1 is its parent and so on. + +It is also possible to query the number of ancestors of a node, +the position of the current node +within the children of its parent, the position of the subtree +containing a node within the children of an ancestor +or to obtain a copy of a given +child without destroying the current node. +Given two nodes that point to the same schedule, their closest +shared ancestor can be obtained using +C. + + #include + int isl_schedule_node_get_tree_depth( + __isl_keep isl_schedule_node *node); + int isl_schedule_node_get_child_position( + __isl_keep isl_schedule_node *node); + int isl_schedule_node_get_ancestor_child_position( + __isl_keep isl_schedule_node *node, + __isl_keep isl_schedule_node *ancestor); + __isl_give isl_schedule_node *isl_schedule_node_get_child( + __isl_keep isl_schedule_node *node, int pos); + __isl_give isl_schedule_node * + isl_schedule_node_get_shared_ancestor( + __isl_keep isl_schedule_node *node1, + __isl_keep isl_schedule_node *node2); + +All nodes in a schedule tree or +all descendants of a specific node (including the node) can be visited +in depth-first pre-order using the following functions. + + #include + isl_stat isl_schedule_foreach_schedule_node_top_down( + __isl_keep isl_schedule *sched, + isl_bool (*fn)(__isl_keep isl_schedule_node *node, + void *user), void *user); + + #include + isl_stat isl_schedule_node_foreach_descendant_top_down( + __isl_keep isl_schedule_node *node, + isl_bool (*fn)(__isl_keep isl_schedule_node *node, + void *user), void *user); + +The callback function is slightly different from the usual +callbacks in that it not only indicates success (non-negative result) +or failure (negative result), but also indicates whether the children +of the given node should be visited. In particular, if the callback +returns a positive value, then the children are visited, but if +the callback returns zero, then the children are not visited. + +The ancestors of a node in a schedule tree can be visited from +the root down to and including the parent of the node using +the following function. + + #include + isl_stat isl_schedule_node_foreach_ancestor_top_down( + __isl_keep isl_schedule_node *node, + isl_stat (*fn)(__isl_keep isl_schedule_node *node, + void *user), void *user); + +The following functions allows for a depth-first post-order +traversal of the nodes in a schedule tree or +of the descendants of a specific node (including the node +itself), where the user callback is allowed to modify the +visited node. + + #include + __isl_give isl_schedule * + isl_schedule_map_schedule_node_bottom_up( + __isl_take isl_schedule *schedule, + __isl_give isl_schedule_node *(*fn)( + __isl_take isl_schedule_node *node, + void *user), void *user); + + #include + __isl_give isl_schedule_node * + isl_schedule_node_map_descendant_bottom_up( + __isl_take isl_schedule_node *node, + __isl_give isl_schedule_node *(*fn)( + __isl_take isl_schedule_node *node, + void *user), void *user); + +The traversal continues from the node returned by the callback function. +It is the responsibility of the user to ensure that this does not +lead to an infinite loop. It is safest to always return a pointer +to the same position (same ancestors and child positions) as the input node. + +The following function removes a node (along with its descendants) +from a schedule tree and returns a pointer to the leaf at the +same position in the updated tree. +It is not allowed to remove the root of a schedule tree or +a child of a set or sequence node. + + #include + __isl_give isl_schedule_node *isl_schedule_node_cut( + __isl_take isl_schedule_node *node); + +The following function removes a single node +from a schedule tree and returns a pointer to the child +of the node, now located at the position of the original node +or to a leaf node at that position if there was no child. +It is not allowed to remove the root of a schedule tree, +a set or sequence node, a child of a set or sequence node or +a band node with an anchored subtree. + + #include + __isl_give isl_schedule_node *isl_schedule_node_delete( + __isl_take isl_schedule_node *node); + +Most nodes in a schedule tree only contain local information. +In some cases, however, a node may also refer to outer band nodes. +This means that the position of the node within the tree should +not be changed, or at least that no changes are performed to the +outer band nodes. The following function can be used to test +whether the subtree rooted at a given node contains any such nodes. + + #include + isl_bool isl_schedule_node_is_subtree_anchored( + __isl_keep isl_schedule_node *node); + +The following function resets the user pointers on all parameter +and tuple identifiers referenced by the given schedule node. + + #include + __isl_give isl_schedule_node *isl_schedule_node_reset_user( + __isl_take isl_schedule_node *node); + +The following function aligns the parameters of the given schedule +node to the given space. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_align_params( + __isl_take isl_schedule_node *node, + __isl_take isl_space *space); + +Several node types have their own functions for querying +(and in some cases setting) some node type specific properties. + + #include + __isl_give isl_space *isl_schedule_node_band_get_space( + __isl_keep isl_schedule_node *node); + __isl_give isl_multi_union_pw_aff * + isl_schedule_node_band_get_partial_schedule( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_map * + isl_schedule_node_band_get_partial_schedule_union_map( + __isl_keep isl_schedule_node *node); + unsigned isl_schedule_node_band_n_member( + __isl_keep isl_schedule_node *node); + isl_bool isl_schedule_node_band_member_get_coincident( + __isl_keep isl_schedule_node *node, int pos); + __isl_give isl_schedule_node * + isl_schedule_node_band_member_set_coincident( + __isl_take isl_schedule_node *node, int pos, + int coincident); + isl_bool isl_schedule_node_band_get_permutable( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node * + isl_schedule_node_band_set_permutable( + __isl_take isl_schedule_node *node, int permutable); + enum isl_ast_loop_type + isl_schedule_node_band_member_get_ast_loop_type( + __isl_keep isl_schedule_node *node, int pos); + __isl_give isl_schedule_node * + isl_schedule_node_band_member_set_ast_loop_type( + __isl_take isl_schedule_node *node, int pos, + enum isl_ast_loop_type type); + __isl_give isl_union_set * + enum isl_ast_loop_type + isl_schedule_node_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_node *node, int pos); + __isl_give isl_schedule_node * + isl_schedule_node_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_node *node, int pos, + enum isl_ast_loop_type type); + isl_schedule_node_band_get_ast_build_options( + __isl_keep isl_schedule_node *node); + __isl_give isl_schedule_node * + isl_schedule_node_band_set_ast_build_options( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set *options); + __isl_give isl_set * + isl_schedule_node_band_get_ast_isolate_option( + __isl_keep isl_schedule_node *node); + +The function C returns the space +of the partial schedule of the band. +The function C +returns a representation of the partial schedule of the band node +in the form of an C. +The coincident and permutable properties are set by +C on the schedule tree +it produces. +A scheduling dimension is considered to be ``coincident'' +if it satisfies the coincidence constraints within its band. +That is, if the dependence distances of the coincidence +constraints are all zero in that direction (for fixed +iterations of outer bands). +A band is marked permutable if it was produced using the Pluto-like scheduler. +Note that the scheduler may have to resort to a Feautrier style scheduling +step even if the default scheduler is used. +An C is one of C, +C, C or C. +For the meaning of these loop AST generation types and the difference +between the regular loop AST generation type and the isolate +loop AST generation type, see L. +The functions C +and C +may return C if an error occurs. +The AST build options govern how an AST is generated for +the individual schedule dimensions during AST generation. +See L. +The isolate option for the given node can be extracted from these +AST build options using the function +C. + + #include + __isl_give isl_set * + isl_schedule_node_context_get_context( + __isl_keep isl_schedule_node *node); + + #include + __isl_give isl_union_set * + isl_schedule_node_domain_get_domain( + __isl_keep isl_schedule_node *node); + + #include + __isl_give isl_union_map * + isl_schedule_node_expansion_get_expansion( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_pw_multi_aff * + isl_schedule_node_expansion_get_contraction( + __isl_keep isl_schedule_node *node); + + #include + __isl_give isl_union_map * + isl_schedule_node_extension_get_extension( + __isl_keep isl_schedule_node *node); + + #include + __isl_give isl_union_set * + isl_schedule_node_filter_get_filter( + __isl_keep isl_schedule_node *node); + + #include + __isl_give isl_set *isl_schedule_node_guard_get_guard( + __isl_keep isl_schedule_node *node); + + #include + __isl_give isl_id *isl_schedule_node_mark_get_id( + __isl_keep isl_schedule_node *node); + +The following functions can be used to obtain an C, +an C or C representation of +partial schedules related to the node. + + #include + __isl_give isl_multi_union_pw_aff * + isl_schedule_node_get_prefix_schedule_multi_union_pw_aff( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_pw_multi_aff * + isl_schedule_node_get_prefix_schedule_union_pw_multi_aff( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_map * + isl_schedule_node_get_prefix_schedule_union_map( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_map * + isl_schedule_node_get_prefix_schedule_relation( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_map * + isl_schedule_node_get_subtree_schedule_union_map( + __isl_keep isl_schedule_node *node); + +In particular, the functions +C, +C +and C +return a relative ordering on the domain elements that reach the given +node determined by its ancestors. +The function C +additionally includes the domain constraints in the result. +The function C +returns a representation of the partial schedule defined by the +subtree rooted at the given node. +If the tree contains any expansion nodes, then the subtree schedule +is formulated in terms of the expanded domain elements. +The tree passed to functions returning a prefix schedule +may only contain extension nodes if these would not affect +the result of these functions. That is, if one of the ancestors +is an extension node, then all of the domain elements that were +added by the extension node need to have been filtered out +by filter nodes between the extension node and the input node. +The tree passed to C +may not contain in extension nodes in the selected subtree. + +The expansion/contraction defined by an entire subtree, combining +the expansions/contractions +on the expansion nodes in the subtree, can be obtained using +the following functions. + + #include + __isl_give isl_union_map * + isl_schedule_node_get_subtree_expansion( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_pw_multi_aff * + isl_schedule_node_get_subtree_contraction( + __isl_keep isl_schedule_node *node); + +The total number of outer band members of given node, i.e., +the shared output dimension of the maps in the result +of C can be obtained +using the following function. + + #include + int isl_schedule_node_get_schedule_depth( + __isl_keep isl_schedule_node *node); + +The following functions return the elements that reach the given node +or the union of universes in the spaces that contain these elements. + + #include + __isl_give isl_union_set * + isl_schedule_node_get_domain( + __isl_keep isl_schedule_node *node); + __isl_give isl_union_set * + isl_schedule_node_get_universe_domain( + __isl_keep isl_schedule_node *node); + +The input tree of C +may only contain extension nodes if these would not affect +the result of this function. That is, if one of the ancestors +is an extension node, then all of the domain elements that were +added by the extension node need to have been filtered out +by filter nodes between the extension node and the input node. + +The following functions can be used to introduce additional nodes +in the schedule tree. The new node is introduced at the point +in the tree where the C points to and +the results points to the new node. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_insert_partial_schedule( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_union_pw_aff *schedule); + +This function inserts a new band node with (the greatest integer +part of) the given partial schedule. +The subtree rooted at the given node is assumed not to have +any anchored nodes. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_insert_context( + __isl_take isl_schedule_node *node, + __isl_take isl_set *context); + +This function inserts a new context node with the given context constraints. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_insert_filter( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set *filter); + +This function inserts a new filter node with the given filter. +If the original node already pointed to a filter node, then the +two filter nodes are merged into one. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_insert_guard( + __isl_take isl_schedule_node *node, + __isl_take isl_set *guard); + +This function inserts a new guard node with the given guard constraints. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_insert_mark( + __isl_take isl_schedule_node *node, + __isl_take isl_id *mark); + +This function inserts a new mark node with the give mark identifier. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_insert_sequence( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set_list *filters); + __isl_give isl_schedule_node * + isl_schedule_node_insert_set( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set_list *filters); + +These functions insert a new sequence or set node with the given +filters as children. + + #include + __isl_give isl_schedule_node *isl_schedule_node_group( + __isl_take isl_schedule_node *node, + __isl_take isl_id *group_id); + +This function introduces an expansion node in between the current +node and its parent that expands instances of a space with tuple +identifier C to the original domain elements that reach +the node. The group instances are identified by the prefix schedule +of those domain elements. The ancestors of the node are adjusted +to refer to the group instances instead of the original domain +elements. The return value points to the same node in the updated +schedule tree as the input node, i.e., to the child of the newly +introduced expansion node. Grouping instances of different statements +ensures that they will be treated as a single statement by the +AST generator up to the point of the expansion node. + +The following function can be used to flatten a nested +sequence. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_sequence_splice_child( + __isl_take isl_schedule_node *node, int pos); + +That is, given a sequence node C that has another sequence node +in its child at position C (in particular, the child of that filter +node is a sequence node), attach the children of that other sequence +node as children of C, replacing the original child at position +C. + +The partial schedule of a band node can be scaled (down) or reduced using +the following functions. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_band_scale( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_val *mv); + __isl_give isl_schedule_node * + isl_schedule_node_band_scale_down( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_val *mv); + __isl_give isl_schedule_node * + isl_schedule_node_band_mod( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_val *mv); + +The spaces of the two arguments need to match. +After scaling, the partial schedule is replaced by its greatest +integer part to ensure that the schedule remains integral. + +The partial schedule of a band node can be shifted by an +C with a domain that is a superset +of the domain of the partial schedule using +the following function. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_band_shift( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_union_pw_aff *shift); + +A band node can be tiled using the following function. + + #include + __isl_give isl_schedule_node *isl_schedule_node_band_tile( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_val *sizes); + + isl_stat isl_options_set_tile_scale_tile_loops(isl_ctx *ctx, + int val); + int isl_options_get_tile_scale_tile_loops(isl_ctx *ctx); + isl_stat isl_options_set_tile_shift_point_loops(isl_ctx *ctx, + int val); + int isl_options_get_tile_shift_point_loops(isl_ctx *ctx); + +The C function tiles +the band using the given tile sizes inside its schedule. +A new child band node is created to represent the point loops and it is +inserted between the modified band and its children. +The subtree rooted at the given node is assumed not to have +any anchored nodes. +The C option specifies whether the tile +loops iterators should be scaled by the tile sizes. +If the C option is set, then the point loops +are shifted to start at zero. + +A band node can be split into two nested band nodes +using the following function. + + #include + __isl_give isl_schedule_node *isl_schedule_node_band_split( + __isl_take isl_schedule_node *node, int pos); + +The resulting outer band node contains the first C dimensions of +the schedule of C while the inner band contains the remaining dimensions. +The schedules of the two band nodes live in anonymous spaces. +The loop AST generation type options and the isolate option +are split over the the two band nodes. + +A band node can be moved down to the leaves of the subtree rooted +at the band node using the following function. + + #include + __isl_give isl_schedule_node *isl_schedule_node_band_sink( + __isl_take isl_schedule_node *node); + +The subtree rooted at the given node is assumed not to have +any anchored nodes. +The result points to the node in the resulting tree that is in the same +position as the node pointed to by C in the original tree. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_order_before( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set *filter); + __isl_give isl_schedule_node * + isl_schedule_node_order_after( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set *filter); + +These functions split the domain elements that reach C +into those that satisfy C and those that do not and +arranges for the elements that do satisfy the filter to be +executed before (in case of C) +or after (in case of C) +those that do not. The order is imposed by +a sequence node, possibly reusing the grandparent of C +on two copies of the subtree attached to the original C. +Both copies are simplified with respect to their filter. + +Return a pointer to the copy of the subtree that does not +satisfy C. If there is no such copy (because all +reaching domain elements satisfy the filter), then return +the original pointer. + + #include + __isl_give isl_schedule_node * + isl_schedule_node_graft_before( + __isl_take isl_schedule_node *node, + __isl_take isl_schedule_node *graft); + __isl_give isl_schedule_node * + isl_schedule_node_graft_after( + __isl_take isl_schedule_node *node, + __isl_take isl_schedule_node *graft); + +This function inserts the C tree into the tree containing C +such that it is executed before (in case of C) +or after (in case of C) C. +The root node of C +should be an extension node where the domain of the extension +is the flat product of all outer band nodes of C. +The root node may also be a domain node. +The elements of the domain or the range of the extension may not +intersect with the domain elements that reach "node". +The schedule tree of C may not be anchored. + +The schedule tree of C is modified to include an extension node +corresponding to the root node of C as a child of the original +parent of C. The original node that C points to and the +child of the root node of C are attached to this extension node +through a sequence, with appropriate filters and with the child +of C appearing before or after the original C. + +If C already appears inside a sequence that is the child of +an extension node and if the spaces of the new domain elements +do not overlap with those of the original domain elements, +then that extension node is extended with the new extension +rather than introducing a new segment of extension and sequence nodes. + +Return a pointer to the same node in the modified tree that +C pointed to in the original tree. + +A representation of the schedule node can be printed using + + #include + __isl_give isl_printer *isl_printer_print_schedule_node( + __isl_take isl_printer *p, + __isl_keep isl_schedule_node *node); + __isl_give char *isl_schedule_node_to_str( + __isl_keep isl_schedule_node *node); + +C prints the schedule node in block format. + +=head2 Dependence Analysis + +C contains specialized functionality for performing +array dataflow analysis. That is, given a I access relation +and a collection of possible I access relations, +C can compute relations that describe +for each iteration of the sink access, which iteration +of which of the source access relations was the last +to access the same data element before the given iteration +of the sink access. +The resulting dependence relations map source iterations +to either the corresponding sink iterations or +pairs of corresponding sink iterations and accessed data elements. +To compute standard flow dependences, the sink should be +a read, while the sources should be writes. +If any of the source accesses are marked as being I +accesses, then there will be a dependence from the last +I access B from any I access that follows +this last I access. +In particular, if I sources are I accesses, +then memory based dependence analysis is performed. +If, on the other hand, all sources are I accesses, +then value based dependence analysis is performed. + +=head3 High-level Interface + +A high-level interface to dependence analysis is provided +by the following function. + + #include + __isl_give isl_union_flow * + isl_union_access_info_compute_flow( + __isl_take isl_union_access_info *access); + +The input C object describes the sink +access relations, the source access relations and a schedule, +while the output C object describes +the resulting dependence relations and the subsets of the +sink relations for which no source was found. + +An C is created, modified, copied and freed using +the following functions. + + #include + __isl_give isl_union_access_info * + isl_union_access_info_from_sink( + __isl_take isl_union_map *sink); + __isl_give isl_union_access_info * + isl_union_access_info_set_must_source( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *must_source); + __isl_give isl_union_access_info * + isl_union_access_info_set_may_source( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *may_source); + __isl_give isl_union_access_info * + isl_union_access_info_set_schedule( + __isl_take isl_union_access_info *access, + __isl_take isl_schedule *schedule); + __isl_give isl_union_access_info * + isl_union_access_info_set_schedule_map( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *schedule_map); + __isl_give isl_union_access_info * + isl_union_access_info_copy( + __isl_keep isl_union_access_info *access); + __isl_null isl_union_access_info * + isl_union_access_info_free( + __isl_take isl_union_access_info *access); + +The may sources set by C +do not need to include the must sources set by +C as a subset. +The user is free not to call one (or both) of these functions, +in which case the corresponding set is kept to its empty default. +Similarly, the default schedule initialized by +C is empty. +The current schedule is determined by the last call to either +C or +C. +The domain of the schedule corresponds to the domains of +the access relations. In particular, the domains of the access +relations are effectively intersected with the domain of the schedule +and only the resulting accesses are considered by the dependence analysis. + +A representation of the information contained in an object +of type C can be obtained using + + #include + __isl_give isl_printer * + isl_printer_print_union_access_info( + __isl_take isl_printer *p, + __isl_keep isl_union_access_info *access); + __isl_give char *isl_union_access_info_to_str( + __isl_keep isl_union_access_info *access); + +C prints the information in flow format. + +The output of C can be examined +and freed using the following functions. + + #include + __isl_give isl_union_map *isl_union_flow_get_must_dependence( + __isl_keep isl_union_flow *flow); + __isl_give isl_union_map *isl_union_flow_get_may_dependence( + __isl_keep isl_union_flow *flow); + __isl_give isl_union_map * + isl_union_flow_get_full_must_dependence( + __isl_keep isl_union_flow *flow); + __isl_give isl_union_map * + isl_union_flow_get_full_may_dependence( + __isl_keep isl_union_flow *flow); + __isl_give isl_union_map *isl_union_flow_get_must_no_source( + __isl_keep isl_union_flow *flow); + __isl_give isl_union_map *isl_union_flow_get_may_no_source( + __isl_keep isl_union_flow *flow); + __isl_null isl_union_flow *isl_union_flow_free( + __isl_take isl_union_flow *flow); + +The relation returned by C +relates domain elements of must sources to domain elements of the sink. +The relation returned by C +relates domain elements of must or may sources to domain elements of the sink +and includes the previous relation as a subset. +The relation returned by C +relates domain elements of must sources to pairs of domain elements of the sink +and accessed data elements. +The relation returned by C +relates domain elements of must or may sources to pairs of +domain elements of the sink and accessed data elements. +This relation includes the previous relation as a subset. +The relation returned by C is the subset +of the sink relation for which no dependences have been found. +The relation returned by C is the subset +of the sink relation for which no definite dependences have been found. +That is, it contains those sink access that do not contribute to any +of the elements in the relation returned +by C. + +A representation of the information contained in an object +of type C can be obtained using + + #include + __isl_give isl_printer *isl_printer_print_union_flow( + __isl_take isl_printer *p, + __isl_keep isl_union_flow *flow); + __isl_give char *isl_union_flow_to_str( + __isl_keep isl_union_flow *flow); + +C prints the information in flow format. + +=head3 Low-level Interface + +A lower-level interface is provided by the following functions. + + #include + + typedef int (*isl_access_level_before)(void *first, void *second); + + __isl_give isl_access_info *isl_access_info_alloc( + __isl_take isl_map *sink, + void *sink_user, isl_access_level_before fn, + int max_source); + __isl_give isl_access_info *isl_access_info_add_source( + __isl_take isl_access_info *acc, + __isl_take isl_map *source, int must, + void *source_user); + __isl_null isl_access_info *isl_access_info_free( + __isl_take isl_access_info *acc); + + __isl_give isl_flow *isl_access_info_compute_flow( + __isl_take isl_access_info *acc); + + isl_stat isl_flow_foreach(__isl_keep isl_flow *deps, + isl_stat (*fn)(__isl_take isl_map *dep, int must, + void *dep_user, void *user), + void *user); + __isl_give isl_map *isl_flow_get_no_source( + __isl_keep isl_flow *deps, int must); + void isl_flow_free(__isl_take isl_flow *deps); + +The function C performs the actual +dependence analysis. The other functions are used to construct +the input for this function or to read off the output. + +The input is collected in an C, which can +be created through a call to C. +The arguments to this functions are the sink access relation +C, a token C used to identify the sink +access to the user, a callback function for specifying the +relative order of source and sink accesses, and the number +of source access relations that will be added. +The callback function has type C. +The function is called with two user supplied tokens identifying +either a source or the sink and it should return the shared nesting +level and the relative order of the two accesses. +In particular, let I be the number of loops shared by +the two accesses. If C precedes C textually, +then the function should return I<2 * n + 1>; otherwise, +it should return I<2 * n>. +The sources can be added to the C by performing +(at most) C calls to C. +C indicates whether the source is a I access +or a I access. Note that a multi-valued access relation +should only be marked I if every iteration in the domain +of the relation accesses I elements in its image. +The C token is again used to identify +the source access. The range of the source access relation +C should have the same dimension as the range +of the sink access relation. +The C function should usually not be +called explicitly, because it is called implicitly by +C. + +The result of the dependence analysis is collected in an +C. There may be elements of +the sink access for which no preceding source access could be +found or for which all preceding sources are I accesses. +The relations containing these elements can be obtained through +calls to C, the first with C set +and the second with C unset. +In the case of standard flow dependence analysis, +with the sink a read and the sources I writes, +the first relation corresponds to the reads from uninitialized +array elements and the second relation is empty. +The actual flow dependences can be extracted using +C. This function will call the user-specified +callback function C for each B dependence between +a source and the sink. The callback function is called +with four arguments, the actual flow dependence relation +mapping source iterations to sink iterations, a boolean that +indicates whether it is a I or I dependence, a token +identifying the source and an additional C with value +equal to the third argument of the C call. +A dependence is marked I if it originates from a I +source and if it is not followed by any I sources. + +After finishing with an C, the user should call +C to free all associated memory. + +=head3 Interaction with the Low-level Interface + +During the dependence analysis, we frequently need to perform +the following operation. Given a relation between sink iterations +and potential source iterations from a particular source domain, +what is the last potential source iteration corresponding to each +sink iteration. It can sometimes be convenient to adjust +the set of potential source iterations before or after each such operation. +The prototypical example is fuzzy array dataflow analysis, +where we need to analyze if, based on data-dependent constraints, +the sink iteration can ever be executed without one or more of +the corresponding potential source iterations being executed. +If so, we can introduce extra parameters and select an unknown +but fixed source iteration from the potential source iterations. +To be able to perform such manipulations, C provides the following +function. + + #include + + typedef __isl_give isl_restriction *(*isl_access_restrict)( + __isl_keep isl_map *source_map, + __isl_keep isl_set *sink, void *source_user, + void *user); + __isl_give isl_access_info *isl_access_info_set_restrict( + __isl_take isl_access_info *acc, + isl_access_restrict fn, void *user); + +The function C should be called +before calling C and registers a callback function +that will be called any time C is about to compute the last +potential source. The first argument is the (reverse) proto-dependence, +mapping sink iterations to potential source iterations. +The second argument represents the sink iterations for which +we want to compute the last source iteration. +The third argument is the token corresponding to the source +and the final argument is the token passed to C. +The callback is expected to return a restriction on either the input or +the output of the operation computing the last potential source. +If the input needs to be restricted then restrictions are needed +for both the source and the sink iterations. The sink iterations +and the potential source iterations will be intersected with these sets. +If the output needs to be restricted then only a restriction on the source +iterations is required. +If any error occurs, the callback should return C. +An C object can be created, freed and inspected +using the following functions. + + #include + + __isl_give isl_restriction *isl_restriction_input( + __isl_take isl_set *source_restr, + __isl_take isl_set *sink_restr); + __isl_give isl_restriction *isl_restriction_output( + __isl_take isl_set *source_restr); + __isl_give isl_restriction *isl_restriction_none( + __isl_take isl_map *source_map); + __isl_give isl_restriction *isl_restriction_empty( + __isl_take isl_map *source_map); + __isl_null isl_restriction *isl_restriction_free( + __isl_take isl_restriction *restr); + +C and C are special +cases of C. C +is essentially equivalent to + + isl_restriction_input(isl_set_universe( + isl_space_range(isl_map_get_space(source_map))), + isl_set_universe( + isl_space_domain(isl_map_get_space(source_map)))); + +whereas C is essentially equivalent to + + isl_restriction_input(isl_set_empty( + isl_space_range(isl_map_get_space(source_map))), + isl_set_universe( + isl_space_domain(isl_map_get_space(source_map)))); + +=head2 Scheduling + + #include + __isl_give isl_schedule * + isl_schedule_constraints_compute_schedule( + __isl_take isl_schedule_constraints *sc); + +The function C can be +used to compute a schedule that satisfies the given schedule constraints. +These schedule constraints include the iteration domain for which +a schedule should be computed and dependences between pairs of +iterations. In particular, these dependences include +I dependences and I dependences. +By default, the algorithm used to construct the schedule is similar +to that of C. +Alternatively, Feautrier's multi-dimensional scheduling algorithm can +be selected. +The generated schedule respects all validity dependences. +That is, all dependence distances over these dependences in the +scheduled space are lexicographically positive. + +The default algorithm tries to ensure that the dependence distances +over coincidence constraints are zero and to minimize the +dependence distances over proximity dependences. +Moreover, it tries to obtain sequences (bands) of schedule dimensions +for groups of domains where the dependence distances over validity +dependences have only non-negative values. +Note that when minimizing the maximal dependence distance +over proximity dependences, a single affine expression in the parameters +is constructed that bounds all dependence distances. If no such expression +exists, then the algorithm will fail and resort to an alternative +scheduling algorithm. In particular, this means that adding proximity +dependences may eliminate valid solutions. A typical example where this +phenomenon may occur is when some subset of the proximity dependences +has no restriction on some parameter, forcing the coefficient of that +parameter to be zero, while some other subset forces the dependence +distance to depend on that parameter, requiring the same coefficient +to be non-zero. +When using Feautrier's algorithm, the coincidence and proximity constraints +are only taken into account during the extension to a +full-dimensional schedule. + +An C object can be constructed +and manipulated using the following functions. + + #include + __isl_give isl_schedule_constraints * + isl_schedule_constraints_copy( + __isl_keep isl_schedule_constraints *sc); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_on_domain( + __isl_take isl_union_set *domain); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_set_context( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_set *context); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_set_validity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *validity); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_set_coincidence( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *coincidence); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_set_proximity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *proximity); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_set_conditional_validity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *condition, + __isl_take isl_union_map *validity); + __isl_give isl_schedule_constraints * + isl_schedule_constraints_apply( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *umap); + __isl_null isl_schedule_constraints * + isl_schedule_constraints_free( + __isl_take isl_schedule_constraints *sc); + +The initial C object created by +C does not impose any constraints. +That is, it has an empty set of dependences. +The function C allows the user +to specify additional constraints on the parameters that may +be assumed to hold during the construction of the schedule. +The function C replaces the +validity dependences, mapping domain elements I to domain +elements that should be scheduled after I. +The function C replaces the +coincidence dependences, mapping domain elements I to domain +elements that should be scheduled together with I, if possible. +The function C replaces the +proximity dependences, mapping domain elements I to domain +elements that should be scheduled either before I +or as early as possible after I. + +The function C +replaces the conditional validity constraints. +A conditional validity constraint is only imposed when any of the corresponding +conditions is satisfied, i.e., when any of them is non-zero. +That is, the scheduler ensures that within each band if the dependence +distances over the condition constraints are not all zero +then all corresponding conditional validity constraints are respected. +A conditional validity constraint corresponds to a condition +if the two are adjacent, i.e., if the domain of one relation intersect +the range of the other relation. +The typical use case of conditional validity constraints is +to allow order constraints between live ranges to be violated +as long as the live ranges themselves are local to the band. +To allow more fine-grained control over which conditions correspond +to which conditional validity constraints, the domains and ranges +of these relations may include I. That is, the domains and +ranges of those relation may themselves be wrapped relations +where the iteration domain appears in the domain of those wrapped relations +and the range of the wrapped relations can be arbitrarily chosen +by the user. Conditions and conditional validity constraints are only +considered adjacent to each other if the entire wrapped relation matches. +In particular, a relation with a tag will never be considered adjacent +to a relation without a tag. + +The function C takes +schedule constraints that are defined on some set of domain elements +and transforms them to schedule constraints on the elements +to which these domain elements are mapped by the given transformation. + +An C object can be inspected +using the following functions. + + #include + __isl_give isl_union_set * + isl_schedule_constraints_get_domain( + __isl_keep isl_schedule_constraints *sc); + __isl_give isl_union_map * + isl_schedule_constraints_get_validity( + __isl_keep isl_schedule_constraints *sc); + __isl_give isl_union_map * + isl_schedule_constraints_get_coincidence( + __isl_keep isl_schedule_constraints *sc); + __isl_give isl_union_map * + isl_schedule_constraints_get_proximity( + __isl_keep isl_schedule_constraints *sc); + __isl_give isl_union_map * + isl_schedule_constraints_get_conditional_validity( + __isl_keep isl_schedule_constraints *sc); + __isl_give isl_union_map * + isl_schedule_constraints_get_conditional_validity_condition( + __isl_keep isl_schedule_constraints *sc); + +The following function computes a schedule directly from +an iteration domain and validity and proximity dependences +and is implemented in terms of the functions described above. +The use of C is discouraged. + + #include + __isl_give isl_schedule *isl_union_set_compute_schedule( + __isl_take isl_union_set *domain, + __isl_take isl_union_map *validity, + __isl_take isl_union_map *proximity); + +The generated schedule represents a schedule tree. +For more information on schedule trees, see +L. + +=head3 Options + + #include + isl_stat isl_options_set_schedule_max_coefficient( + isl_ctx *ctx, int val); + int isl_options_get_schedule_max_coefficient( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_max_constant_term( + isl_ctx *ctx, int val); + int isl_options_get_schedule_max_constant_term( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_serialize_sccs( + isl_ctx *ctx, int val); + int isl_options_get_schedule_serialize_sccs(isl_ctx *ctx); + isl_stat isl_options_set_schedule_whole_component( + isl_ctx *ctx, int val); + int isl_options_get_schedule_whole_component( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_maximize_band_depth( + isl_ctx *ctx, int val); + int isl_options_get_schedule_maximize_band_depth( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_maximize_coincidence( + isl_ctx *ctx, int val); + int isl_options_get_schedule_maximize_coincidence( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_outer_coincidence( + isl_ctx *ctx, int val); + int isl_options_get_schedule_outer_coincidence( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_split_scaled( + isl_ctx *ctx, int val); + int isl_options_get_schedule_split_scaled( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_treat_coalescing( + isl_ctx *ctx, int val); + int isl_options_get_schedule_treat_coalescing( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_algorithm( + isl_ctx *ctx, int val); + int isl_options_get_schedule_algorithm( + isl_ctx *ctx); + isl_stat isl_options_set_schedule_separate_components( + isl_ctx *ctx, int val); + int isl_options_get_schedule_separate_components( + isl_ctx *ctx); + +=over + +=item * schedule_max_coefficient + +This option enforces that the coefficients for variable and parameter +dimensions in the calculated schedule are not larger than the specified value. +This option can significantly increase the speed of the scheduling calculation +and may also prevent fusing of unrelated dimensions. A value of -1 means that +this option does not introduce bounds on the variable or parameter +coefficients. + +=item * schedule_max_constant_term + +This option enforces that the constant coefficients in the calculated schedule +are not larger than the maximal constant term. This option can significantly +increase the speed of the scheduling calculation and may also prevent fusing of +unrelated dimensions. A value of -1 means that this option does not introduce +bounds on the constant coefficients. + +=item * schedule_serialize_sccs + +If this option is set, then all strongly connected components +in the dependence graph are serialized as soon as they are detected. +This means in particular that instances of statements will only +appear in the same band node if these statements belong +to the same strongly connected component at the point where +the band node is constructed. + +=item * schedule_whole_component + +If this option is set, then entire (weakly) connected +components in the dependence graph are scheduled together +as a whole. +Otherwise, each strongly connected component within +such a weakly connected component is first scheduled separately +and then combined with other strongly connected components. +This option has no effect if C is set. + +=item * schedule_maximize_band_depth + +If this option is set, then the scheduler tries to maximize +the width of the bands. Wider bands give more possibilities for tiling. +In particular, if the C option is set, +then bands are split if this might result in wider bands. +Otherwise, the effect of this option is to only allow +strongly connected components to be combined if this does +not reduce the width of the bands. +Note that if the C options is set, then +the C option therefore has no effect. + +=item * schedule_maximize_coincidence + +This option is only effective if the C +option is turned off. +If the C option is set, then (clusters of) +strongly connected components are only combined with each other +if this does not reduce the number of coincident band members. + +=item * schedule_outer_coincidence + +If this option is set, then we try to construct schedules +where the outermost scheduling dimension in each band +satisfies the coincidence constraints. + +=item * schedule_algorithm + +Selects the scheduling algorithm to be used. +Available scheduling algorithms are C +and C. + +=item * schedule_split_scaled + +If this option is set, then we try to construct schedules in which the +constant term is split off from the linear part if the linear parts of +the scheduling rows for all nodes in the graphs have a common non-trivial +divisor. +The constant term is then placed in a separate band and the linear +part is reduced. +This option is only effective when the Feautrier style scheduler is +being used, either as the main scheduler or as a fallback for the +Pluto-like scheduler. + +=item * schedule_treat_coalescing + +If this option is set, then the scheduler will try and avoid +producing schedules that perform loop coalescing. +In particular, for the Pluto-like scheduler, this option places +bounds on the schedule coefficients based on the sizes of the instance sets. +For the Feautrier style scheduler, this option detects potentially +coalescing schedules and then tries to adjust the schedule to avoid +the coalescing. + +=item * schedule_separate_components + +If this option is set then the function C +will treat set nodes in the same way as sequence nodes. + +=back + +=head2 AST Generation + +This section describes the C functionality for generating +ASTs that visit all the elements +in a domain in an order specified by a schedule tree or +a schedule map. +In case the schedule given as a C, an AST is generated +that visits all the elements in the domain of the C +according to the lexicographic order of the corresponding image +element(s). If the range of the C consists of +elements in more than one space, then each of these spaces is handled +separately in an arbitrary order. +It should be noted that the schedule tree or the image elements +in a schedule map only specify the I +in which the corresponding domain elements should be visited. +No direct relation between the partial schedule values +or the image elements on the one hand and the loop iterators +in the generated AST on the other hand should be assumed. + +Each AST is generated within a build. The initial build +simply specifies the constraints on the parameters (if any) +and can be created, inspected, copied and freed using the following functions. + + #include + __isl_give isl_ast_build *isl_ast_build_alloc( + isl_ctx *ctx); + __isl_give isl_ast_build *isl_ast_build_from_context( + __isl_take isl_set *set); + __isl_give isl_ast_build *isl_ast_build_copy( + __isl_keep isl_ast_build *build); + __isl_null isl_ast_build *isl_ast_build_free( + __isl_take isl_ast_build *build); + +The C argument is usually a parameter set with zero or more parameters. +In fact, when creating an AST using C, +this set is required to be a parameter set. +An C created using C does not +specify any parameter constraints. +More C functions are described in L +and L. +Finally, the AST itself can be constructed using one of the following +functions. + + #include + __isl_give isl_ast_node *isl_ast_build_node_from_schedule( + __isl_keep isl_ast_build *build, + __isl_take isl_schedule *schedule); + __isl_give isl_ast_node * + isl_ast_build_node_from_schedule_map( + __isl_keep isl_ast_build *build, + __isl_take isl_union_map *schedule); + +=head3 Inspecting the AST + +The basic properties of an AST node can be obtained as follows. + + #include + enum isl_ast_node_type isl_ast_node_get_type( + __isl_keep isl_ast_node *node); + +The type of an AST node is one of +C, +C, +C, +C or +C. +An C represents a for node. +An C represents an if node. +An C represents a compound node. +An C introduces a mark in the AST. +An C represents an expression statement. +An expression statement typically corresponds to a domain element, i.e., +one of the elements that is visited by the AST. + +Each type of node has its own additional properties. + + #include + __isl_give isl_ast_expr *isl_ast_node_for_get_iterator( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_expr *isl_ast_node_for_get_init( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_expr *isl_ast_node_for_get_cond( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_expr *isl_ast_node_for_get_inc( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_node *isl_ast_node_for_get_body( + __isl_keep isl_ast_node *node); + isl_bool isl_ast_node_for_is_degenerate( + __isl_keep isl_ast_node *node); + +An C is considered degenerate if it is known to execute +exactly once. + + #include + __isl_give isl_ast_expr *isl_ast_node_if_get_cond( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_node *isl_ast_node_if_get_then( + __isl_keep isl_ast_node *node); + isl_bool isl_ast_node_if_has_else( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_node *isl_ast_node_if_get_else( + __isl_keep isl_ast_node *node); + + __isl_give isl_ast_node_list * + isl_ast_node_block_get_children( + __isl_keep isl_ast_node *node); + + __isl_give isl_id *isl_ast_node_mark_get_id( + __isl_keep isl_ast_node *node); + __isl_give isl_ast_node *isl_ast_node_mark_get_node( + __isl_keep isl_ast_node *node); + +C returns the identifier of the mark. +C returns the child node that is being marked. + + #include + __isl_give isl_ast_expr *isl_ast_node_user_get_expr( + __isl_keep isl_ast_node *node); + +All descendants of a specific node in the AST (including the node itself) +can be visited +in depth-first pre-order using the following function. + + #include + isl_stat isl_ast_node_foreach_descendant_top_down( + __isl_keep isl_ast_node *node, + isl_bool (*fn)(__isl_keep isl_ast_node *node, + void *user), void *user); + +The callback function should return C if the children +of the given node should be visited and C if they should not. +It should return C in case of failure, in which case +the entire traversal is aborted. + +Each of the returned Cs can in turn be inspected using +the following functions. + + #include + enum isl_ast_expr_type isl_ast_expr_get_type( + __isl_keep isl_ast_expr *expr); + +The type of an AST expression is one of +C, +C or +C. +An C represents the result of an operation. +An C represents an identifier. +An C represents an integer value. + +Each type of expression has its own additional properties. + + #include + enum isl_ast_op_type isl_ast_expr_get_op_type( + __isl_keep isl_ast_expr *expr); + int isl_ast_expr_get_op_n_arg(__isl_keep isl_ast_expr *expr); + __isl_give isl_ast_expr *isl_ast_expr_get_op_arg( + __isl_keep isl_ast_expr *expr, int pos); + isl_stat isl_ast_expr_foreach_ast_op_type( + __isl_keep isl_ast_expr *expr, + isl_stat (*fn)(enum isl_ast_op_type type, + void *user), void *user); + isl_stat isl_ast_node_foreach_ast_op_type( + __isl_keep isl_ast_node *node, + isl_stat (*fn)(enum isl_ast_op_type type, + void *user), void *user); + +C returns the type of the operation +performed. C returns the number of +arguments. C returns the specified +argument. +C calls C for each distinct +C that appears in C. +C does the same for each distinct +C that appears in C. +The operation type is one of the following. + +=over + +=item C + +Logical I of two arguments. +Both arguments can be evaluated. + +=item C + +Logical I of two arguments. +The second argument can only be evaluated if the first evaluates to true. + +=item C + +Logical I of two arguments. +Both arguments can be evaluated. + +=item C + +Logical I of two arguments. +The second argument can only be evaluated if the first evaluates to false. + +=item C + +Maximum of two or more arguments. + +=item C + +Minimum of two or more arguments. + +=item C + +Change sign. + +=item C + +Sum of two arguments. + +=item C + +Difference of two arguments. + +=item C + +Product of two arguments. + +=item C + +Exact division. That is, the result is known to be an integer. + +=item C + +Result of integer division, rounded towards negative +infinity. + +=item C + +Result of integer division, where dividend is known to be non-negative. + +=item C + +Remainder of integer division, where dividend is known to be non-negative. + +=item C + +Equal to zero iff the remainder on integer division is zero. + +=item C + +Conditional operator defined on three arguments. +If the first argument evaluates to true, then the result +is equal to the second argument. Otherwise, the result +is equal to the third argument. +The second and third argument may only be evaluated if +the first argument evaluates to true and false, respectively. +Corresponds to C in C. + +=item C + +Conditional operator defined on three arguments. +If the first argument evaluates to true, then the result +is equal to the second argument. Otherwise, the result +is equal to the third argument. +The second and third argument may be evaluated independently +of the value of the first argument. +Corresponds to C in C. + +=item C + +Equality relation. + +=item C + +Less than or equal relation. + +=item C + +Less than relation. + +=item C + +Greater than or equal relation. + +=item C + +Greater than relation. + +=item C + +A function call. +The number of arguments of the C is one more than +the number of arguments in the function call, the first argument +representing the function being called. + +=item C + +An array access. +The number of arguments of the C is one more than +the number of index expressions in the array access, the first argument +representing the array being accessed. + +=item C + +A member access. +This operation has two arguments, a structure and the name of +the member of the structure being accessed. + +=back + + #include + __isl_give isl_id *isl_ast_expr_get_id( + __isl_keep isl_ast_expr *expr); + +Return the identifier represented by the AST expression. + + #include + __isl_give isl_val *isl_ast_expr_get_val( + __isl_keep isl_ast_expr *expr); + +Return the integer represented by the AST expression. + +=head3 Properties of ASTs + + #include + isl_bool isl_ast_expr_is_equal( + __isl_keep isl_ast_expr *expr1, + __isl_keep isl_ast_expr *expr2); + +Check if two Cs are equal to each other. + +=head3 Manipulating and printing the AST + +AST nodes can be copied and freed using the following functions. + + #include + __isl_give isl_ast_node *isl_ast_node_copy( + __isl_keep isl_ast_node *node); + __isl_null isl_ast_node *isl_ast_node_free( + __isl_take isl_ast_node *node); + +AST expressions can be copied and freed using the following functions. + + #include + __isl_give isl_ast_expr *isl_ast_expr_copy( + __isl_keep isl_ast_expr *expr); + __isl_null isl_ast_expr *isl_ast_expr_free( + __isl_take isl_ast_expr *expr); + +New AST expressions can be created either directly or within +the context of an C. + + #include + __isl_give isl_ast_expr *isl_ast_expr_from_val( + __isl_take isl_val *v); + __isl_give isl_ast_expr *isl_ast_expr_from_id( + __isl_take isl_id *id); + __isl_give isl_ast_expr *isl_ast_expr_neg( + __isl_take isl_ast_expr *expr); + __isl_give isl_ast_expr *isl_ast_expr_address_of( + __isl_take isl_ast_expr *expr); + __isl_give isl_ast_expr *isl_ast_expr_add( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_sub( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_mul( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_div( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_pdiv_q( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_pdiv_r( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_and( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) + __isl_give isl_ast_expr *isl_ast_expr_and_then( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) + __isl_give isl_ast_expr *isl_ast_expr_or( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) + __isl_give isl_ast_expr *isl_ast_expr_or_else( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) + __isl_give isl_ast_expr *isl_ast_expr_eq( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_le( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_lt( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_ge( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_gt( + __isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); + __isl_give isl_ast_expr *isl_ast_expr_access( + __isl_take isl_ast_expr *array, + __isl_take isl_ast_expr_list *indices); + __isl_give isl_ast_expr *isl_ast_expr_call( + __isl_take isl_ast_expr *function, + __isl_take isl_ast_expr_list *arguments); + +The function C can be applied to an +C of type C only. It is meant +to represent the address of the C. The function +C as well as C are short-circuit +versions of C and C, respectively. + + #include + __isl_give isl_ast_expr *isl_ast_build_expr_from_set( + __isl_keep isl_ast_build *build, + __isl_take isl_set *set); + __isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff( + __isl_keep isl_ast_build *build, + __isl_take isl_pw_aff *pa); + __isl_give isl_ast_expr * + isl_ast_build_access_from_pw_multi_aff( + __isl_keep isl_ast_build *build, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_ast_expr * + isl_ast_build_access_from_multi_pw_aff( + __isl_keep isl_ast_build *build, + __isl_take isl_multi_pw_aff *mpa); + __isl_give isl_ast_expr * + isl_ast_build_call_from_pw_multi_aff( + __isl_keep isl_ast_build *build, + __isl_take isl_pw_multi_aff *pma); + __isl_give isl_ast_expr * + isl_ast_build_call_from_multi_pw_aff( + __isl_keep isl_ast_build *build, + __isl_take isl_multi_pw_aff *mpa); + +The set and +the domains of C, C and C should correspond +to the schedule space of C. +The tuple id of C or C is used as the array being accessed or +the function being called. +If the accessed space is a nested relation, then it is taken +to represent an access of the member specified by the range +of this nested relation of the structure specified by the domain +of the nested relation. + +The following functions can be used to modify an C. + + #include + __isl_give isl_ast_expr *isl_ast_expr_set_op_arg( + __isl_take isl_ast_expr *expr, int pos, + __isl_take isl_ast_expr *arg); + +Replace the argument of C at position C by C. + + #include + __isl_give isl_ast_expr *isl_ast_expr_substitute_ids( + __isl_take isl_ast_expr *expr, + __isl_take isl_id_to_ast_expr *id2expr); + +The function C replaces the +subexpressions of C of type C +by the corresponding expression in C, if there is any. + + +User specified data can be attached to an C and obtained +from the same C using the following functions. + + #include + __isl_give isl_ast_node *isl_ast_node_set_annotation( + __isl_take isl_ast_node *node, + __isl_take isl_id *annotation); + __isl_give isl_id *isl_ast_node_get_annotation( + __isl_keep isl_ast_node *node); + +Basic printing can be performed using the following functions. + + #include + __isl_give isl_printer *isl_printer_print_ast_expr( + __isl_take isl_printer *p, + __isl_keep isl_ast_expr *expr); + __isl_give isl_printer *isl_printer_print_ast_node( + __isl_take isl_printer *p, + __isl_keep isl_ast_node *node); + __isl_give char *isl_ast_expr_to_str( + __isl_keep isl_ast_expr *expr); + +More advanced printing can be performed using the following functions. + + #include + __isl_give isl_printer *isl_ast_op_type_set_print_name( + __isl_take isl_printer *p, + enum isl_ast_op_type type, + __isl_keep const char *name); + isl_stat isl_options_set_ast_print_macro_once( + isl_ctx *ctx, int val); + int isl_options_get_ast_print_macro_once(isl_ctx *ctx); + __isl_give isl_printer *isl_ast_op_type_print_macro( + enum isl_ast_op_type type, + __isl_take isl_printer *p); + __isl_give isl_printer *isl_ast_expr_print_macros( + __isl_keep isl_ast_expr *expr, + __isl_take isl_printer *p); + __isl_give isl_printer *isl_ast_node_print_macros( + __isl_keep isl_ast_node *node, + __isl_take isl_printer *p); + __isl_give isl_printer *isl_ast_node_print( + __isl_keep isl_ast_node *node, + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options); + __isl_give isl_printer *isl_ast_node_for_print( + __isl_keep isl_ast_node *node, + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options); + __isl_give isl_printer *isl_ast_node_if_print( + __isl_keep isl_ast_node *node, + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options); + +While printing an C in C, +C may print out an AST that makes use of macros such +as C, C and C. +The names of these macros may be modified by a call +to C. The user-specified +names are associated to the printer object. +C prints out the macro +corresponding to a specific C. +If the print-macro-once option is set, then a given macro definition +is only printed once to any given printer object. +C scans the C +for subexpressions where these macros would be used and prints +out the required macro definitions. +Essentially, C calls +C with C +as function argument. +C does the same +for expressions in its C argument. +C, C and +C print an C +in C, but allow for some extra control +through an C object. +This object can be created using the following functions. + + #include + __isl_give isl_ast_print_options * + isl_ast_print_options_alloc(isl_ctx *ctx); + __isl_give isl_ast_print_options * + isl_ast_print_options_copy( + __isl_keep isl_ast_print_options *options); + __isl_null isl_ast_print_options * + isl_ast_print_options_free( + __isl_take isl_ast_print_options *options); + + __isl_give isl_ast_print_options * + isl_ast_print_options_set_print_user( + __isl_take isl_ast_print_options *options, + __isl_give isl_printer *(*print_user)( + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user), + void *user); + __isl_give isl_ast_print_options * + isl_ast_print_options_set_print_for( + __isl_take isl_ast_print_options *options, + __isl_give isl_printer *(*print_for)( + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user), + void *user); + +The callback set by C +is called whenever a node of type C needs to +be printed. +The callback set by C +is called whenever a node of type C needs to +be printed. +Note that C will I call the +callback set by C on the node +on which C is called, but only on nested +nodes of type C. It is therefore safe to +call C from within the callback set by +C. + +The following option determines the type to be used for iterators +while printing the AST. + + isl_stat isl_options_set_ast_iterator_type( + isl_ctx *ctx, const char *val); + const char *isl_options_get_ast_iterator_type( + isl_ctx *ctx); + +The AST printer only prints body nodes as blocks if these +blocks cannot be safely omitted. +For example, a C node with one body node will not be +surrounded with braces in C. +A block will always be printed by setting the following option. + + isl_stat isl_options_set_ast_always_print_block(isl_ctx *ctx, + int val); + int isl_options_get_ast_always_print_block(isl_ctx *ctx); + +=head3 Options + + #include + isl_stat isl_options_set_ast_build_atomic_upper_bound( + isl_ctx *ctx, int val); + int isl_options_get_ast_build_atomic_upper_bound( + isl_ctx *ctx); + isl_stat isl_options_set_ast_build_prefer_pdiv(isl_ctx *ctx, + int val); + int isl_options_get_ast_build_prefer_pdiv(isl_ctx *ctx); + isl_stat isl_options_set_ast_build_detect_min_max( + isl_ctx *ctx, int val); + int isl_options_get_ast_build_detect_min_max( + isl_ctx *ctx); + isl_stat isl_options_set_ast_build_exploit_nested_bounds( + isl_ctx *ctx, int val); + int isl_options_get_ast_build_exploit_nested_bounds( + isl_ctx *ctx); + isl_stat isl_options_set_ast_build_group_coscheduled( + isl_ctx *ctx, int val); + int isl_options_get_ast_build_group_coscheduled( + isl_ctx *ctx); + isl_stat isl_options_set_ast_build_scale_strides( + isl_ctx *ctx, int val); + int isl_options_get_ast_build_scale_strides( + isl_ctx *ctx); + isl_stat isl_options_set_ast_build_allow_else(isl_ctx *ctx, + int val); + int isl_options_get_ast_build_allow_else(isl_ctx *ctx); + isl_stat isl_options_set_ast_build_allow_or(isl_ctx *ctx, + int val); + int isl_options_get_ast_build_allow_or(isl_ctx *ctx); + +=over + +=item * ast_build_atomic_upper_bound + +Generate loop upper bounds that consist of the current loop iterator, +an operator and an expression not involving the iterator. +If this option is not set, then the current loop iterator may appear +several times in the upper bound. +For example, when this option is turned off, AST generation +for the schedule + + [n] -> { A[i] -> [i] : 0 <= i <= 100, n } + +produces + + for (int c0 = 0; c0 <= 100 && n >= c0; c0 += 1) + A(c0); + +When the option is turned on, the following AST is generated + + for (int c0 = 0; c0 <= min(100, n); c0 += 1) + A(c0); + +=item * ast_build_prefer_pdiv + +If this option is turned off, then the AST generation will +produce ASTs that may only contain C +operators, but no C or +C operators. +If this option is turned on, then C will try to convert +some of the C operators to (expressions containing) +C or C operators. + +=item * ast_build_detect_min_max + +If this option is turned on, then C will try and detect +min or max-expressions when building AST expressions from +piecewise affine expressions. + +=item * ast_build_exploit_nested_bounds + +Simplify conditions based on bounds of nested for loops. +In particular, remove conditions that are implied by the fact +that one or more nested loops have at least one iteration, +meaning that the upper bound is at least as large as the lower bound. +For example, when this option is turned off, AST generation +for the schedule + + [N,M] -> { A[i,j] -> [i,j] : 0 <= i <= N and + 0 <= j <= M } + +produces + + if (M >= 0) + for (int c0 = 0; c0 <= N; c0 += 1) + for (int c1 = 0; c1 <= M; c1 += 1) + A(c0, c1); + +When the option is turned on, the following AST is generated + + for (int c0 = 0; c0 <= N; c0 += 1) + for (int c1 = 0; c1 <= M; c1 += 1) + A(c0, c1); + +=item * ast_build_group_coscheduled + +If two domain elements are assigned the same schedule point, then +they may be executed in any order and they may even appear in different +loops. If this options is set, then the AST generator will make +sure that coscheduled domain elements do not appear in separate parts +of the AST. This is useful in case of nested AST generation +if the outer AST generation is given only part of a schedule +and the inner AST generation should handle the domains that are +coscheduled by this initial part of the schedule together. +For example if an AST is generated for a schedule + + { A[i] -> [0]; B[i] -> [0] } + +then the C callback described +below may get called twice, once for each domain. +Setting this option ensures that the callback is only called once +on both domains together. + +=item * ast_build_separation_bounds + +This option specifies which bounds to use during separation. +If this option is set to C +then all (possibly implicit) bounds on the current dimension will +be used during separation. +If this option is set to C +then only those bounds that are explicitly available will +be used during separation. + +=item * ast_build_scale_strides + +This option specifies whether the AST generator is allowed +to scale down iterators of strided loops. + +=item * ast_build_allow_else + +This option specifies whether the AST generator is allowed +to construct if statements with else branches. + +=item * ast_build_allow_or + +This option specifies whether the AST generator is allowed +to construct if conditions with disjunctions. + +=back + +=head3 AST Generation Options (Schedule Tree) + +In case of AST construction from a schedule tree, the options +that control how an AST is created from the individual schedule +dimensions are stored in the band nodes of the tree +(see L). + +In particular, a schedule dimension can be handled in four +different ways, atomic, separate, unroll or the default. +This loop AST generation type can be set using +C. +Alternatively, +the first three can be selected by including a one-dimensional +element with as value the position of the schedule dimension +within the band and as name one of C, C +or C in the options +set by C. +Only one of these three may be specified for +any given schedule dimension within a band node. +If none of these is specified, then the default +is used. The meaning of the options is as follows. + +=over + +=item C + +When this option is specified, the AST generator will make +sure that a given domains space only appears in a single +loop at the specified level. + +For example, for the schedule tree + + domain: "{ a[i] : 0 <= i < 10; b[i] : 0 <= i < 10 }" + child: + schedule: "[{ a[i] -> [i]; b[i] -> [i+1] }]" + options: "{ atomic[x] }" + +the following AST will be generated + + for (int c0 = 0; c0 <= 10; c0 += 1) { + if (c0 >= 1) + b(c0 - 1); + if (c0 <= 9) + a(c0); + } + +On the other hand, for the schedule tree + + domain: "{ a[i] : 0 <= i < 10; b[i] : 0 <= i < 10 }" + child: + schedule: "[{ a[i] -> [i]; b[i] -> [i+1] }]" + options: "{ separate[x] }" + +the following AST will be generated + + { + a(0); + for (int c0 = 1; c0 <= 9; c0 += 1) { + b(c0 - 1); + a(c0); + } + b(9); + } + +If neither C nor C is specified, then the AST generator +may produce either of these two results or some intermediate form. + +=item C + +When this option is specified, the AST generator will +split the domain of the specified schedule dimension +into pieces with a fixed set of statements for which +instances need to be executed by the iterations in +the schedule domain part. This option tends to avoid +the generation of guards inside the corresponding loops. +See also the C option. + +=item C + +When this option is specified, the AST generator will +I unroll the corresponding schedule dimension. +It is the responsibility of the user to ensure that such +unrolling is possible. +To obtain a partial unrolling, the user should apply an additional +strip-mining to the schedule and fully unroll the inner schedule +dimension. + +=back + +The C option is a bit more involved. It allows the user +to isolate a range of schedule dimension values from smaller and +greater values. Additionally, the user may specify a different +atomic/separate/unroll choice for the isolated part and the remaining +parts. The typical use case of the C option is to isolate +full tiles from partial tiles. +The part that needs to be isolated may depend on outer schedule dimensions. +The option therefore needs to be able to reference those outer schedule +dimensions. In particular, the space of the C option is that +of a wrapped map with as domain the flat product of all outer band nodes +and as range the space of the current band node. +The atomic/separate/unroll choice for the isolated part is determined +by an option that lives in an unnamed wrapped space with as domain +a zero-dimensional C space and as range the regular +C, C or C space. +This option may also be set directly using +C. +The atomic/separate/unroll choice for the remaining part is determined +by the regular C, C or C option. +The use of the C option causes any tree containing the node +to be considered anchored. + +As an example, consider the isolation of full tiles from partial tiles +in a tiling of a triangular domain. The original schedule is as follows. + + domain: "{ A[i,j] : 0 <= i,j and i + j <= 100 }" + child: + schedule: "[{ A[i,j] -> [floor(i/10)] }, \ + { A[i,j] -> [floor(j/10)] }, \ + { A[i,j] -> [i] }, { A[i,j] -> [j] }]" + +The output is + + for (int c0 = 0; c0 <= 10; c0 += 1) + for (int c1 = 0; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(10 * c0 + 9, -10 * c1 + 100); c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); c3 += 1) + A(c2, c3); + +Isolating the full tiles, we have the following input + + domain: "{ A[i,j] : 0 <= i,j and i + j <= 100 }" + child: + schedule: "[{ A[i,j] -> [floor(i/10)] }, \ + { A[i,j] -> [floor(j/10)] }, \ + { A[i,j] -> [i] }, { A[i,j] -> [j] }]" + options: "{ isolate[[] -> [a,b,c,d]] : 0 <= 10a,10b and \ + 10a+9+10b+9 <= 100 }" + +and output + + { + for (int c0 = 0; c0 <= 8; c0 += 1) { + for (int c1 = 0; c1 <= -c0 + 8; c1 += 1) + for (int c2 = 10 * c0; + c2 <= 10 * c0 + 9; c2 += 1) + for (int c3 = 10 * c1; + c3 <= 10 * c1 + 9; c3 += 1) + A(c2, c3); + for (int c1 = -c0 + 9; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(10 * c0 + 9, -10 * c1 + 100); c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); c3 += 1) + A(c2, c3); + } + for (int c0 = 9; c0 <= 10; c0 += 1) + for (int c1 = 0; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(10 * c0 + 9, -10 * c1 + 100); c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); c3 += 1) + A(c2, c3); + } + +We may then additionally unroll the innermost loop of the isolated part + + domain: "{ A[i,j] : 0 <= i,j and i + j <= 100 }" + child: + schedule: "[{ A[i,j] -> [floor(i/10)] }, \ + { A[i,j] -> [floor(j/10)] }, \ + { A[i,j] -> [i] }, { A[i,j] -> [j] }]" + options: "{ isolate[[] -> [a,b,c,d]] : 0 <= 10a,10b and \ + 10a+9+10b+9 <= 100; [isolate[] -> unroll[3]] }" + +to obtain + + { + for (int c0 = 0; c0 <= 8; c0 += 1) { + for (int c1 = 0; c1 <= -c0 + 8; c1 += 1) + for (int c2 = 10 * c0; c2 <= 10 * c0 + 9; c2 += 1) { + A(c2, 10 * c1); + A(c2, 10 * c1 + 1); + A(c2, 10 * c1 + 2); + A(c2, 10 * c1 + 3); + A(c2, 10 * c1 + 4); + A(c2, 10 * c1 + 5); + A(c2, 10 * c1 + 6); + A(c2, 10 * c1 + 7); + A(c2, 10 * c1 + 8); + A(c2, 10 * c1 + 9); + } + for (int c1 = -c0 + 9; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(10 * c0 + 9, -10 * c1 + 100); c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); c3 += 1) + A(c2, c3); + } + for (int c0 = 9; c0 <= 10; c0 += 1) + for (int c1 = 0; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(10 * c0 + 9, -10 * c1 + 100); c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); c3 += 1) + A(c2, c3); + } + + +=head3 AST Generation Options (Schedule Map) + +In case of AST construction using +C, the options +that control how an AST is created from the individual schedule +dimensions are stored in the C. +They can be set using the following function. + + #include + __isl_give isl_ast_build * + isl_ast_build_set_options( + __isl_take isl_ast_build *control, + __isl_take isl_union_map *options); + +The options are encoded in an C. +The domain of this union relation refers to the schedule domain, +i.e., the range of the schedule passed +to C. +In the case of nested AST generation (see L), +the domain of C should refer to the extra piece of the schedule. +That is, it should be equal to the range of the wrapped relation in the +range of the schedule. +The range of the options can consist of elements in one or more spaces, +the names of which determine the effect of the option. +The values of the range typically also refer to the schedule dimension +to which the option applies. In case of nested AST generation +(see L), these values refer to the position +of the schedule dimension within the innermost AST generation. +The constraints on the domain elements of +the option should only refer to this dimension and earlier dimensions. +We consider the following spaces. + +=over + +=item C + +B + +This space is a wrapped relation between two one dimensional spaces. +The input space represents the schedule dimension to which the option +applies and the output space represents the separation class. +While constructing a loop corresponding to the specified schedule +dimension(s), the AST generator will try to generate separate loops +for domain elements that are assigned different classes. +If only some of the elements are assigned a class, then those elements +that are not assigned any class will be treated as belonging to a class +that is separate from the explicitly assigned classes. +The typical use case for this option is to separate full tiles from +partial tiles. +The other options, described below, are applied after the separation +into classes. + +As an example, consider the separation into full and partial tiles +of a tiling of a triangular domain. +Take, for example, the domain + + { A[i,j] : 0 <= i,j and i + j <= 100 } + +and a tiling into tiles of 10 by 10. The input to the AST generator +is then the schedule + + { A[i,j] -> [([i/10]),[j/10],i,j] : 0 <= i,j and + i + j <= 100 } + +Without any options, the following AST is generated + + for (int c0 = 0; c0 <= 10; c0 += 1) + for (int c1 = 0; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(-10 * c1 + 100, 10 * c0 + 9); + c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); + c3 += 1) + A(c2, c3); + +Separation into full and partial tiles can be obtained by assigning +a class, say C<0>, to the full tiles. The full tiles are represented by those +values of the first and second schedule dimensions for which there are +values of the third and fourth dimensions to cover an entire tile. +That is, we need to specify the following option + + { [a,b,c,d] -> separation_class[[0]->[0]] : + exists b': 0 <= 10a,10b' and + 10a+9+10b'+9 <= 100; + [a,b,c,d] -> separation_class[[1]->[0]] : + 0 <= 10a,10b and 10a+9+10b+9 <= 100 } + +which simplifies to + + { [a, b, c, d] -> separation_class[[1] -> [0]] : + a >= 0 and b >= 0 and b <= 8 - a; + [a, b, c, d] -> separation_class[[0] -> [0]] : + a >= 0 and a <= 8 } + +With this option, the generated AST is as follows + + { + for (int c0 = 0; c0 <= 8; c0 += 1) { + for (int c1 = 0; c1 <= -c0 + 8; c1 += 1) + for (int c2 = 10 * c0; + c2 <= 10 * c0 + 9; c2 += 1) + for (int c3 = 10 * c1; + c3 <= 10 * c1 + 9; c3 += 1) + A(c2, c3); + for (int c1 = -c0 + 9; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(-10 * c1 + 100, 10 * c0 + 9); + c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(-c2 + 100, 10 * c1 + 9); + c3 += 1) + A(c2, c3); + } + for (int c0 = 9; c0 <= 10; c0 += 1) + for (int c1 = 0; c1 <= -c0 + 10; c1 += 1) + for (int c2 = 10 * c0; + c2 <= min(-10 * c1 + 100, 10 * c0 + 9); + c2 += 1) + for (int c3 = 10 * c1; + c3 <= min(10 * c1 + 9, -c2 + 100); + c3 += 1) + A(c2, c3); + } + +=item C + +This is a single-dimensional space representing the schedule dimension(s) +to which ``separation'' should be applied. Separation tries to split +a loop into several pieces if this can avoid the generation of guards +inside the loop. +See also the C option. + +=item C + +This is a single-dimensional space representing the schedule dimension(s) +for which the domains should be considered ``atomic''. That is, the +AST generator will make sure that any given domain space will only appear +in a single loop at the specified level. + +Consider the following schedule + + { a[i] -> [i] : 0 <= i < 10; + b[i] -> [i+1] : 0 <= i < 10 } + +If the following option is specified + + { [i] -> separate[x] } + +then the following AST will be generated + + { + a(0); + for (int c0 = 1; c0 <= 9; c0 += 1) { + a(c0); + b(c0 - 1); + } + b(9); + } + +If, on the other hand, the following option is specified + + { [i] -> atomic[x] } + +then the following AST will be generated + + for (int c0 = 0; c0 <= 10; c0 += 1) { + if (c0 <= 9) + a(c0); + if (c0 >= 1) + b(c0 - 1); + } + +If neither C nor C is specified, then the AST generator +may produce either of these two results or some intermediate form. + +=item C + +This is a single-dimensional space representing the schedule dimension(s) +that should be I unrolled. +To obtain a partial unrolling, the user should apply an additional +strip-mining to the schedule and fully unroll the inner loop. + +=back + +=head3 Fine-grained Control over AST Generation + +Besides specifying the constraints on the parameters, +an C object can be used to control +various aspects of the AST generation process. +In case of AST construction using +C, +the most prominent way of control is through ``options'', +as explained above. + +Additional control is available through the following functions. + + #include + __isl_give isl_ast_build * + isl_ast_build_set_iterators( + __isl_take isl_ast_build *control, + __isl_take isl_id_list *iterators); + +The function C allows the user to +specify a list of iterator Cs to be used as iterators. +If the input schedule is injective, then +the number of elements in this list should be as large as the dimension +of the schedule space, but no direct correspondence should be assumed +between dimensions and elements. +If the input schedule is not injective, then an additional number +of Cs equal to the largest dimension of the input domains +may be required. +If the number of provided Cs is insufficient, then additional +names are automatically generated. + + #include + __isl_give isl_ast_build * + isl_ast_build_set_create_leaf( + __isl_take isl_ast_build *control, + __isl_give isl_ast_node *(*fn)( + __isl_take isl_ast_build *build, + void *user), void *user); + +The +C function allows for the +specification of a callback that should be called whenever the AST +generator arrives at an element of the schedule domain. +The callback should return an AST node that should be inserted +at the corresponding position of the AST. The default action (when +the callback is not set) is to continue generating parts of the AST to scan +all the domain elements associated to the schedule domain element +and to insert user nodes, ``calling'' the domain element, for each of them. +The C argument contains the current state of the C. +To ease nested AST generation (see L), +all control information that is +specific to the current AST generation such as the options and +the callbacks has been removed from this C. +The callback would typically return the result of a nested +AST generation or a +user defined node created using the following function. + + #include + __isl_give isl_ast_node *isl_ast_node_alloc_user( + __isl_take isl_ast_expr *expr); + + #include + __isl_give isl_ast_build * + isl_ast_build_set_at_each_domain( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)( + __isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, + void *user), void *user); + __isl_give isl_ast_build * + isl_ast_build_set_before_each_for( + __isl_take isl_ast_build *build, + __isl_give isl_id *(*fn)( + __isl_keep isl_ast_build *build, + void *user), void *user); + __isl_give isl_ast_build * + isl_ast_build_set_after_each_for( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)( + __isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, + void *user), void *user); + __isl_give isl_ast_build * + isl_ast_build_set_before_each_mark( + __isl_take isl_ast_build *build, + isl_stat (*fn)(__isl_keep isl_id *mark, + __isl_keep isl_ast_build *build, + void *user), void *user); + __isl_give isl_ast_build * + isl_ast_build_set_after_each_mark( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)( + __isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, + void *user), void *user); + +The callback set by C will +be called for each domain AST node. +The callbacks set by C +and C will be called +for each for AST node. The first will be called in depth-first +pre-order, while the second will be called in depth-first post-order. +Since C is called before the for +node is actually constructed, it is only passed an C. +The returned C will be added as an annotation (using +C) to the constructed for node. +In particular, if the user has also specified an C +callback, then the annotation can be retrieved from the node passed to +that callback using C. +The callbacks set by C +and C will be called for each +mark AST node that is created, i.e., for each mark schedule node +in the input schedule tree. The first will be called in depth-first +pre-order, while the second will be called in depth-first post-order. +Since the callback set by C +is called before the mark AST node is actually constructed, it is passed +the identifier of the mark node. +All callbacks should C (or -1) on failure. +The given C can be used to create new +C objects using C +or C. + +=head3 Nested AST Generation + +C allows the user to create an AST within the context +of another AST. These nested ASTs are created using the +same C function that is used to create +the outer AST. The C argument should be an C +passed to a callback set by +C. +The space of the range of the C argument should refer +to this build. In particular, the space should be a wrapped +relation and the domain of this wrapped relation should be the +same as that of the range of the schedule returned by +C below. +In practice, the new schedule is typically +created by calling C on the old schedule +and some extra piece of the schedule. +The space of the schedule domain is also available from +the C. + + #include + __isl_give isl_union_map *isl_ast_build_get_schedule( + __isl_keep isl_ast_build *build); + __isl_give isl_space *isl_ast_build_get_schedule_space( + __isl_keep isl_ast_build *build); + __isl_give isl_ast_build *isl_ast_build_restrict( + __isl_take isl_ast_build *build, + __isl_take isl_set *set); + +The C function returns a (partial) +schedule for the domains elements for which part of the AST still needs to +be generated in the current build. +In particular, the domain elements are mapped to those iterations of the loops +enclosing the current point of the AST generation inside which +the domain elements are executed. +No direct correspondence between +the input schedule and this schedule should be assumed. +The space obtained from C can be used +to create a set for C to intersect +with the current build. In particular, the set passed to +C can have additional parameters. +The ids of the set dimensions in the space returned by +C correspond to the +iterators of the already generated loops. +The user should not rely on the ids of the output dimensions +of the relations in the union relation returned by +C having any particular value. + +=head1 Applications + +Although C is mainly meant to be used as a library, +it also contains some basic applications that use some +of the functionality of C. +The input may be specified in either the L +or the L. + +=head2 C + +C takes a polyhedron as input and prints +an integer element of the polyhedron, if there is any. +The first column in the output is the denominator and is always +equal to 1. If the polyhedron contains no integer points, +then a vector of length zero is printed. + +=head2 C + +C takes the same input as the C program +from the C distribution, i.e., a set of constraints +on the parameters, a line containing only -1 and finally a set +of constraints on a parametric polyhedron. +The coefficients of the parameters appear in the last columns +(but before the final constant column). +The output is the lexicographic minimum of the parametric polyhedron. +As C currently does not have its own output format, the output +is just a dump of the internal state. + +=head2 C + +C computes the minimum of some linear +or affine objective function over the integer points in a polyhedron. +If an affine objective function +is given, then the constant should appear in the last column. + +=head2 C + +Given a polytope, C prints +all integer points in the polytope. + +=head2 C + +Given a schedule, a context set and an options relation, +C prints out an AST that scans the domain elements +of the schedule in the order of their image(s) taking into account +the constraints in the context set. Index: lib/Analysis/isl/imath/gmp_compat.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath/gmp_compat.h @@ -0,0 +1,229 @@ +/* + Name: gmp_compat.h + Purpose: Provide GMP compatiable routines for imath library + Author: David Peixotto + + Copyright (c) 2012 Qualcomm Innovation Center, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#ifndef IMATH_GMP_COMPAT_H_ +#define IMATH_GMP_COMPAT_H_ +#include "imath.h" +#include "imrat.h" +#include + +#define GMPZAPI(fun) impz_ ## fun +#define GMPQAPI(fun) impq_ ## fun + +#ifdef __cplusplus +extern "C" { +#endif +/************************************************************************* + * + * Functions with direct translations + * + *************************************************************************/ +/* gmp: mpq_clear */ +void GMPQAPI(clear)(mp_rat x); + +/* gmp: mpq_cmp */ +int GMPQAPI(cmp)(mp_rat op1, mp_rat op2); + +/* gmp: mpq_init */ +void GMPQAPI(init)(mp_rat x); + +/* gmp: mpq_mul */ +void GMPQAPI(mul)(mp_rat product, mp_rat multiplier, mp_rat multiplicand); + +/* gmp: mpq_set */ +void GMPQAPI(set)(mp_rat rop, mp_rat op); + +/* gmp: mpz_abs */ +void GMPZAPI(abs)(mp_int rop, mp_int op); + +/* gmp: mpz_add */ +void GMPZAPI(add)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_clear */ +void GMPZAPI(clear)(mp_int x); + +/* gmp: mpz_cmp_si */ +int GMPZAPI(cmp_si)(mp_int op1, long op2); + +/* gmp: mpz_cmpabs */ +int GMPZAPI(cmpabs)(mp_int op1, mp_int op2); + +/* gmp: mpz_cmp */ +int GMPZAPI(cmp)(mp_int op1, mp_int op2); + +/* gmp: mpz_init */ +void GMPZAPI(init)(mp_int x); + +/* gmp: mpz_mul */ +void GMPZAPI(mul)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_neg */ +void GMPZAPI(neg)(mp_int rop, mp_int op); + +/* gmp: mpz_set_si */ +void GMPZAPI(set_si)(mp_int rop, long op); + +/* gmp: mpz_set */ +void GMPZAPI(set)(mp_int rop, mp_int op); + +/* gmp: mpz_sub */ +void GMPZAPI(sub)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_swap */ +void GMPZAPI(swap)(mp_int rop1, mp_int rop2); + +/* gmp: mpq_sgn */ +int GMPQAPI(sgn)(mp_rat op); + +/* gmp: mpz_sgn */ +int GMPZAPI(sgn)(mp_int op); + +/* gmp: mpq_set_ui */ +void GMPQAPI(set_ui)(mp_rat rop, unsigned long op1, unsigned long op2); + +/* gmp: mpz_set_ui */ +void GMPZAPI(set_ui)(mp_int rop, unsigned long op); + +/* gmp: mpq_den_ref */ +mp_int GMPQAPI(denref)(mp_rat op); + +/* gmp: mpq_num_ref */ +mp_int GMPQAPI(numref)(mp_rat op); + +/* gmp: mpq_canonicalize */ +void GMPQAPI(canonicalize)(mp_rat op); + +/************************************************************************* + * + * Functions that can be implemented as a combination of imath functions + * + *************************************************************************/ +/* gmp: mpz_addmul */ +void GMPZAPI(addmul)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_divexact */ +void GMPZAPI(divexact)(mp_int q, mp_int n, mp_int d); + +/* gmp: mpz_divisible_p */ +int GMPZAPI(divisible_p)(mp_int n, mp_int d); + +/* gmp: mpz_submul */ +void GMPZAPI(submul)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_add_ui */ +void GMPZAPI(add_ui)(mp_int rop, mp_int op1, unsigned long op2); + +/* gmp: mpz_divexact_ui */ +void GMPZAPI(divexact_ui)(mp_int q, mp_int n, unsigned long d); + +/* gmp: mpz_mul_ui */ +void GMPZAPI(mul_ui)(mp_int rop, mp_int op1, unsigned long op2); + +/* gmp: mpz_pow_ui */ +void GMPZAPI(pow_ui)(mp_int rop, mp_int base, unsigned long exp); + +/* gmp: mpz_sub_ui */ +void GMPZAPI(sub_ui)(mp_int rop, mp_int op1, unsigned long op2); + +/* gmp: mpz_fdiv_q_ui */ +unsigned long GMPZAPI(fdiv_q_ui)(mp_int q, mp_int n, unsigned long d); + +/* gmp: mpz_sizeinbase */ +size_t GMPZAPI(sizeinbase)(mp_int op, int base); + +/************************************************************************* + * + * Functions with different behavior in corner cases + * + *************************************************************************/ +/* gmp: mpz_gcd */ +/* gmp: When op1 = 0 and op2 = 0, return 0.*/ +void GMPZAPI(gcd)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_get_str */ +/* gmp: If str is NULL then allocate space using the default allocator. */ +char* GMPZAPI(get_str)(char *str, int radix, mp_int op); + +/* gmp: mpq_get_str */ +/* gmp: If str is NULL then allocate space using the default allocator. */ +/* gmp: If value is a whole number do not print denomenator. */ +/* TODO: Need to handle 0 values better. GMP prints 0/4 instead of 0.*/ +char* GMPQAPI(get_str)(char *str, int radix, mp_rat op); + +/* gmp: mpz_set_str */ +/* gmp: Allow and ignore spaces in string. */ +int GMPZAPI(set_str)(mp_int rop, char *str, int base); + +/* gmp: mpq_set_str */ +int GMPQAPI(set_str)(mp_rat rop, char *str, int base); + +/* gmp: mpz_get_ui */ +/* gmp: Return least significant bits if value is too big for a long. */ +unsigned long GMPZAPI(get_ui)(mp_int op); + +/* gmp: mpz_get_si */ +/* gmp: Return least significant bits if value is too bit for a long. */ +/* gmp: If value is too big for long, return the least significant + (8*sizeof(long)-1) bits from the op and set the sign bit according to + the sign of the op. */ +long GMPZAPI(get_si)(mp_int op); + +/* gmp: mpz_lcm */ +/* gmp: When op1 = 0 or op2 = 0, return 0.*/ +/* gmp: The resutl of lcm(a,b) is always positive. */ +void GMPZAPI(lcm)(mp_int rop, mp_int op1, mp_int op2); + +/* gmp: mpz_mul_2exp */ +/* gmp: allow big values for op2 when op1 == 0 */ +void GMPZAPI(mul_2exp)(mp_int rop, mp_int op1, unsigned long op2); + +/************************************************************************* + * + * Functions needing expanded functionality + * + *************************************************************************/ +/* gmp: mpz_cdiv_q */ +void GMPZAPI(cdiv_q)(mp_int q, mp_int n, mp_int d); + +/* gmp: mpz_fdiv_q */ +void GMPZAPI(fdiv_q)(mp_int q, mp_int n, mp_int d); + +/* gmp: mpz_fdiv_r */ +void GMPZAPI(fdiv_r)(mp_int r, mp_int n, mp_int d); + +/* gmp: mpz_tdiv_q */ +void GMPZAPI(tdiv_q)(mp_int q, mp_int n, mp_int d); + +/* gmp: mpz_export */ +void* GMPZAPI(export)(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int op); + +/* gmp: mpz_import */ +void GMPZAPI(import)(mp_int rop, size_t count, int order, size_t size, int endian, size_t nails, const void* op); + +#ifdef __cplusplus +} +#endif +#endif /* end IMATH_GMP_COMPAT_H_ */ Index: lib/Analysis/isl/imath/gmp_compat.c =================================================================== --- /dev/null +++ lib/Analysis/isl/imath/gmp_compat.c @@ -0,0 +1,862 @@ +/* + Name: gmp_compat.c + Purpose: Provide GMP compatiable routines for imath library + Author: David Peixotto + + Copyright (c) 2012 Qualcomm Innovation Center, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ +#include "gmp_compat.h" +#include +#include +#include +#include + +#ifdef NDEBUG +#define CHECK(res) (res) +#else +#define CHECK(res) assert(((res) == MP_OK) && "expected MP_OK") +#endif + +/************************************************************************* + * + * Functions with direct translations + * + *************************************************************************/ +/* gmp: mpq_clear */ +void GMPQAPI(clear)(mp_rat x) { + mp_rat_clear(x); +} + +/* gmp: mpq_cmp */ +int GMPQAPI(cmp)(mp_rat op1, mp_rat op2) { + return mp_rat_compare(op1, op2); +} + +/* gmp: mpq_init */ +void GMPQAPI(init)(mp_rat x) { + CHECK(mp_rat_init(x)); +} + +/* gmp: mpq_mul */ +void GMPQAPI(mul)(mp_rat product, mp_rat multiplier, mp_rat multiplicand) { + CHECK(mp_rat_mul(multiplier, multiplicand, product)); +} + +/* gmp: mpq_set*/ +void GMPQAPI(set)(mp_rat rop, mp_rat op) { + CHECK(mp_rat_copy(op, rop)); +} + +/* gmp: mpz_abs */ +void GMPZAPI(abs)(mp_int rop, mp_int op) { + CHECK(mp_int_abs(op, rop)); +} + +/* gmp: mpz_add */ +void GMPZAPI(add)(mp_int rop, mp_int op1, mp_int op2) { + CHECK(mp_int_add(op1, op2, rop)); +} + +/* gmp: mpz_clear */ +void GMPZAPI(clear)(mp_int x) { + mp_int_clear(x); +} + +/* gmp: mpz_cmp_si */ +int GMPZAPI(cmp_si)(mp_int op1, long op2) { + return mp_int_compare_value(op1, op2); +} + +/* gmp: mpz_cmpabs */ +int GMPZAPI(cmpabs)(mp_int op1, mp_int op2) { + return mp_int_compare_unsigned(op1, op2); +} + +/* gmp: mpz_cmp */ +int GMPZAPI(cmp)(mp_int op1, mp_int op2) { + return mp_int_compare(op1, op2); +} + +/* gmp: mpz_init */ +void GMPZAPI(init)(mp_int x) { + CHECK(mp_int_init(x)); +} + +/* gmp: mpz_mul */ +void GMPZAPI(mul)(mp_int rop, mp_int op1, mp_int op2) { + CHECK(mp_int_mul(op1, op2, rop)); +} + +/* gmp: mpz_neg */ +void GMPZAPI(neg)(mp_int rop, mp_int op) { + CHECK(mp_int_neg(op, rop)); +} + +/* gmp: mpz_set_si */ +void GMPZAPI(set_si)(mp_int rop, long op) { + CHECK(mp_int_set_value(rop, op)); +} + +/* gmp: mpz_set */ +void GMPZAPI(set)(mp_int rop, mp_int op) { + CHECK(mp_int_copy(op, rop)); +} + +/* gmp: mpz_sub */ +void GMPZAPI(sub)(mp_int rop, mp_int op1, mp_int op2) { + CHECK(mp_int_sub(op1, op2, rop)); +} + +/* gmp: mpz_swap */ +void GMPZAPI(swap)(mp_int rop1, mp_int rop2) { + mp_int_swap(rop1, rop2); +} + +/* gmp: mpq_sgn */ +int GMPQAPI(sgn)(mp_rat op) { + return mp_rat_compare_zero(op); +} + +/* gmp: mpz_sgn */ +int GMPZAPI(sgn)(mp_int op) { + return mp_int_compare_zero(op); +} + +/* gmp: mpq_set_ui */ +void GMPQAPI(set_ui)(mp_rat rop, unsigned long op1, unsigned long op2) { + CHECK(mp_rat_set_uvalue(rop, op1, op2)); +} + +/* gmp: mpz_set_ui */ +void GMPZAPI(set_ui)(mp_int rop, unsigned long op) { + CHECK(mp_int_set_uvalue(rop, op)); +} + +/* gmp: mpq_den_ref */ +mp_int GMPQAPI(denref)(mp_rat op) { + return mp_rat_denom_ref(op); +} + +/* gmp: mpq_num_ref */ +mp_int GMPQAPI(numref)(mp_rat op) { + return mp_rat_numer_ref(op); +} + +/* gmp: mpq_canonicalize */ +void GMPQAPI(canonicalize)(mp_rat op) { + CHECK(mp_rat_reduce(op)); +} + +/************************************************************************* + * + * Functions that can be implemented as a combination of imath functions + * + *************************************************************************/ +/* gmp: mpz_addmul */ +/* gmp: rop = rop + (op1 * op2) */ +void GMPZAPI(addmul)(mp_int rop, mp_int op1, mp_int op2) { + mpz_t tempz; + mp_int temp = &tempz; + mp_int_init(temp); + + CHECK(mp_int_mul(op1, op2, temp)); + CHECK(mp_int_add(rop, temp, rop)); + mp_int_clear(temp); +} + +/* gmp: mpz_divexact */ +/* gmp: only produces correct results when d divides n */ +void GMPZAPI(divexact)(mp_int q, mp_int n, mp_int d) { + CHECK(mp_int_div(n, d, q, NULL)); +} + +/* gmp: mpz_divisible_p */ +/* gmp: return 1 if d divides n, 0 otherwise */ +/* gmp: 0 is considered to divide 0*/ +int GMPZAPI(divisible_p)(mp_int n, mp_int d) { + /* variables to hold remainder */ + mpz_t rz; + mp_int r = &rz; + int r_is_zero; + + /* check for n = 0, d = 0 */ + int n_is_zero = mp_int_compare_zero(n) == 0; + int d_is_zero = mp_int_compare_zero(d) == 0; + if (n_is_zero && d_is_zero) + return 1; + + /* return true if remainder is 0 */ + CHECK(mp_int_init(r)); + CHECK(mp_int_div(n, d, NULL, r)); + r_is_zero = mp_int_compare_zero(r) == 0; + mp_int_clear(r); + + return r_is_zero; +} + +/* gmp: mpz_submul */ +/* gmp: rop = rop - (op1 * op2) */ +void GMPZAPI(submul)(mp_int rop, mp_int op1, mp_int op2) { + mpz_t tempz; + mp_int temp = &tempz; + mp_int_init(temp); + + CHECK(mp_int_mul(op1, op2, temp)); + CHECK(mp_int_sub(rop, temp, rop)); + + mp_int_clear(temp); +} + +/* gmp: mpz_add_ui */ +void GMPZAPI(add_ui)(mp_int rop, mp_int op1, unsigned long op2) { + mpz_t tempz; + mp_int temp = &tempz; + CHECK(mp_int_init_uvalue(temp, op2)); + + CHECK(mp_int_add(op1, temp, rop)); + + mp_int_clear(temp); +} + +/* gmp: mpz_divexact_ui */ +/* gmp: only produces correct results when d divides n */ +void GMPZAPI(divexact_ui)(mp_int q, mp_int n, unsigned long d) { + mpz_t tempz; + mp_int temp = &tempz; + CHECK(mp_int_init_uvalue(temp, d)); + + CHECK(mp_int_div(n, temp, q, NULL)); + + mp_int_clear(temp); +} + +/* gmp: mpz_mul_ui */ +void GMPZAPI(mul_ui)(mp_int rop, mp_int op1, unsigned long op2) { + mpz_t tempz; + mp_int temp = &tempz; + CHECK(mp_int_init_uvalue(temp, op2)); + + CHECK(mp_int_mul(op1, temp, rop)); + + mp_int_clear(temp); +} + +/* gmp: mpz_pow_ui */ +/* gmp: 0^0 = 1 */ +void GMPZAPI(pow_ui)(mp_int rop, mp_int base, unsigned long exp) { + mpz_t tempz; + mp_int temp = &tempz; + + /* check for 0^0 */ + if (exp == 0 && mp_int_compare_zero(base) == 0) { + CHECK(mp_int_set_value(rop, 1)); + return; + } + + /* rop = base^exp */ + CHECK(mp_int_init_uvalue(temp, exp)); + CHECK(mp_int_expt_full(base, temp, rop)); + mp_int_clear(temp); +} + +/* gmp: mpz_sub_ui */ +void GMPZAPI(sub_ui)(mp_int rop, mp_int op1, unsigned long op2) { + mpz_t tempz; + mp_int temp = &tempz; + CHECK(mp_int_init_uvalue(temp, op2)); + + CHECK(mp_int_sub(op1, temp, rop)); + + mp_int_clear(temp); +} + +/************************************************************************* + * + * Functions with different behavior in corner cases + * + *************************************************************************/ + +/* gmp: mpz_gcd */ +void GMPZAPI(gcd)(mp_int rop, mp_int op1, mp_int op2) { + int op1_is_zero = mp_int_compare_zero(op1) == 0; + int op2_is_zero = mp_int_compare_zero(op2) == 0; + + if (op1_is_zero && op2_is_zero) { + mp_int_zero(rop); + return; + } + + CHECK(mp_int_gcd(op1, op2, rop)); +} + +/* gmp: mpz_get_str */ +char* GMPZAPI(get_str)(char *str, int radix, mp_int op) { + int i, r, len; + + /* Support negative radix like gmp */ + r = radix; + if (r < 0) + r = -r; + + /* Compute the length of the string needed to hold the int */ + len = mp_int_string_len(op, r); + if (str == NULL) { + str = malloc(len); + } + + /* Convert to string using imath function */ + CHECK(mp_int_to_string(op, r, str, len)); + + /* Change case to match gmp */ + for (i = 0; i < len - 1; i++) + if (radix < 0) + str[i] = toupper(str[i]); + else + str[i] = tolower(str[i]); + return str; +} + +/* gmp: mpq_get_str */ +char* GMPQAPI(get_str)(char *str, int radix, mp_rat op) { + int i, r, len; + + /* Only print numerator if it is a whole number */ + if (mp_int_compare_value(mp_rat_denom_ref(op), 1) == 0) + return GMPZAPI(get_str)(str, radix, mp_rat_numer_ref(op)); + + /* Support negative radix like gmp */ + r = radix; + if (r < 0) + r = -r; + + /* Compute the length of the string needed to hold the int */ + len = mp_rat_string_len(op, r); + if (str == NULL) { + str = malloc(len); + } + + /* Convert to string using imath function */ + CHECK(mp_rat_to_string(op, r, str, len)); + + /* Change case to match gmp */ + for (i = 0; i < len; i++) + if (radix < 0) + str[i] = toupper(str[i]); + else + str[i] = tolower(str[i]); + + return str; +} + +/* gmp: mpz_set_str */ +int GMPZAPI(set_str)(mp_int rop, char *str, int base) { + mp_result res = mp_int_read_string(rop, base, str); + return ((res == MP_OK) ? 0 : -1); +} + +/* gmp: mpq_set_str */ +int GMPQAPI(set_str)(mp_rat rop, char *s, int base) { + char *slash; + char *str; + mp_result resN; + mp_result resD; + int res = 0; + + /* Copy string to temporary storage so we can modify it below */ + str = malloc(strlen(s)+1); + strcpy(str, s); + + /* Properly format the string as an int by terminating at the / */ + slash = strchr(str, '/'); + if (slash) + *slash = '\0'; + + /* Parse numerator */ + resN = mp_int_read_string(mp_rat_numer_ref(rop), base, str); + + /* Parse denomenator if given or set to 1 if not */ + if (slash) + resD = mp_int_read_string(mp_rat_denom_ref(rop), base, slash+1); + else + resD = mp_int_set_uvalue(mp_rat_denom_ref(rop), 1); + + /* Return failure if either parse failed */ + if (resN != MP_OK || resD != MP_OK) + res = -1; + + free(str); + return res; +} + +static unsigned long get_long_bits(mp_int op) { + /* Deal with integer that does not fit into unsigned long. We want to grab + * the least significant digits that will fit into the long. Read the digits + * into the long starting at the most significant digit that fits into a + * long. The long is shifted over by MP_DIGIT_BIT before each digit is added. + * The shift is decomposed into two steps to follow the patten used in the + * rest of the imath library. The two step shift is used to accomedate + * architectures that don't deal well with 32-bit shifts. */ + mp_size num_digits_in_long = sizeof(unsigned long) / sizeof(mp_digit); + mp_digit *digits = MP_DIGITS(op); + unsigned long out = 0; + int i; + + for (i = num_digits_in_long - 1; i >= 0; i--) { + out <<= (MP_DIGIT_BIT/2); + out <<= (MP_DIGIT_BIT/2); + out |= digits[i]; + } + + return out; +} + +/* gmp: mpz_get_ui */ +unsigned long GMPZAPI(get_ui)(mp_int op) { + unsigned long out; + + /* Try a standard conversion that fits into an unsigned long */ + mp_result res = mp_int_to_uint(op, &out); + if (res == MP_OK) + return out; + + /* Abort the try if we don't have a range error in the conversion. + * The range error indicates that the value cannot fit into a long. */ + CHECK(res == MP_RANGE ? MP_OK : MP_RANGE); + if (res != MP_RANGE) + return 0; + + return get_long_bits(op); +} + +/* gmp: mpz_get_si */ +long GMPZAPI(get_si)(mp_int op) { + long out; + unsigned long uout; + int long_msb; + + /* Try a standard conversion that fits into a long */ + mp_result res = mp_int_to_int(op, &out); + if (res == MP_OK) + return out; + + /* Abort the try if we don't have a range error in the conversion. + * The range error indicates that the value cannot fit into a long. */ + CHECK(res == MP_RANGE ? MP_OK : MP_RANGE); + if (res != MP_RANGE) + return 0; + + /* get least significant bits into an unsigned long */ + uout = get_long_bits(op); + + /* clear the top bit */ + long_msb = (sizeof(unsigned long) * 8) - 1; + uout &= (~(1UL << long_msb)); + + /* convert to negative if needed based on sign of op */ + if (MP_SIGN(op) == MP_NEG) + uout = 0 - uout; + + out = (long) uout; + return out; +} + +/* gmp: mpz_lcm */ +void GMPZAPI(lcm)(mp_int rop, mp_int op1, mp_int op2) { + int op1_is_zero = mp_int_compare_zero(op1) == 0; + int op2_is_zero = mp_int_compare_zero(op2) == 0; + + if (op1_is_zero || op2_is_zero) { + mp_int_zero(rop); + return; + } + + CHECK(mp_int_lcm(op1, op2, rop)); + CHECK(mp_int_abs(rop, rop)); +} + +/* gmp: mpz_mul_2exp */ +/* gmp: allow big values for op2 when op1 == 0 */ +void GMPZAPI(mul_2exp)(mp_int rop, mp_int op1, unsigned long op2) { + if (mp_int_compare_zero(op1) == 0) + mp_int_zero(rop); + else + CHECK(mp_int_mul_pow2(op1, op2, rop)); +} + +/************************************************************************* + * + * Functions needing expanded functionality + * + *************************************************************************/ +/* [Note]Overview of division implementation + + All division operations (N / D) compute q and r such that + + N = q * D + r, with 0 <= abs(r) < abs(d) + + The q and r values are not uniquely specified by N and D. To specify which q + and r values should be used, GMP implements three different rounding modes + for integer division: + + ceiling - round q twords +infinity, r has opposite sign as d + floor - round q twords -infinity, r has same sign as d + truncate - round q twords zero, r has same sign as n + + The imath library only supports truncate as a rounding mode. We need to + implement the other rounding modes in terms of truncating division. We first + perform the division in trucate mode and then adjust q accordingly. Once we + know q, we can easily compute the correct r according the the formula above + by computing: + + r = N - q * D + + The main task is to compute q. We can compute the correct q from a trucated + version as follows. + + For ceiling rounding mode, if q is less than 0 then the truncated rounding + mode is the same as the ceiling rounding mode. If q is greater than zero + then we need to round q up by one because the truncated version was rounded + down to zero. If q equals zero then check to see if the result of the + divison is positive. A positive result needs to increment q to one. + + For floor rounding mode, if q is greater than 0 then the trucated rounding + mode is the same as the floor rounding mode. If q is less than zero then we + need to round q down by one because the trucated mode rounded q up by one + twords zero. If q is zero then we need to check to see if the result of the + division is negative. A negative result needs to decrement q to negative + one. + */ + +/* gmp: mpz_cdiv_q */ +void GMPZAPI(cdiv_q)(mp_int q, mp_int n, mp_int d) { + mpz_t rz; + mp_int r = &rz; + int qsign, rsign, nsign, dsign; + CHECK(mp_int_init(r)); + + /* save signs before division because q can alias with n or d */ + nsign = mp_int_compare_zero(n); + dsign = mp_int_compare_zero(d); + + /* truncating division */ + CHECK(mp_int_div(n, d, q, r)); + + /* see: [Note]Overview of division implementation */ + qsign = mp_int_compare_zero(q); + rsign = mp_int_compare_zero(r); + if (qsign > 0) { /* q > 0 */ + if (rsign != 0) { /* r != 0 */ + CHECK(mp_int_add_value(q, 1, q)); + } + } + else if (qsign == 0) { /* q == 0 */ + if (rsign != 0) { /* r != 0 */ + if ((nsign > 0 && dsign > 0) || (nsign < 0 && dsign < 0)) { + CHECK(mp_int_set_value(q, 1)); + } + } + } + mp_int_clear(r); +} + +/* gmp: mpz_fdiv_q */ +void GMPZAPI(fdiv_q)(mp_int q, mp_int n, mp_int d) { + mpz_t rz; + mp_int r = &rz; + int qsign, rsign, nsign, dsign; + CHECK(mp_int_init(r)); + + /* save signs before division because q can alias with n or d */ + nsign = mp_int_compare_zero(n); + dsign = mp_int_compare_zero(d); + + /* truncating division */ + CHECK(mp_int_div(n, d, q, r)); + + /* see: [Note]Overview of division implementation */ + qsign = mp_int_compare_zero(q); + rsign = mp_int_compare_zero(r); + if (qsign < 0) { /* q < 0 */ + if (rsign != 0) { /* r != 0 */ + CHECK(mp_int_sub_value(q, 1, q)); + } + } + else if (qsign == 0) { /* q == 0 */ + if (rsign != 0) { /* r != 0 */ + if ((nsign < 0 && dsign > 0) || (nsign > 0 && dsign < 0)) { + CHECK(mp_int_set_value(q, -1)); + } + } + } + mp_int_clear(r); +} + +/* gmp: mpz_fdiv_r */ +void GMPZAPI(fdiv_r)(mp_int r, mp_int n, mp_int d) { + mpz_t qz; + mpz_t tempz; + mpz_t orig_dz; + mpz_t orig_nz; + mp_int q = &qz; + mp_int temp = &tempz; + mp_int orig_d = &orig_dz; + mp_int orig_n = &orig_nz; + CHECK(mp_int_init(q)); + CHECK(mp_int_init(temp)); + /* Make a copy of n in case n and d in case they overlap with q */ + CHECK(mp_int_init_copy(orig_d, d)); + CHECK(mp_int_init_copy(orig_n, n)); + + /* floor division */ + GMPZAPI(fdiv_q)(q, n, d); + + /* see: [Note]Overview of division implementation */ + /* n = q * d + r ==> r = n - q * d */ + mp_int_mul(q, orig_d, temp); + mp_int_sub(orig_n, temp, r); + + mp_int_clear(q); + mp_int_clear(temp); + mp_int_clear(orig_d); + mp_int_clear(orig_n); +} + +/* gmp: mpz_tdiv_q */ +void GMPZAPI(tdiv_q)(mp_int q, mp_int n, mp_int d) { + /* truncating division*/ + CHECK(mp_int_div(n, d, q, NULL)); +} + +/* gmp: mpz_fdiv_q_ui */ +unsigned long GMPZAPI(fdiv_q_ui)(mp_int q, mp_int n, unsigned long d) { + mpz_t tempz; + mp_int temp = &tempz; + mpz_t rz; + mp_int r = &rz; + mpz_t orig_nz; + mp_int orig_n = &orig_nz; + unsigned long rl; + CHECK(mp_int_init_uvalue(temp, d)); + CHECK(mp_int_init(r)); + /* Make a copy of n in case n and q overlap */ + CHECK(mp_int_init_copy(orig_n, n)); + + /* use floor division mode to compute q and r */ + GMPZAPI(fdiv_q)(q, n, temp); + GMPZAPI(fdiv_r)(r, orig_n, temp); + CHECK(mp_int_to_uint(r, &rl)); + + mp_int_clear(temp); + mp_int_clear(r); + mp_int_clear(orig_n); + + return rl; +} + +/* gmp: mpz_export */ +void* GMPZAPI(export)(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int op) { + int i; + int num_used_bytes; + size_t num_words, num_missing_bytes; + unsigned char* dst; + unsigned char* src; + + /* We do not have a complete implementation. Assert to ensure our + * restrictions are in place, We do not support big endian output, but do not + * check that native endian is little endian. */ + assert(nails == 0 && "Do not support non-full words"); + assert((endian == 0 || endian == -1) && "Do not support big endian"); + + /* The gmp API requires that order must be -1 or 1. + Not sure how gmp behaves when order is not 1 or -1, so force all non-one + values to -1 for now. */ + if (order != 1) order = -1; + + /* Test for zero */ + if (mp_int_compare_zero(op) == 0) { + if (countp) + *countp = 0; + return rop; + } + + /* Calculate how many words we need */ + num_used_bytes = mp_int_unsigned_len(op); + num_words = (num_used_bytes + (size-1)) / size; /* ceil division */ + assert(num_used_bytes > 0); + + /* Check to see if we will have missing bytes in the last word. + + Missing bytes can only occur when the size of words we output is + greater than the size of words used internally by imath. The number of + missing bytes is the number of bytes needed to fill out the last word. If + this number is greater than the size of a single mp_digit, then we need to + pad the word with extra zeros. Otherwise, the missing bytes can be filled + directly from the zeros in the last digit in the number. + */ + num_missing_bytes = (size * num_words) - num_used_bytes; + assert(num_missing_bytes < size); + + /* Allocate space for the result if needed */ + if (rop == NULL) { + rop = malloc(num_words * size); + } + + /* Initialize dst and src pointers */ + dst = (unsigned char *)rop; + src = (unsigned char *)MP_DIGITS(op); + + /* Most significant word first */ + if (order == 1) { + size_t words_written = 0; + src += (num_words-1) * size; + + /* Handle write of first word specially */ + for (i = 0; i < size - num_missing_bytes; i++) + dst[i] = src[i]; + for (; i < size; i++) + dst[i] = 0; + dst += size; + src -= size; + words_written++; + + for (; words_written < num_words; words_written++) { + for (i = 0; i < size; i++) + dst[i] = src[i]; + dst += size; + src -= size; + } + } + /* Least significant word first */ + else { + size_t words_written = 0; + for (; words_written < num_words - 1; words_written++) { + for (i = 0; i < size; i++) + dst[i] = src[i]; + dst += size; + src += size; + } + + /* Handle write of last word specially */ + for (i = 0; i < size - num_missing_bytes; i++) + dst[i] = src[i]; + + for (; i < size; i++) + dst[i] = 0; + } + + if (countp) + *countp = num_words; + return rop; +} + +/* gmp: mpz_import */ +void GMPZAPI(import)(mp_int rop, size_t count, int order, size_t size, int endian, size_t nails, const void* op) { + mpz_t tmpz; + mp_int tmp = &tmpz; + size_t total_size; + size_t num_digits; + const char *src; + char *dst; + int i; + if (count == 0 || op == NULL) + return; + + /* We do not have a complete implementation. Assert to ensure our + * restrictions are in place, We do not support big endian output, but do not + * check that native endian is little endian. */ + assert(nails == 0 && "Do not support non-full words"); + assert((endian == 0 || endian == -1) && "Do not support big endian"); + + /* Compute number of needed digits by ceil division */ + total_size = count * size; + num_digits = (total_size + sizeof(mp_digit) - 1) / sizeof(mp_digit); + + /* Init temporary */ + mp_int_init_size(tmp, num_digits); + for (i = 0; i < num_digits; i++) + tmp->digits[i] = 0; + + /* Copy bytes */ + src = (const char *) op; + dst = (char *)MP_DIGITS(tmp); + + /* Most significant word is first */ + if (order == 1) { + size_t word; + dst += (count - 1) * size; + for (word = 0; word < count; word++) { + for (i = 0; i < size; i++) + dst[i] = src[i]; + dst -= size; + src += size; + } + } + /* Least significant word is first */ + else { + size_t word; + for (word = 0; word < count; word++) { + for (i = 0; i < size; i++) + dst[i] = src[i]; + dst += size; + src += size; + } + } + MP_USED(tmp) = num_digits; + + /* Remove leading zeros from number */ + { + mp_size uz_ = MP_USED(tmp); + mp_digit *dz_ = MP_DIGITS(tmp) + uz_ -1; + while (uz_ > 1 && (*dz_-- == 0)) + --uz_; + MP_USED(tmp) = uz_; + } + + /* Copy to destination */ + mp_int_copy(tmp, rop); + mp_int_clear(tmp); +} + +/* gmp: mpz_sizeinbase */ +size_t GMPZAPI(sizeinbase)(mp_int op, int base) { + mp_result res; + size_t size; + + /* If op == 0, return 1 */ + if (mp_int_compare_zero(op) == 0) + return 1; + + /* Compute string length in base */ + res = mp_int_string_len(op, base); + CHECK((res > 0) == MP_OK); + + /* Now adjust the final size by getting rid of string artifacts */ + size = res; + + /* subtract one for the null terminator */ + size -= 1; + + /* subtract one for the negative sign */ + if (mp_int_compare_zero(op) < 0) + size -= 1; + + return size; +} Index: lib/Analysis/isl/imath/imath.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath/imath.h @@ -0,0 +1,232 @@ +/* + Name: imath.h + Purpose: Arbitrary precision integer arithmetic routines. + Author: M. J. Fromberger + + Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#ifndef IMATH_H_ +#define IMATH_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char mp_sign; +typedef unsigned int mp_size; +typedef int mp_result; +typedef long mp_small; /* must be a signed type */ +typedef unsigned long mp_usmall; /* must be an unsigned type */ + +/* Force building with uint64_t so that the library builds consistently + * whether we build from the makefile or by embedding imath in another project. + */ +#undef USE_64BIT_WORDS +#define USE_64BIT_WORDS +#ifdef USE_64BIT_WORDS +typedef uint32_t mp_digit; +typedef uint64_t mp_word; +#else +typedef uint16_t mp_digit; +typedef uint32_t mp_word; +#endif + +typedef struct mpz { + mp_digit single; + mp_digit *digits; + mp_size alloc; + mp_size used; + mp_sign sign; +} mpz_t, *mp_int; + +#define MP_DIGITS(Z) ((Z)->digits) +#define MP_ALLOC(Z) ((Z)->alloc) +#define MP_USED(Z) ((Z)->used) +#define MP_SIGN(Z) ((Z)->sign) + +extern const mp_result MP_OK; +extern const mp_result MP_FALSE; +extern const mp_result MP_TRUE; +extern const mp_result MP_MEMORY; +extern const mp_result MP_RANGE; +extern const mp_result MP_UNDEF; +extern const mp_result MP_TRUNC; +extern const mp_result MP_BADARG; +extern const mp_result MP_MINERR; + +#define MP_DIGIT_BIT (sizeof(mp_digit) * CHAR_BIT) +#define MP_WORD_BIT (sizeof(mp_word) * CHAR_BIT) +#define MP_SMALL_MIN LONG_MIN +#define MP_SMALL_MAX LONG_MAX +#define MP_USMALL_MIN ULONG_MIN +#define MP_USMALL_MAX ULONG_MAX + +#ifdef USE_64BIT_WORDS +# define MP_DIGIT_MAX (UINT32_MAX * UINT64_C(1)) +# define MP_WORD_MAX (UINT64_MAX) +#else +# define MP_DIGIT_MAX (UINT16_MAX * 1UL) +# define MP_WORD_MAX (UINT32_MAX * 1UL) +#endif + +#define MP_MIN_RADIX 2 +#define MP_MAX_RADIX 36 + +/* Values with fewer than this many significant digits use the standard + multiplication algorithm; otherwise, a recursive algorithm is used. + Choose a value to suit your platform. + */ +#define MP_MULT_THRESH 22 + +#define MP_DEFAULT_PREC 8 /* default memory allocation, in digits */ + +extern const mp_sign MP_NEG; +extern const mp_sign MP_ZPOS; + +#define mp_int_is_odd(Z) ((Z)->digits[0] & 1) +#define mp_int_is_even(Z) !((Z)->digits[0] & 1) + +mp_result mp_int_init(mp_int z); +mp_int mp_int_alloc(void); +mp_result mp_int_init_size(mp_int z, mp_size prec); +mp_result mp_int_init_copy(mp_int z, mp_int old); +mp_result mp_int_init_value(mp_int z, mp_small value); +mp_result mp_int_init_uvalue(mp_int z, mp_usmall uvalue); +mp_result mp_int_set_value(mp_int z, mp_small value); +mp_result mp_int_set_uvalue(mp_int z, mp_usmall uvalue); +void mp_int_clear(mp_int z); +void mp_int_free(mp_int z); + +mp_result mp_int_copy(mp_int a, mp_int c); /* c = a */ +void mp_int_swap(mp_int a, mp_int c); /* swap a, c */ +void mp_int_zero(mp_int z); /* z = 0 */ +mp_result mp_int_abs(mp_int a, mp_int c); /* c = |a| */ +mp_result mp_int_neg(mp_int a, mp_int c); /* c = -a */ +mp_result mp_int_add(mp_int a, mp_int b, mp_int c); /* c = a + b */ +mp_result mp_int_add_value(mp_int a, mp_small value, mp_int c); +mp_result mp_int_sub(mp_int a, mp_int b, mp_int c); /* c = a - b */ +mp_result mp_int_sub_value(mp_int a, mp_small value, mp_int c); +mp_result mp_int_mul(mp_int a, mp_int b, mp_int c); /* c = a * b */ +mp_result mp_int_mul_value(mp_int a, mp_small value, mp_int c); +mp_result mp_int_mul_pow2(mp_int a, mp_small p2, mp_int c); +mp_result mp_int_sqr(mp_int a, mp_int c); /* c = a * a */ +mp_result mp_int_div(mp_int a, mp_int b, /* q = a / b */ + mp_int q, mp_int r); /* r = a % b */ +mp_result mp_int_div_value(mp_int a, mp_small value, /* q = a / value */ + mp_int q, mp_small *r); /* r = a % value */ +mp_result mp_int_div_pow2(mp_int a, mp_small p2, /* q = a / 2^p2 */ + mp_int q, mp_int r); /* r = q % 2^p2 */ +mp_result mp_int_mod(mp_int a, mp_int m, mp_int c); /* c = a % m */ +#define mp_int_mod_value(A, V, R) mp_int_div_value((A), (V), 0, (R)) +mp_result mp_int_expt(mp_int a, mp_small b, mp_int c); /* c = a^b */ +mp_result mp_int_expt_value(mp_small a, mp_small b, mp_int c); /* c = a^b */ +mp_result mp_int_expt_full(mp_int a, mp_int b, mp_int c); /* c = a^b */ + +int mp_int_compare(mp_int a, mp_int b); /* a <=> b */ +int mp_int_compare_unsigned(mp_int a, mp_int b); /* |a| <=> |b| */ +int mp_int_compare_zero(mp_int z); /* a <=> 0 */ +int mp_int_compare_value(mp_int z, mp_small v); /* a <=> v */ +int mp_int_compare_uvalue(mp_int z, mp_usmall uv); /* a <=> uv */ + +/* Returns true if v|a, false otherwise (including errors) */ +int mp_int_divisible_value(mp_int a, mp_small v); + +/* Returns k >= 0 such that z = 2^k, if one exists; otherwise < 0 */ +int mp_int_is_pow2(mp_int z); + +mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m, + mp_int c); /* c = a^b (mod m) */ +mp_result mp_int_exptmod_evalue(mp_int a, mp_small value, + mp_int m, mp_int c); /* c = a^v (mod m) */ +mp_result mp_int_exptmod_bvalue(mp_small value, mp_int b, + mp_int m, mp_int c); /* c = v^b (mod m) */ +mp_result mp_int_exptmod_known(mp_int a, mp_int b, + mp_int m, mp_int mu, + mp_int c); /* c = a^b (mod m) */ +mp_result mp_int_redux_const(mp_int m, mp_int c); + +mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c); /* c = 1/a (mod m) */ + +mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c); /* c = gcd(a, b) */ + +mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c, /* c = gcd(a, b) */ + mp_int x, mp_int y); /* c = ax + by */ + +mp_result mp_int_lcm(mp_int a, mp_int b, mp_int c); /* c = lcm(a, b) */ + +mp_result mp_int_root(mp_int a, mp_small b, mp_int c); /* c = floor(a^{1/b}) */ +#define mp_int_sqrt(a, c) mp_int_root(a, 2, c) /* c = floor(sqrt(a)) */ + +/* Convert to a small int, if representable; else MP_RANGE */ +mp_result mp_int_to_int(mp_int z, mp_small *out); +mp_result mp_int_to_uint(mp_int z, mp_usmall *out); + +/* Convert to nul-terminated string with the specified radix, writing at + most limit characters including the nul terminator */ +mp_result mp_int_to_string(mp_int z, mp_size radix, + char *str, int limit); + +/* Return the number of characters required to represent + z in the given radix. May over-estimate. */ +mp_result mp_int_string_len(mp_int z, mp_size radix); + +/* Read zero-terminated string into z */ +mp_result mp_int_read_string(mp_int z, mp_size radix, const char *str); +mp_result mp_int_read_cstring(mp_int z, mp_size radix, const char *str, + char **end); + +/* Return the number of significant bits in z */ +mp_result mp_int_count_bits(mp_int z); + +/* Convert z to two's complement binary, writing at most limit bytes */ +mp_result mp_int_to_binary(mp_int z, unsigned char *buf, int limit); + +/* Read a two's complement binary value into z from the given buffer */ +mp_result mp_int_read_binary(mp_int z, unsigned char *buf, int len); + +/* Return the number of bytes required to represent z in binary. */ +mp_result mp_int_binary_len(mp_int z); + +/* Convert z to unsigned binary, writing at most limit bytes */ +mp_result mp_int_to_unsigned(mp_int z, unsigned char *buf, int limit); + +/* Read an unsigned binary value into z from the given buffer */ +mp_result mp_int_read_unsigned(mp_int z, unsigned char *buf, int len); + +/* Return the number of bytes required to represent z as unsigned output */ +mp_result mp_int_unsigned_len(mp_int z); + +/* Return a statically allocated string describing error code res */ +const char *mp_error_string(mp_result res); + +#if DEBUG +void s_print(char *tag, mp_int z); +void s_print_buf(char *tag, mp_digit *buf, mp_size num); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* end IMATH_H_ */ Index: lib/Analysis/isl/imath/imath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/imath/imath.c @@ -0,0 +1,3256 @@ +/* + Name: imath.c + Purpose: Arbitrary precision integer arithmetic routines. + Author: M. J. Fromberger + + Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "imath.h" + +#if DEBUG +#include +#endif + +#include +#include +#include + +#include + +#if DEBUG +#define STATIC /* public */ +#else +#define STATIC static +#endif + +const mp_result MP_OK = 0; /* no error, all is well */ +const mp_result MP_FALSE = 0; /* boolean false */ +const mp_result MP_TRUE = -1; /* boolean true */ +const mp_result MP_MEMORY = -2; /* out of memory */ +const mp_result MP_RANGE = -3; /* argument out of range */ +const mp_result MP_UNDEF = -4; /* result undefined */ +const mp_result MP_TRUNC = -5; /* output truncated */ +const mp_result MP_BADARG = -6; /* invalid null argument */ +const mp_result MP_MINERR = -6; + +const mp_sign MP_NEG = 1; /* value is strictly negative */ +const mp_sign MP_ZPOS = 0; /* value is non-negative */ + +STATIC const char *s_unknown_err = "unknown result code"; +STATIC const char *s_error_msg[] = { + "error code 0", + "boolean true", + "out of memory", + "argument out of range", + "result undefined", + "output truncated", + "invalid argument", + NULL +}; + +/* Argument checking macros + Use CHECK() where a return value is required; NRCHECK() elsewhere */ +#define CHECK(TEST) assert(TEST) +#define NRCHECK(TEST) assert(TEST) + +/* The ith entry of this table gives the value of log_i(2). + + An integer value n requires ceil(log_i(n)) digits to be represented + in base i. Since it is easy to compute lg(n), by counting bits, we + can compute log_i(n) = lg(n) * log_i(2). + + The use of this table eliminates a dependency upon linkage against + the standard math libraries. + + If MP_MAX_RADIX is increased, this table should be expanded too. + */ +STATIC const double s_log2[] = { + 0.000000000, 0.000000000, 1.000000000, 0.630929754, /* (D)(D) 2 3 */ + 0.500000000, 0.430676558, 0.386852807, 0.356207187, /* 4 5 6 7 */ + 0.333333333, 0.315464877, 0.301029996, 0.289064826, /* 8 9 10 11 */ + 0.278942946, 0.270238154, 0.262649535, 0.255958025, /* 12 13 14 15 */ + 0.250000000, 0.244650542, 0.239812467, 0.235408913, /* 16 17 18 19 */ + 0.231378213, 0.227670249, 0.224243824, 0.221064729, /* 20 21 22 23 */ + 0.218104292, 0.215338279, 0.212746054, 0.210309918, /* 24 25 26 27 */ + 0.208014598, 0.205846832, 0.203795047, 0.201849087, /* 28 29 30 31 */ + 0.200000000, 0.198239863, 0.196561632, 0.194959022, /* 32 33 34 35 */ + 0.193426404, /* 36 */ +}; + + + +/* Return the number of digits needed to represent a static value */ +#define MP_VALUE_DIGITS(V) \ +((sizeof(V)+(sizeof(mp_digit)-1))/sizeof(mp_digit)) + +/* Round precision P to nearest word boundary */ +#define ROUND_PREC(P) ((mp_size)(2*(((P)+1)/2))) + +/* Set array P of S digits to zero */ +#define ZERO(P, S) \ +do{ \ + mp_size i__ = (S) * sizeof(mp_digit); \ + mp_digit *p__ = (P); \ + memset(p__, 0, i__); \ +} while(0) + +/* Copy S digits from array P to array Q */ +#define COPY(P, Q, S) \ +do{ \ + mp_size i__ = (S) * sizeof(mp_digit); \ + mp_digit *p__ = (P), *q__ = (Q); \ + memcpy(q__, p__, i__); \ +} while(0) + +/* Reverse N elements of type T in array A */ +#define REV(T, A, N) \ +do{ \ + T *u_ = (A), *v_ = u_ + (N) - 1; \ + while (u_ < v_) { \ + T xch = *u_; \ + *u_++ = *v_; \ + *v_-- = xch; \ + } \ +} while(0) + +#define CLAMP(Z) \ +do{ \ + mp_int z_ = (Z); \ + mp_size uz_ = MP_USED(z_); \ + mp_digit *dz_ = MP_DIGITS(z_) + uz_ -1; \ + while (uz_ > 1 && (*dz_-- == 0)) \ + --uz_; \ + MP_USED(z_) = uz_; \ +} while(0) + +/* Select min/max. Do not provide expressions for which multiple + evaluation would be problematic, e.g. x++ */ +#define MIN(A, B) ((B)<(A)?(B):(A)) +#define MAX(A, B) ((B)>(A)?(B):(A)) + +/* Exchange lvalues A and B of type T, e.g. + SWAP(int, x, y) where x and y are variables of type int. */ +#define SWAP(T, A, B) \ +do{ \ + T t_ = (A); \ + A = (B); \ + B = t_; \ +} while(0) + +/* Used to set up and access simple temp stacks within functions. */ +#define DECLARE_TEMP(N) \ + mpz_t temp[(N)]; \ + int last__ = 0 +#define CLEANUP_TEMP() \ + CLEANUP: \ + while (--last__ >= 0) \ + mp_int_clear(TEMP(last__)) +#define TEMP(K) (temp + (K)) +#define LAST_TEMP() TEMP(last__) +#define SETUP(E) \ +do{ \ + if ((res = (E)) != MP_OK) \ + goto CLEANUP; \ + ++(last__); \ +} while(0) + +/* Compare value to zero. */ +#define CMPZ(Z) \ +(((Z)->used==1&&(Z)->digits[0]==0)?0:((Z)->sign==MP_NEG)?-1:1) + +/* Multiply X by Y into Z, ignoring signs. Requires that Z have + enough storage preallocated to hold the result. */ +#define UMUL(X, Y, Z) \ +do{ \ + mp_size ua_ = MP_USED(X), ub_ = MP_USED(Y); \ + mp_size o_ = ua_ + ub_; \ + ZERO(MP_DIGITS(Z), o_); \ + (void) s_kmul(MP_DIGITS(X), MP_DIGITS(Y), MP_DIGITS(Z), ua_, ub_); \ + MP_USED(Z) = o_; \ + CLAMP(Z); \ +} while(0) + +/* Square X into Z. Requires that Z have enough storage to hold the + result. */ +#define USQR(X, Z) \ +do{ \ + mp_size ua_ = MP_USED(X), o_ = ua_ + ua_; \ + ZERO(MP_DIGITS(Z), o_); \ + (void) s_ksqr(MP_DIGITS(X), MP_DIGITS(Z), ua_); \ + MP_USED(Z) = o_; \ + CLAMP(Z); \ +} while(0) + +#define UPPER_HALF(W) ((mp_word)((W) >> MP_DIGIT_BIT)) +#define LOWER_HALF(W) ((mp_digit)(W)) +#define HIGH_BIT_SET(W) ((W) >> (MP_WORD_BIT - 1)) +#define ADD_WILL_OVERFLOW(W, V) ((MP_WORD_MAX - (V)) < (W)) + + + +/* Default number of digits allocated to a new mp_int */ +#if IMATH_TEST +mp_size default_precision = MP_DEFAULT_PREC; +#else +STATIC const mp_size default_precision = MP_DEFAULT_PREC; +#endif + +/* Minimum number of digits to invoke recursive multiply */ +#if IMATH_TEST +mp_size multiply_threshold = MP_MULT_THRESH; +#else +STATIC const mp_size multiply_threshold = MP_MULT_THRESH; +#endif + +/* Allocate a buffer of (at least) num digits, or return + NULL if that couldn't be done. */ +STATIC mp_digit *s_alloc(mp_size num); + +/* Release a buffer of digits allocated by s_alloc(). */ +STATIC void s_free(void *ptr); + +/* Insure that z has at least min digits allocated, resizing if + necessary. Returns true if successful, false if out of memory. */ +STATIC int s_pad(mp_int z, mp_size min); + +/* Fill in a "fake" mp_int on the stack with a given value */ +STATIC void s_fake(mp_int z, mp_small value, mp_digit vbuf[]); +STATIC void s_ufake(mp_int z, mp_usmall value, mp_digit vbuf[]); + +/* Compare two runs of digits of given length, returns <0, 0, >0 */ +STATIC int s_cdig(mp_digit *da, mp_digit *db, mp_size len); + +/* Pack the unsigned digits of v into array t */ +STATIC int s_uvpack(mp_usmall v, mp_digit t[]); + +/* Compare magnitudes of a and b, returns <0, 0, >0 */ +STATIC int s_ucmp(mp_int a, mp_int b); + +/* Compare magnitudes of a and v, returns <0, 0, >0 */ +STATIC int s_vcmp(mp_int a, mp_small v); +STATIC int s_uvcmp(mp_int a, mp_usmall uv); + +/* Unsigned magnitude addition; assumes dc is big enough. + Carry out is returned (no memory allocated). */ +STATIC mp_digit s_uadd(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b); + +/* Unsigned magnitude subtraction. Assumes dc is big enough. */ +STATIC void s_usub(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b); + +/* Unsigned recursive multiplication. Assumes dc is big enough. */ +STATIC int s_kmul(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b); + +/* Unsigned magnitude multiplication. Assumes dc is big enough. */ +STATIC void s_umul(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b); + +/* Unsigned recursive squaring. Assumes dc is big enough. */ +STATIC int s_ksqr(mp_digit *da, mp_digit *dc, mp_size size_a); + +/* Unsigned magnitude squaring. Assumes dc is big enough. */ +STATIC void s_usqr(mp_digit *da, mp_digit *dc, mp_size size_a); + +/* Single digit addition. Assumes a is big enough. */ +STATIC void s_dadd(mp_int a, mp_digit b); + +/* Single digit multiplication. Assumes a is big enough. */ +STATIC void s_dmul(mp_int a, mp_digit b); + +/* Single digit multiplication on buffers; assumes dc is big enough. */ +STATIC void s_dbmul(mp_digit *da, mp_digit b, mp_digit *dc, + mp_size size_a); + +/* Single digit division. Replaces a with the quotient, + returns the remainder. */ +STATIC mp_digit s_ddiv(mp_int a, mp_digit b); + +/* Quick division by a power of 2, replaces z (no allocation) */ +STATIC void s_qdiv(mp_int z, mp_size p2); + +/* Quick remainder by a power of 2, replaces z (no allocation) */ +STATIC void s_qmod(mp_int z, mp_size p2); + +/* Quick multiplication by a power of 2, replaces z. + Allocates if necessary; returns false in case this fails. */ +STATIC int s_qmul(mp_int z, mp_size p2); + +/* Quick subtraction from a power of 2, replaces z. + Allocates if necessary; returns false in case this fails. */ +STATIC int s_qsub(mp_int z, mp_size p2); + +/* Return maximum k such that 2^k divides z. */ +STATIC int s_dp2k(mp_int z); + +/* Return k >= 0 such that z = 2^k, or -1 if there is no such k. */ +STATIC int s_isp2(mp_int z); + +/* Set z to 2^k. May allocate; returns false in case this fails. */ +STATIC int s_2expt(mp_int z, mp_small k); + +/* Normalize a and b for division, returns normalization constant */ +STATIC int s_norm(mp_int a, mp_int b); + +/* Compute constant mu for Barrett reduction, given modulus m, result + replaces z, m is untouched. */ +STATIC mp_result s_brmu(mp_int z, mp_int m); + +/* Reduce a modulo m, using Barrett's algorithm. */ +STATIC int s_reduce(mp_int x, mp_int m, mp_int mu, mp_int q1, mp_int q2); + +/* Modular exponentiation, using Barrett reduction */ +STATIC mp_result s_embar(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c); + +/* Unsigned magnitude division. Assumes |a| > |b|. Allocates temporaries; + overwrites a with quotient, b with remainder. */ +STATIC mp_result s_udiv_knuth(mp_int a, mp_int b); + +/* Compute the number of digits in radix r required to represent the given + value. Does not account for sign flags, terminators, etc. */ +STATIC int s_outlen(mp_int z, mp_size r); + +/* Guess how many digits of precision will be needed to represent a radix r + value of the specified number of digits. Returns a value guaranteed to be + no smaller than the actual number required. */ +STATIC mp_size s_inlen(int len, mp_size r); + +/* Convert a character to a digit value in radix r, or + -1 if out of range */ +STATIC int s_ch2val(char c, int r); + +/* Convert a digit value to a character */ +STATIC char s_val2ch(int v, int caps); + +/* Take 2's complement of a buffer in place */ +STATIC void s_2comp(unsigned char *buf, int len); + +/* Convert a value to binary, ignoring sign. On input, *limpos is the bound on + how many bytes should be written to buf; on output, *limpos is set to the + number of bytes actually written. */ +STATIC mp_result s_tobin(mp_int z, unsigned char *buf, int *limpos, int pad); + +#if DEBUG +/* Dump a representation of the mp_int to standard output */ +void s_print(char *tag, mp_int z); +void s_print_buf(char *tag, mp_digit *buf, mp_size num); +#endif + +mp_result mp_int_init(mp_int z) +{ + if (z == NULL) + return MP_BADARG; + + z->single = 0; + z->digits = &(z->single); + z->alloc = 1; + z->used = 1; + z->sign = MP_ZPOS; + + return MP_OK; +} + +mp_int mp_int_alloc(void) +{ + mp_int out = malloc(sizeof(mpz_t)); + + if (out != NULL) + mp_int_init(out); + + return out; +} + +mp_result mp_int_init_size(mp_int z, mp_size prec) +{ + CHECK(z != NULL); + + if (prec == 0) + prec = default_precision; + else if (prec == 1) + return mp_int_init(z); + else + prec = (mp_size) ROUND_PREC(prec); + + if ((MP_DIGITS(z) = s_alloc(prec)) == NULL) + return MP_MEMORY; + + z->digits[0] = 0; + MP_USED(z) = 1; + MP_ALLOC(z) = prec; + MP_SIGN(z) = MP_ZPOS; + + return MP_OK; +} + +mp_result mp_int_init_copy(mp_int z, mp_int old) +{ + mp_result res; + mp_size uold; + + CHECK(z != NULL && old != NULL); + + uold = MP_USED(old); + if (uold == 1) { + mp_int_init(z); + } + else { + mp_size target = MAX(uold, default_precision); + + if ((res = mp_int_init_size(z, target)) != MP_OK) + return res; + } + + MP_USED(z) = uold; + MP_SIGN(z) = MP_SIGN(old); + COPY(MP_DIGITS(old), MP_DIGITS(z), uold); + + return MP_OK; +} + +mp_result mp_int_init_value(mp_int z, mp_small value) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + return mp_int_init_copy(z, &vtmp); +} + +mp_result mp_int_init_uvalue(mp_int z, mp_usmall uvalue) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(uvalue)]; + + s_ufake(&vtmp, uvalue, vbuf); + return mp_int_init_copy(z, &vtmp); +} + +mp_result mp_int_set_value(mp_int z, mp_small value) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + return mp_int_copy(&vtmp, z); +} + +mp_result mp_int_set_uvalue(mp_int z, mp_usmall uvalue) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(uvalue)]; + + s_ufake(&vtmp, uvalue, vbuf); + return mp_int_copy(&vtmp, z); +} + +void mp_int_clear(mp_int z) +{ + if (z == NULL) + return; + + if (MP_DIGITS(z) != NULL) { + if (MP_DIGITS(z) != &(z->single)) + s_free(MP_DIGITS(z)); + + MP_DIGITS(z) = NULL; + } +} + +void mp_int_free(mp_int z) +{ + NRCHECK(z != NULL); + + mp_int_clear(z); + free(z); /* note: NOT s_free() */ +} + +mp_result mp_int_copy(mp_int a, mp_int c) +{ + CHECK(a != NULL && c != NULL); + + if (a != c) { + mp_size ua = MP_USED(a); + mp_digit *da, *dc; + + if (!s_pad(c, ua)) + return MP_MEMORY; + + da = MP_DIGITS(a); dc = MP_DIGITS(c); + COPY(da, dc, ua); + + MP_USED(c) = ua; + MP_SIGN(c) = MP_SIGN(a); + } + + return MP_OK; +} + +void mp_int_swap(mp_int a, mp_int c) +{ + if (a != c) { + mpz_t tmp = *a; + + *a = *c; + *c = tmp; + + if (MP_DIGITS(a) == &(c->single)) + MP_DIGITS(a) = &(a->single); + if (MP_DIGITS(c) == &(a->single)) + MP_DIGITS(c) = &(c->single); + } +} + +void mp_int_zero(mp_int z) +{ + NRCHECK(z != NULL); + + z->digits[0] = 0; + MP_USED(z) = 1; + MP_SIGN(z) = MP_ZPOS; +} + +mp_result mp_int_abs(mp_int a, mp_int c) +{ + mp_result res; + + CHECK(a != NULL && c != NULL); + + if ((res = mp_int_copy(a, c)) != MP_OK) + return res; + + MP_SIGN(c) = MP_ZPOS; + return MP_OK; +} + +mp_result mp_int_neg(mp_int a, mp_int c) +{ + mp_result res; + + CHECK(a != NULL && c != NULL); + + if ((res = mp_int_copy(a, c)) != MP_OK) + return res; + + if (CMPZ(c) != 0) + MP_SIGN(c) = 1 - MP_SIGN(a); + + return MP_OK; +} + +mp_result mp_int_add(mp_int a, mp_int b, mp_int c) +{ + mp_size ua, ub, uc, max; + + CHECK(a != NULL && b != NULL && c != NULL); + + ua = MP_USED(a); ub = MP_USED(b); uc = MP_USED(c); + max = MAX(ua, ub); + + if (MP_SIGN(a) == MP_SIGN(b)) { + /* Same sign -- add magnitudes, preserve sign of addends */ + mp_digit carry; + + if (!s_pad(c, max)) + return MP_MEMORY; + + carry = s_uadd(MP_DIGITS(a), MP_DIGITS(b), MP_DIGITS(c), ua, ub); + uc = max; + + if (carry) { + if (!s_pad(c, max + 1)) + return MP_MEMORY; + + c->digits[max] = carry; + ++uc; + } + + MP_USED(c) = uc; + MP_SIGN(c) = MP_SIGN(a); + + } + else { + /* Different signs -- subtract magnitudes, preserve sign of greater */ + mp_int x, y; + int cmp = s_ucmp(a, b); /* magnitude comparision, sign ignored */ + + /* Set x to max(a, b), y to min(a, b) to simplify later code. + A special case yields zero for equal magnitudes. + */ + if (cmp == 0) { + mp_int_zero(c); + return MP_OK; + } + else if (cmp < 0) { + x = b; y = a; + } + else { + x = a; y = b; + } + + if (!s_pad(c, MP_USED(x))) + return MP_MEMORY; + + /* Subtract smaller from larger */ + s_usub(MP_DIGITS(x), MP_DIGITS(y), MP_DIGITS(c), MP_USED(x), MP_USED(y)); + MP_USED(c) = MP_USED(x); + CLAMP(c); + + /* Give result the sign of the larger */ + MP_SIGN(c) = MP_SIGN(x); + } + + return MP_OK; +} + +mp_result mp_int_add_value(mp_int a, mp_small value, mp_int c) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + + return mp_int_add(a, &vtmp, c); +} + +mp_result mp_int_sub(mp_int a, mp_int b, mp_int c) +{ + mp_size ua, ub, uc, max; + + CHECK(a != NULL && b != NULL && c != NULL); + + ua = MP_USED(a); ub = MP_USED(b); uc = MP_USED(c); + max = MAX(ua, ub); + + if (MP_SIGN(a) != MP_SIGN(b)) { + /* Different signs -- add magnitudes and keep sign of a */ + mp_digit carry; + + if (!s_pad(c, max)) + return MP_MEMORY; + + carry = s_uadd(MP_DIGITS(a), MP_DIGITS(b), MP_DIGITS(c), ua, ub); + uc = max; + + if (carry) { + if (!s_pad(c, max + 1)) + return MP_MEMORY; + + c->digits[max] = carry; + ++uc; + } + + MP_USED(c) = uc; + MP_SIGN(c) = MP_SIGN(a); + + } + else { + /* Same signs -- subtract magnitudes */ + mp_int x, y; + mp_sign osign; + int cmp = s_ucmp(a, b); + + if (!s_pad(c, max)) + return MP_MEMORY; + + if (cmp >= 0) { + x = a; y = b; osign = MP_ZPOS; + } + else { + x = b; y = a; osign = MP_NEG; + } + + if (MP_SIGN(a) == MP_NEG && cmp != 0) + osign = 1 - osign; + + s_usub(MP_DIGITS(x), MP_DIGITS(y), MP_DIGITS(c), MP_USED(x), MP_USED(y)); + MP_USED(c) = MP_USED(x); + CLAMP(c); + + MP_SIGN(c) = osign; + } + + return MP_OK; +} + +mp_result mp_int_sub_value(mp_int a, mp_small value, mp_int c) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + + return mp_int_sub(a, &vtmp, c); +} + +mp_result mp_int_mul(mp_int a, mp_int b, mp_int c) +{ + mp_digit *out; + mp_size osize, ua, ub, p = 0; + mp_sign osign; + + CHECK(a != NULL && b != NULL && c != NULL); + + /* If either input is zero, we can shortcut multiplication */ + if (mp_int_compare_zero(a) == 0 || mp_int_compare_zero(b) == 0) { + mp_int_zero(c); + return MP_OK; + } + + /* Output is positive if inputs have same sign, otherwise negative */ + osign = (MP_SIGN(a) == MP_SIGN(b)) ? MP_ZPOS : MP_NEG; + + /* If the output is not identical to any of the inputs, we'll write the + results directly; otherwise, allocate a temporary space. */ + ua = MP_USED(a); ub = MP_USED(b); + osize = MAX(ua, ub); + osize = 4 * ((osize + 1) / 2); + + if (c == a || c == b) { + p = ROUND_PREC(osize); + p = MAX(p, default_precision); + + if ((out = s_alloc(p)) == NULL) + return MP_MEMORY; + } + else { + if (!s_pad(c, osize)) + return MP_MEMORY; + + out = MP_DIGITS(c); + } + ZERO(out, osize); + + if (!s_kmul(MP_DIGITS(a), MP_DIGITS(b), out, ua, ub)) + return MP_MEMORY; + + /* If we allocated a new buffer, get rid of whatever memory c was already + using, and fix up its fields to reflect that. + */ + if (out != MP_DIGITS(c)) { + if ((void *) MP_DIGITS(c) != (void *) c) + s_free(MP_DIGITS(c)); + MP_DIGITS(c) = out; + MP_ALLOC(c) = p; + } + + MP_USED(c) = osize; /* might not be true, but we'll fix it ... */ + CLAMP(c); /* ... right here */ + MP_SIGN(c) = osign; + + return MP_OK; +} + +mp_result mp_int_mul_value(mp_int a, mp_small value, mp_int c) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + + return mp_int_mul(a, &vtmp, c); +} + +mp_result mp_int_mul_pow2(mp_int a, mp_small p2, mp_int c) +{ + mp_result res; + CHECK(a != NULL && c != NULL && p2 >= 0); + + if ((res = mp_int_copy(a, c)) != MP_OK) + return res; + + if (s_qmul(c, (mp_size) p2)) + return MP_OK; + else + return MP_MEMORY; +} + +mp_result mp_int_sqr(mp_int a, mp_int c) +{ + mp_digit *out; + mp_size osize, p = 0; + + CHECK(a != NULL && c != NULL); + + /* Get a temporary buffer big enough to hold the result */ + osize = (mp_size) 4 * ((MP_USED(a) + 1) / 2); + if (a == c) { + p = ROUND_PREC(osize); + p = MAX(p, default_precision); + + if ((out = s_alloc(p)) == NULL) + return MP_MEMORY; + } + else { + if (!s_pad(c, osize)) + return MP_MEMORY; + + out = MP_DIGITS(c); + } + ZERO(out, osize); + + s_ksqr(MP_DIGITS(a), out, MP_USED(a)); + + /* Get rid of whatever memory c was already using, and fix up its fields to + reflect the new digit array it's using + */ + if (out != MP_DIGITS(c)) { + if ((void *) MP_DIGITS(c) != (void *) c) + s_free(MP_DIGITS(c)); + MP_DIGITS(c) = out; + MP_ALLOC(c) = p; + } + + MP_USED(c) = osize; /* might not be true, but we'll fix it ... */ + CLAMP(c); /* ... right here */ + MP_SIGN(c) = MP_ZPOS; + + return MP_OK; +} + +mp_result mp_int_div(mp_int a, mp_int b, mp_int q, mp_int r) +{ + int cmp, lg; + mp_result res = MP_OK; + mp_int qout, rout; + mp_sign sa = MP_SIGN(a), sb = MP_SIGN(b); + DECLARE_TEMP(2); + + CHECK(a != NULL && b != NULL && q != r); + + if (CMPZ(b) == 0) + return MP_UNDEF; + else if ((cmp = s_ucmp(a, b)) < 0) { + /* If |a| < |b|, no division is required: + q = 0, r = a + */ + if (r && (res = mp_int_copy(a, r)) != MP_OK) + return res; + + if (q) + mp_int_zero(q); + + return MP_OK; + } + else if (cmp == 0) { + /* If |a| = |b|, no division is required: + q = 1 or -1, r = 0 + */ + if (r) + mp_int_zero(r); + + if (q) { + mp_int_zero(q); + q->digits[0] = 1; + + if (sa != sb) + MP_SIGN(q) = MP_NEG; + } + + return MP_OK; + } + + /* When |a| > |b|, real division is required. We need someplace to store + quotient and remainder, but q and r are allowed to be NULL or to overlap + with the inputs. + */ + if ((lg = s_isp2(b)) < 0) { + if (q && b != q) { + if ((res = mp_int_copy(a, q)) != MP_OK) + goto CLEANUP; + else + qout = q; + } + else { + qout = LAST_TEMP(); + SETUP(mp_int_init_copy(LAST_TEMP(), a)); + } + + if (r && a != r) { + if ((res = mp_int_copy(b, r)) != MP_OK) + goto CLEANUP; + else + rout = r; + } + else { + rout = LAST_TEMP(); + SETUP(mp_int_init_copy(LAST_TEMP(), b)); + } + + if ((res = s_udiv_knuth(qout, rout)) != MP_OK) goto CLEANUP; + } + else { + if (q && (res = mp_int_copy(a, q)) != MP_OK) goto CLEANUP; + if (r && (res = mp_int_copy(a, r)) != MP_OK) goto CLEANUP; + + if (q) s_qdiv(q, (mp_size) lg); qout = q; + if (r) s_qmod(r, (mp_size) lg); rout = r; + } + + /* Recompute signs for output */ + if (rout) { + MP_SIGN(rout) = sa; + if (CMPZ(rout) == 0) + MP_SIGN(rout) = MP_ZPOS; + } + if (qout) { + MP_SIGN(qout) = (sa == sb) ? MP_ZPOS : MP_NEG; + if (CMPZ(qout) == 0) + MP_SIGN(qout) = MP_ZPOS; + } + + if (q && (res = mp_int_copy(qout, q)) != MP_OK) goto CLEANUP; + if (r && (res = mp_int_copy(rout, r)) != MP_OK) goto CLEANUP; + + CLEANUP_TEMP(); + return res; +} + +mp_result mp_int_mod(mp_int a, mp_int m, mp_int c) +{ + mp_result res; + mpz_t tmp; + mp_int out; + + if (m == c) { + mp_int_init(&tmp); + out = &tmp; + } + else { + out = c; + } + + if ((res = mp_int_div(a, m, NULL, out)) != MP_OK) + goto CLEANUP; + + if (CMPZ(out) < 0) + res = mp_int_add(out, m, c); + else + res = mp_int_copy(out, c); + + CLEANUP: + if (out != c) + mp_int_clear(&tmp); + + return res; +} + +mp_result mp_int_div_value(mp_int a, mp_small value, mp_int q, mp_small *r) +{ + mpz_t vtmp, rtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + mp_result res; + + mp_int_init(&rtmp); + s_fake(&vtmp, value, vbuf); + + if ((res = mp_int_div(a, &vtmp, q, &rtmp)) != MP_OK) + goto CLEANUP; + + if (r) + (void) mp_int_to_int(&rtmp, r); /* can't fail */ + + CLEANUP: + mp_int_clear(&rtmp); + return res; +} + +mp_result mp_int_div_pow2(mp_int a, mp_small p2, mp_int q, mp_int r) +{ + mp_result res = MP_OK; + + CHECK(a != NULL && p2 >= 0 && q != r); + + if (q != NULL && (res = mp_int_copy(a, q)) == MP_OK) + s_qdiv(q, (mp_size) p2); + + if (res == MP_OK && r != NULL && (res = mp_int_copy(a, r)) == MP_OK) + s_qmod(r, (mp_size) p2); + + return res; +} + +mp_result mp_int_expt(mp_int a, mp_small b, mp_int c) +{ + mpz_t t; + mp_result res; + unsigned int v = abs(b); + + CHECK(c != NULL); + if (b < 0) + return MP_RANGE; + + if ((res = mp_int_init_copy(&t, a)) != MP_OK) + return res; + + (void) mp_int_set_value(c, 1); + while (v != 0) { + if (v & 1) { + if ((res = mp_int_mul(c, &t, c)) != MP_OK) + goto CLEANUP; + } + + v >>= 1; + if (v == 0) break; + + if ((res = mp_int_sqr(&t, &t)) != MP_OK) + goto CLEANUP; + } + + CLEANUP: + mp_int_clear(&t); + return res; +} + +mp_result mp_int_expt_value(mp_small a, mp_small b, mp_int c) +{ + mpz_t t; + mp_result res; + unsigned int v = abs(b); + + CHECK(c != NULL); + if (b < 0) + return MP_RANGE; + + if ((res = mp_int_init_value(&t, a)) != MP_OK) + return res; + + (void) mp_int_set_value(c, 1); + while (v != 0) { + if (v & 1) { + if ((res = mp_int_mul(c, &t, c)) != MP_OK) + goto CLEANUP; + } + + v >>= 1; + if (v == 0) break; + + if ((res = mp_int_sqr(&t, &t)) != MP_OK) + goto CLEANUP; + } + + CLEANUP: + mp_int_clear(&t); + return res; +} + +mp_result mp_int_expt_full(mp_int a, mp_int b, mp_int c) +{ + mpz_t t; + mp_result res; + unsigned ix, jx; + + CHECK(a != NULL && b != NULL && c != NULL); + if (MP_SIGN(b) == MP_NEG) + return MP_RANGE; + + if ((res = mp_int_init_copy(&t, a)) != MP_OK) + return res; + + (void) mp_int_set_value(c, 1); + for (ix = 0; ix < MP_USED(b); ++ix) { + mp_digit d = b->digits[ix]; + + for (jx = 0; jx < MP_DIGIT_BIT; ++jx) { + if (d & 1) { + if ((res = mp_int_mul(c, &t, c)) != MP_OK) + goto CLEANUP; + } + + d >>= 1; + if (d == 0 && ix + 1 == MP_USED(b)) + break; + if ((res = mp_int_sqr(&t, &t)) != MP_OK) + goto CLEANUP; + } + } + + CLEANUP: + mp_int_clear(&t); + return res; +} + +int mp_int_compare(mp_int a, mp_int b) +{ + mp_sign sa; + + CHECK(a != NULL && b != NULL); + + sa = MP_SIGN(a); + if (sa == MP_SIGN(b)) { + int cmp = s_ucmp(a, b); + + /* If they're both zero or positive, the normal comparison applies; if both + negative, the sense is reversed. */ + if (sa == MP_ZPOS) + return cmp; + else + return -cmp; + + } + else { + if (sa == MP_ZPOS) + return 1; + else + return -1; + } +} + +int mp_int_compare_unsigned(mp_int a, mp_int b) +{ + NRCHECK(a != NULL && b != NULL); + + return s_ucmp(a, b); +} + +int mp_int_compare_zero(mp_int z) +{ + NRCHECK(z != NULL); + + if (MP_USED(z) == 1 && z->digits[0] == 0) + return 0; + else if (MP_SIGN(z) == MP_ZPOS) + return 1; + else + return -1; +} + +int mp_int_compare_value(mp_int z, mp_small value) +{ + mp_sign vsign = (value < 0) ? MP_NEG : MP_ZPOS; + int cmp; + + CHECK(z != NULL); + + if (vsign == MP_SIGN(z)) { + cmp = s_vcmp(z, value); + + return (vsign == MP_ZPOS) ? cmp : -cmp; + } + else { + return (value < 0) ? 1 : -1; + } +} + +int mp_int_compare_uvalue(mp_int z, mp_usmall uv) +{ + CHECK(z != NULL); + + if (MP_SIGN(z) == MP_NEG) + return -1; + else + return s_uvcmp(z, uv); +} + +mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m, mp_int c) +{ + mp_result res; + mp_size um; + mp_int s; + DECLARE_TEMP(3); + + CHECK(a != NULL && b != NULL && c != NULL && m != NULL); + + /* Zero moduli and negative exponents are not considered. */ + if (CMPZ(m) == 0) + return MP_UNDEF; + if (CMPZ(b) < 0) + return MP_RANGE; + + um = MP_USED(m); + SETUP(mp_int_init_size(TEMP(0), 2 * um)); + SETUP(mp_int_init_size(TEMP(1), 2 * um)); + + if (c == b || c == m) { + SETUP(mp_int_init_size(TEMP(2), 2 * um)); + s = TEMP(2); + } + else { + s = c; + } + + if ((res = mp_int_mod(a, m, TEMP(0))) != MP_OK) goto CLEANUP; + + if ((res = s_brmu(TEMP(1), m)) != MP_OK) goto CLEANUP; + + if ((res = s_embar(TEMP(0), b, m, TEMP(1), s)) != MP_OK) + goto CLEANUP; + + res = mp_int_copy(s, c); + + CLEANUP_TEMP(); + return res; +} + +mp_result mp_int_exptmod_evalue(mp_int a, mp_small value, mp_int m, mp_int c) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + + return mp_int_exptmod(a, &vtmp, m, c); +} + +mp_result mp_int_exptmod_bvalue(mp_small value, mp_int b, + mp_int m, mp_int c) +{ + mpz_t vtmp; + mp_digit vbuf[MP_VALUE_DIGITS(value)]; + + s_fake(&vtmp, value, vbuf); + + return mp_int_exptmod(&vtmp, b, m, c); +} + +mp_result mp_int_exptmod_known(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c) +{ + mp_result res; + mp_size um; + mp_int s; + DECLARE_TEMP(2); + + CHECK(a && b && m && c); + + /* Zero moduli and negative exponents are not considered. */ + if (CMPZ(m) == 0) + return MP_UNDEF; + if (CMPZ(b) < 0) + return MP_RANGE; + + um = MP_USED(m); + SETUP(mp_int_init_size(TEMP(0), 2 * um)); + + if (c == b || c == m) { + SETUP(mp_int_init_size(TEMP(1), 2 * um)); + s = TEMP(1); + } + else { + s = c; + } + + if ((res = mp_int_mod(a, m, TEMP(0))) != MP_OK) goto CLEANUP; + + if ((res = s_embar(TEMP(0), b, m, mu, s)) != MP_OK) + goto CLEANUP; + + res = mp_int_copy(s, c); + + CLEANUP_TEMP(); + return res; +} + +mp_result mp_int_redux_const(mp_int m, mp_int c) +{ + CHECK(m != NULL && c != NULL && m != c); + + return s_brmu(c, m); +} + +mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c) +{ + mp_result res; + mp_sign sa; + DECLARE_TEMP(2); + + CHECK(a != NULL && m != NULL && c != NULL); + + if (CMPZ(a) == 0 || CMPZ(m) <= 0) + return MP_RANGE; + + sa = MP_SIGN(a); /* need this for the result later */ + + for (last__ = 0; last__ < 2; ++last__) + mp_int_init(LAST_TEMP()); + + if ((res = mp_int_egcd(a, m, TEMP(0), TEMP(1), NULL)) != MP_OK) + goto CLEANUP; + + if (mp_int_compare_value(TEMP(0), 1) != 0) { + res = MP_UNDEF; + goto CLEANUP; + } + + /* It is first necessary to constrain the value to the proper range */ + if ((res = mp_int_mod(TEMP(1), m, TEMP(1))) != MP_OK) + goto CLEANUP; + + /* Now, if 'a' was originally negative, the value we have is actually the + magnitude of the negative representative; to get the positive value we + have to subtract from the modulus. Otherwise, the value is okay as it + stands. + */ + if (sa == MP_NEG) + res = mp_int_sub(m, TEMP(1), c); + else + res = mp_int_copy(TEMP(1), c); + + CLEANUP_TEMP(); + return res; +} + +/* Binary GCD algorithm due to Josef Stein, 1961 */ +mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c) +{ + int ca, cb, k = 0; + mpz_t u, v, t; + mp_result res; + + CHECK(a != NULL && b != NULL && c != NULL); + + ca = CMPZ(a); + cb = CMPZ(b); + if (ca == 0 && cb == 0) + return MP_UNDEF; + else if (ca == 0) + return mp_int_abs(b, c); + else if (cb == 0) + return mp_int_abs(a, c); + + mp_int_init(&t); + if ((res = mp_int_init_copy(&u, a)) != MP_OK) + goto U; + if ((res = mp_int_init_copy(&v, b)) != MP_OK) + goto V; + + MP_SIGN(&u) = MP_ZPOS; MP_SIGN(&v) = MP_ZPOS; + + { /* Divide out common factors of 2 from u and v */ + int div2_u = s_dp2k(&u), div2_v = s_dp2k(&v); + + k = MIN(div2_u, div2_v); + s_qdiv(&u, (mp_size) k); + s_qdiv(&v, (mp_size) k); + } + + if (mp_int_is_odd(&u)) { + if ((res = mp_int_neg(&v, &t)) != MP_OK) + goto CLEANUP; + } + else { + if ((res = mp_int_copy(&u, &t)) != MP_OK) + goto CLEANUP; + } + + for (;;) { + s_qdiv(&t, s_dp2k(&t)); + + if (CMPZ(&t) > 0) { + if ((res = mp_int_copy(&t, &u)) != MP_OK) + goto CLEANUP; + } + else { + if ((res = mp_int_neg(&t, &v)) != MP_OK) + goto CLEANUP; + } + + if ((res = mp_int_sub(&u, &v, &t)) != MP_OK) + goto CLEANUP; + + if (CMPZ(&t) == 0) + break; + } + + if ((res = mp_int_abs(&u, c)) != MP_OK) + goto CLEANUP; + if (!s_qmul(c, (mp_size) k)) + res = MP_MEMORY; + + CLEANUP: + mp_int_clear(&v); + V: mp_int_clear(&u); + U: mp_int_clear(&t); + + return res; +} + +/* This is the binary GCD algorithm again, but this time we keep track of the + elementary matrix operations as we go, so we can get values x and y + satisfying c = ax + by. + */ +mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c, + mp_int x, mp_int y) +{ + int k, ca, cb; + mp_result res; + DECLARE_TEMP(8); + + CHECK(a != NULL && b != NULL && c != NULL && + (x != NULL || y != NULL)); + + ca = CMPZ(a); + cb = CMPZ(b); + if (ca == 0 && cb == 0) + return MP_UNDEF; + else if (ca == 0) { + if ((res = mp_int_abs(b, c)) != MP_OK) return res; + mp_int_zero(x); (void) mp_int_set_value(y, 1); return MP_OK; + } + else if (cb == 0) { + if ((res = mp_int_abs(a, c)) != MP_OK) return res; + (void) mp_int_set_value(x, 1); mp_int_zero(y); return MP_OK; + } + + /* Initialize temporaries: + A:0, B:1, C:2, D:3, u:4, v:5, ou:6, ov:7 */ + for (last__ = 0; last__ < 4; ++last__) + mp_int_init(LAST_TEMP()); + TEMP(0)->digits[0] = 1; + TEMP(3)->digits[0] = 1; + + SETUP(mp_int_init_copy(TEMP(4), a)); + SETUP(mp_int_init_copy(TEMP(5), b)); + + /* We will work with absolute values here */ + MP_SIGN(TEMP(4)) = MP_ZPOS; + MP_SIGN(TEMP(5)) = MP_ZPOS; + + { /* Divide out common factors of 2 from u and v */ + int div2_u = s_dp2k(TEMP(4)), div2_v = s_dp2k(TEMP(5)); + + k = MIN(div2_u, div2_v); + s_qdiv(TEMP(4), k); + s_qdiv(TEMP(5), k); + } + + SETUP(mp_int_init_copy(TEMP(6), TEMP(4))); + SETUP(mp_int_init_copy(TEMP(7), TEMP(5))); + + for (;;) { + while (mp_int_is_even(TEMP(4))) { + s_qdiv(TEMP(4), 1); + + if (mp_int_is_odd(TEMP(0)) || mp_int_is_odd(TEMP(1))) { + if ((res = mp_int_add(TEMP(0), TEMP(7), TEMP(0))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_sub(TEMP(1), TEMP(6), TEMP(1))) != MP_OK) + goto CLEANUP; + } + + s_qdiv(TEMP(0), 1); + s_qdiv(TEMP(1), 1); + } + + while (mp_int_is_even(TEMP(5))) { + s_qdiv(TEMP(5), 1); + + if (mp_int_is_odd(TEMP(2)) || mp_int_is_odd(TEMP(3))) { + if ((res = mp_int_add(TEMP(2), TEMP(7), TEMP(2))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_sub(TEMP(3), TEMP(6), TEMP(3))) != MP_OK) + goto CLEANUP; + } + + s_qdiv(TEMP(2), 1); + s_qdiv(TEMP(3), 1); + } + + if (mp_int_compare(TEMP(4), TEMP(5)) >= 0) { + if ((res = mp_int_sub(TEMP(4), TEMP(5), TEMP(4))) != MP_OK) goto CLEANUP; + if ((res = mp_int_sub(TEMP(0), TEMP(2), TEMP(0))) != MP_OK) goto CLEANUP; + if ((res = mp_int_sub(TEMP(1), TEMP(3), TEMP(1))) != MP_OK) goto CLEANUP; + } + else { + if ((res = mp_int_sub(TEMP(5), TEMP(4), TEMP(5))) != MP_OK) goto CLEANUP; + if ((res = mp_int_sub(TEMP(2), TEMP(0), TEMP(2))) != MP_OK) goto CLEANUP; + if ((res = mp_int_sub(TEMP(3), TEMP(1), TEMP(3))) != MP_OK) goto CLEANUP; + } + + if (CMPZ(TEMP(4)) == 0) { + if (x && (res = mp_int_copy(TEMP(2), x)) != MP_OK) goto CLEANUP; + if (y && (res = mp_int_copy(TEMP(3), y)) != MP_OK) goto CLEANUP; + if (c) { + if (!s_qmul(TEMP(5), k)) { + res = MP_MEMORY; + goto CLEANUP; + } + + res = mp_int_copy(TEMP(5), c); + } + + break; + } + } + + CLEANUP_TEMP(); + return res; +} + +mp_result mp_int_lcm(mp_int a, mp_int b, mp_int c) +{ + mpz_t lcm; + mp_result res; + + CHECK(a != NULL && b != NULL && c != NULL); + + /* Since a * b = gcd(a, b) * lcm(a, b), we can compute + lcm(a, b) = (a / gcd(a, b)) * b. + + This formulation insures everything works even if the input + variables share space. + */ + if ((res = mp_int_init(&lcm)) != MP_OK) + return res; + if ((res = mp_int_gcd(a, b, &lcm)) != MP_OK) + goto CLEANUP; + if ((res = mp_int_div(a, &lcm, &lcm, NULL)) != MP_OK) + goto CLEANUP; + if ((res = mp_int_mul(&lcm, b, &lcm)) != MP_OK) + goto CLEANUP; + + res = mp_int_copy(&lcm, c); + + CLEANUP: + mp_int_clear(&lcm); + + return res; +} + +int mp_int_divisible_value(mp_int a, mp_small v) +{ + mp_small rem = 0; + + if (mp_int_div_value(a, v, NULL, &rem) != MP_OK) + return 0; + + return rem == 0; +} + +int mp_int_is_pow2(mp_int z) +{ + CHECK(z != NULL); + + return s_isp2(z); +} + +/* Implementation of Newton's root finding method, based loosely on a patch + contributed by Hal Finkel + modified by M. J. Fromberger. + */ +mp_result mp_int_root(mp_int a, mp_small b, mp_int c) +{ + mp_result res = MP_OK; + int flips = 0; + DECLARE_TEMP(5); + + CHECK(a != NULL && c != NULL && b > 0); + + if (b == 1) { + return mp_int_copy(a, c); + } + if (MP_SIGN(a) == MP_NEG) { + if (b % 2 == 0) + return MP_UNDEF; /* root does not exist for negative a with even b */ + else + flips = 1; + } + + SETUP(mp_int_init_copy(LAST_TEMP(), a)); + SETUP(mp_int_init_copy(LAST_TEMP(), a)); + SETUP(mp_int_init(LAST_TEMP())); + SETUP(mp_int_init(LAST_TEMP())); + SETUP(mp_int_init(LAST_TEMP())); + + (void) mp_int_abs(TEMP(0), TEMP(0)); + (void) mp_int_abs(TEMP(1), TEMP(1)); + + for (;;) { + if ((res = mp_int_expt(TEMP(1), b, TEMP(2))) != MP_OK) + goto CLEANUP; + + if (mp_int_compare_unsigned(TEMP(2), TEMP(0)) <= 0) + break; + + if ((res = mp_int_sub(TEMP(2), TEMP(0), TEMP(2))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_expt(TEMP(1), b - 1, TEMP(3))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_mul_value(TEMP(3), b, TEMP(3))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_div(TEMP(2), TEMP(3), TEMP(4), NULL)) != MP_OK) + goto CLEANUP; + if ((res = mp_int_sub(TEMP(1), TEMP(4), TEMP(4))) != MP_OK) + goto CLEANUP; + + if (mp_int_compare_unsigned(TEMP(1), TEMP(4)) == 0) { + if ((res = mp_int_sub_value(TEMP(4), 1, TEMP(4))) != MP_OK) + goto CLEANUP; + } + if ((res = mp_int_copy(TEMP(4), TEMP(1))) != MP_OK) + goto CLEANUP; + } + + if ((res = mp_int_copy(TEMP(1), c)) != MP_OK) + goto CLEANUP; + + /* If the original value of a was negative, flip the output sign. */ + if (flips) + (void) mp_int_neg(c, c); /* cannot fail */ + + CLEANUP_TEMP(); + return res; +} + +mp_result mp_int_to_int(mp_int z, mp_small *out) +{ + mp_usmall uv = 0; + mp_size uz; + mp_digit *dz; + mp_sign sz; + + CHECK(z != NULL); + + /* Make sure the value is representable as a small integer */ + sz = MP_SIGN(z); + if ((sz == MP_ZPOS && mp_int_compare_value(z, MP_SMALL_MAX) > 0) || + mp_int_compare_value(z, MP_SMALL_MIN) < 0) + return MP_RANGE; + + uz = MP_USED(z); + dz = MP_DIGITS(z) + uz - 1; + + while (uz > 0) { + uv <<= MP_DIGIT_BIT/2; + uv = (uv << (MP_DIGIT_BIT/2)) | *dz--; + --uz; + } + + if (out) + *out = (sz == MP_NEG) ? -(mp_small)uv : (mp_small)uv; + + return MP_OK; +} + +mp_result mp_int_to_uint(mp_int z, mp_usmall *out) +{ + mp_usmall uv = 0; + mp_size uz; + mp_digit *dz; + mp_sign sz; + + CHECK(z != NULL); + + /* Make sure the value is representable as an unsigned small integer */ + sz = MP_SIGN(z); + if (sz == MP_NEG || mp_int_compare_uvalue(z, MP_USMALL_MAX) > 0) + return MP_RANGE; + + uz = MP_USED(z); + dz = MP_DIGITS(z) + uz - 1; + + while (uz > 0) { + uv <<= MP_DIGIT_BIT/2; + uv = (uv << (MP_DIGIT_BIT/2)) | *dz--; + --uz; + } + + if (out) + *out = uv; + + return MP_OK; +} + +mp_result mp_int_to_string(mp_int z, mp_size radix, + char *str, int limit) +{ + mp_result res; + int cmp = 0; + + CHECK(z != NULL && str != NULL && limit >= 2); + + if (radix < MP_MIN_RADIX || radix > MP_MAX_RADIX) + return MP_RANGE; + + if (CMPZ(z) == 0) { + *str++ = s_val2ch(0, 1); + } + else { + mpz_t tmp; + char *h, *t; + + if ((res = mp_int_init_copy(&tmp, z)) != MP_OK) + return res; + + if (MP_SIGN(z) == MP_NEG) { + *str++ = '-'; + --limit; + } + h = str; + + /* Generate digits in reverse order until finished or limit reached */ + for (/* */; limit > 0; --limit) { + mp_digit d; + + if ((cmp = CMPZ(&tmp)) == 0) + break; + + d = s_ddiv(&tmp, (mp_digit)radix); + *str++ = s_val2ch(d, 1); + } + t = str - 1; + + /* Put digits back in correct output order */ + while (h < t) { + char tc = *h; + *h++ = *t; + *t-- = tc; + } + + mp_int_clear(&tmp); + } + + *str = '\0'; + if (cmp == 0) + return MP_OK; + else + return MP_TRUNC; +} + +mp_result mp_int_string_len(mp_int z, mp_size radix) +{ + int len; + + CHECK(z != NULL); + + if (radix < MP_MIN_RADIX || radix > MP_MAX_RADIX) + return MP_RANGE; + + len = s_outlen(z, radix) + 1; /* for terminator */ + + /* Allow for sign marker on negatives */ + if (MP_SIGN(z) == MP_NEG) + len += 1; + + return len; +} + +/* Read zero-terminated string into z */ +mp_result mp_int_read_string(mp_int z, mp_size radix, const char *str) +{ + return mp_int_read_cstring(z, radix, str, NULL); +} + +mp_result mp_int_read_cstring(mp_int z, mp_size radix, const char *str, char **end) +{ + int ch; + + CHECK(z != NULL && str != NULL); + + if (radix < MP_MIN_RADIX || radix > MP_MAX_RADIX) + return MP_RANGE; + + /* Skip leading whitespace */ + while (isspace((int)*str)) + ++str; + + /* Handle leading sign tag (+/-, positive default) */ + switch (*str) { + case '-': + MP_SIGN(z) = MP_NEG; + ++str; + break; + case '+': + ++str; /* fallthrough */ + default: + MP_SIGN(z) = MP_ZPOS; + break; + } + + /* Skip leading zeroes */ + while ((ch = s_ch2val(*str, radix)) == 0) + ++str; + + /* Make sure there is enough space for the value */ + if (!s_pad(z, s_inlen(strlen(str), radix))) + return MP_MEMORY; + + MP_USED(z) = 1; z->digits[0] = 0; + + while (*str != '\0' && ((ch = s_ch2val(*str, radix)) >= 0)) { + s_dmul(z, (mp_digit)radix); + s_dadd(z, (mp_digit)ch); + ++str; + } + + CLAMP(z); + + /* Override sign for zero, even if negative specified. */ + if (CMPZ(z) == 0) + MP_SIGN(z) = MP_ZPOS; + + if (end != NULL) + *end = (char *)str; + + /* Return a truncation error if the string has unprocessed characters + remaining, so the caller can tell if the whole string was done */ + if (*str != '\0') + return MP_TRUNC; + else + return MP_OK; +} + +mp_result mp_int_count_bits(mp_int z) +{ + mp_size nbits = 0, uz; + mp_digit d; + + CHECK(z != NULL); + + uz = MP_USED(z); + if (uz == 1 && z->digits[0] == 0) + return 1; + + --uz; + nbits = uz * MP_DIGIT_BIT; + d = z->digits[uz]; + + while (d != 0) { + d >>= 1; + ++nbits; + } + + return nbits; +} + +mp_result mp_int_to_binary(mp_int z, unsigned char *buf, int limit) +{ + static const int PAD_FOR_2C = 1; + + mp_result res; + int limpos = limit; + + CHECK(z != NULL && buf != NULL); + + res = s_tobin(z, buf, &limpos, PAD_FOR_2C); + + if (MP_SIGN(z) == MP_NEG) + s_2comp(buf, limpos); + + return res; +} + +mp_result mp_int_read_binary(mp_int z, unsigned char *buf, int len) +{ + mp_size need, i; + unsigned char *tmp; + mp_digit *dz; + + CHECK(z != NULL && buf != NULL && len > 0); + + /* Figure out how many digits are needed to represent this value */ + need = ((len * CHAR_BIT) + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT; + if (!s_pad(z, need)) + return MP_MEMORY; + + mp_int_zero(z); + + /* If the high-order bit is set, take the 2's complement before reading the + value (it will be restored afterward) */ + if (buf[0] >> (CHAR_BIT - 1)) { + MP_SIGN(z) = MP_NEG; + s_2comp(buf, len); + } + + dz = MP_DIGITS(z); + for (tmp = buf, i = len; i > 0; --i, ++tmp) { + s_qmul(z, (mp_size) CHAR_BIT); + *dz |= *tmp; + } + + /* Restore 2's complement if we took it before */ + if (MP_SIGN(z) == MP_NEG) + s_2comp(buf, len); + + return MP_OK; +} + +mp_result mp_int_binary_len(mp_int z) +{ + mp_result res = mp_int_count_bits(z); + int bytes = mp_int_unsigned_len(z); + + if (res <= 0) + return res; + + bytes = (res + (CHAR_BIT - 1)) / CHAR_BIT; + + /* If the highest-order bit falls exactly on a byte boundary, we need to pad + with an extra byte so that the sign will be read correctly when reading it + back in. */ + if (bytes * CHAR_BIT == res) + ++bytes; + + return bytes; +} + +mp_result mp_int_to_unsigned(mp_int z, unsigned char *buf, int limit) +{ + static const int NO_PADDING = 0; + + CHECK(z != NULL && buf != NULL); + + return s_tobin(z, buf, &limit, NO_PADDING); +} + +mp_result mp_int_read_unsigned(mp_int z, unsigned char *buf, int len) +{ + mp_size need, i; + unsigned char *tmp; + + CHECK(z != NULL && buf != NULL && len > 0); + + /* Figure out how many digits are needed to represent this value */ + need = ((len * CHAR_BIT) + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT; + if (!s_pad(z, need)) + return MP_MEMORY; + + mp_int_zero(z); + + for (tmp = buf, i = len; i > 0; --i, ++tmp) { + (void) s_qmul(z, CHAR_BIT); + *MP_DIGITS(z) |= *tmp; + } + + return MP_OK; +} + +mp_result mp_int_unsigned_len(mp_int z) +{ + mp_result res = mp_int_count_bits(z); + int bytes; + + if (res <= 0) + return res; + + bytes = (res + (CHAR_BIT - 1)) / CHAR_BIT; + + return bytes; +} + +const char *mp_error_string(mp_result res) +{ + int ix; + if (res > 0) + return s_unknown_err; + + res = -res; + for (ix = 0; ix < res && s_error_msg[ix] != NULL; ++ix) + ; + + if (s_error_msg[ix] != NULL) + return s_error_msg[ix]; + else + return s_unknown_err; +} + +/*------------------------------------------------------------------------*/ +/* Private functions for internal use. These make assumptions. */ + +STATIC mp_digit *s_alloc(mp_size num) +{ + mp_digit *out = malloc(num * sizeof(mp_digit)); + + assert(out != NULL); /* for debugging */ +#if DEBUG > 1 + { + mp_digit v = (mp_digit) 0xdeadbeef; + int ix; + + for (ix = 0; ix < num; ++ix) + out[ix] = v; + } +#endif + + return out; +} + +STATIC mp_digit *s_realloc(mp_digit *old, mp_size osize, mp_size nsize) +{ +#if DEBUG > 1 + mp_digit *new = s_alloc(nsize); + int ix; + + for (ix = 0; ix < nsize; ++ix) + new[ix] = (mp_digit) 0xdeadbeef; + + memcpy(new, old, osize * sizeof(mp_digit)); +#else + mp_digit *new = realloc(old, nsize * sizeof(mp_digit)); + + assert(new != NULL); /* for debugging */ +#endif + return new; +} + +STATIC void s_free(void *ptr) +{ + free(ptr); +} + +STATIC int s_pad(mp_int z, mp_size min) +{ + if (MP_ALLOC(z) < min) { + mp_size nsize = ROUND_PREC(min); + mp_digit *tmp; + + if ((void *)z->digits == (void *)z) { + if ((tmp = s_alloc(nsize)) == NULL) + return 0; + + COPY(MP_DIGITS(z), tmp, MP_USED(z)); + } + else if ((tmp = s_realloc(MP_DIGITS(z), MP_ALLOC(z), nsize)) == NULL) + return 0; + + MP_DIGITS(z) = tmp; + MP_ALLOC(z) = nsize; + } + + return 1; +} + +/* Note: This will not work correctly when value == MP_SMALL_MIN */ +STATIC void s_fake(mp_int z, mp_small value, mp_digit vbuf[]) +{ + mp_usmall uv = (mp_usmall) (value < 0) ? -value : value; + s_ufake(z, uv, vbuf); + if (value < 0) + z->sign = MP_NEG; +} + +STATIC void s_ufake(mp_int z, mp_usmall value, mp_digit vbuf[]) +{ + mp_size ndig = (mp_size) s_uvpack(value, vbuf); + + z->used = ndig; + z->alloc = MP_VALUE_DIGITS(value); + z->sign = MP_ZPOS; + z->digits = vbuf; +} + +STATIC int s_cdig(mp_digit *da, mp_digit *db, mp_size len) +{ + mp_digit *dat = da + len - 1, *dbt = db + len - 1; + + for (/* */; len != 0; --len, --dat, --dbt) { + if (*dat > *dbt) + return 1; + else if (*dat < *dbt) + return -1; + } + + return 0; +} + +STATIC int s_uvpack(mp_usmall uv, mp_digit t[]) +{ + int ndig = 0; + + if (uv == 0) + t[ndig++] = 0; + else { + while (uv != 0) { + t[ndig++] = (mp_digit) uv; + uv >>= MP_DIGIT_BIT/2; + uv >>= MP_DIGIT_BIT/2; + } + } + + return ndig; +} + +STATIC int s_ucmp(mp_int a, mp_int b) +{ + mp_size ua = MP_USED(a), ub = MP_USED(b); + + if (ua > ub) + return 1; + else if (ub > ua) + return -1; + else + return s_cdig(MP_DIGITS(a), MP_DIGITS(b), ua); +} + +STATIC int s_vcmp(mp_int a, mp_small v) +{ + mp_usmall uv = (mp_usmall) (v < 0) ? -v : v; + return s_uvcmp(a, uv); +} + +STATIC int s_uvcmp(mp_int a, mp_usmall uv) +{ + mpz_t vtmp; + mp_digit vdig[MP_VALUE_DIGITS(uv)]; + + s_ufake(&vtmp, uv, vdig); + return s_ucmp(a, &vtmp); +} + +STATIC mp_digit s_uadd(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b) +{ + mp_size pos; + mp_word w = 0; + + /* Insure that da is the longer of the two to simplify later code */ + if (size_b > size_a) { + SWAP(mp_digit *, da, db); + SWAP(mp_size, size_a, size_b); + } + + /* Add corresponding digits until the shorter number runs out */ + for (pos = 0; pos < size_b; ++pos, ++da, ++db, ++dc) { + w = w + (mp_word) *da + (mp_word) *db; + *dc = LOWER_HALF(w); + w = UPPER_HALF(w); + } + + /* Propagate carries as far as necessary */ + for (/* */; pos < size_a; ++pos, ++da, ++dc) { + w = w + *da; + + *dc = LOWER_HALF(w); + w = UPPER_HALF(w); + } + + /* Return carry out */ + return (mp_digit)w; +} + +STATIC void s_usub(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b) +{ + mp_size pos; + mp_word w = 0; + + /* We assume that |a| >= |b| so this should definitely hold */ + assert(size_a >= size_b); + + /* Subtract corresponding digits and propagate borrow */ + for (pos = 0; pos < size_b; ++pos, ++da, ++db, ++dc) { + w = ((mp_word)MP_DIGIT_MAX + 1 + /* MP_RADIX */ + (mp_word)*da) - w - (mp_word)*db; + + *dc = LOWER_HALF(w); + w = (UPPER_HALF(w) == 0); + } + + /* Finish the subtraction for remaining upper digits of da */ + for (/* */; pos < size_a; ++pos, ++da, ++dc) { + w = ((mp_word)MP_DIGIT_MAX + 1 + /* MP_RADIX */ + (mp_word)*da) - w; + + *dc = LOWER_HALF(w); + w = (UPPER_HALF(w) == 0); + } + + /* If there is a borrow out at the end, it violates the precondition */ + assert(w == 0); +} + +STATIC int s_kmul(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b) +{ + mp_size bot_size; + + /* Make sure b is the smaller of the two input values */ + if (size_b > size_a) { + SWAP(mp_digit *, da, db); + SWAP(mp_size, size_a, size_b); + } + + /* Insure that the bottom is the larger half in an odd-length split; the code + below relies on this being true. + */ + bot_size = (size_a + 1) / 2; + + /* If the values are big enough to bother with recursion, use the Karatsuba + algorithm to compute the product; otherwise use the normal multiplication + algorithm + */ + if (multiply_threshold && + size_a >= multiply_threshold && + size_b > bot_size) { + + mp_digit *t1, *t2, *t3, carry; + + mp_digit *a_top = da + bot_size; + mp_digit *b_top = db + bot_size; + + mp_size at_size = size_a - bot_size; + mp_size bt_size = size_b - bot_size; + mp_size buf_size = 2 * bot_size; + + /* Do a single allocation for all three temporary buffers needed; each + buffer must be big enough to hold the product of two bottom halves, and + one buffer needs space for the completed product; twice the space is + plenty. + */ + if ((t1 = s_alloc(4 * buf_size)) == NULL) return 0; + t2 = t1 + buf_size; + t3 = t2 + buf_size; + ZERO(t1, 4 * buf_size); + + /* t1 and t2 are initially used as temporaries to compute the inner product + (a1 + a0)(b1 + b0) = a1b1 + a1b0 + a0b1 + a0b0 + */ + carry = s_uadd(da, a_top, t1, bot_size, at_size); /* t1 = a1 + a0 */ + t1[bot_size] = carry; + + carry = s_uadd(db, b_top, t2, bot_size, bt_size); /* t2 = b1 + b0 */ + t2[bot_size] = carry; + + (void) s_kmul(t1, t2, t3, bot_size + 1, bot_size + 1); /* t3 = t1 * t2 */ + + /* Now we'll get t1 = a0b0 and t2 = a1b1, and subtract them out so that + we're left with only the pieces we want: t3 = a1b0 + a0b1 + */ + ZERO(t1, buf_size); + ZERO(t2, buf_size); + (void) s_kmul(da, db, t1, bot_size, bot_size); /* t1 = a0 * b0 */ + (void) s_kmul(a_top, b_top, t2, at_size, bt_size); /* t2 = a1 * b1 */ + + /* Subtract out t1 and t2 to get the inner product */ + s_usub(t3, t1, t3, buf_size + 2, buf_size); + s_usub(t3, t2, t3, buf_size + 2, buf_size); + + /* Assemble the output value */ + COPY(t1, dc, buf_size); + carry = s_uadd(t3, dc + bot_size, dc + bot_size, + buf_size + 1, buf_size); + assert(carry == 0); + + carry = s_uadd(t2, dc + 2*bot_size, dc + 2*bot_size, + buf_size, buf_size); + assert(carry == 0); + + s_free(t1); /* note t2 and t3 are just internal pointers to t1 */ + } + else { + s_umul(da, db, dc, size_a, size_b); + } + + return 1; +} + +STATIC void s_umul(mp_digit *da, mp_digit *db, mp_digit *dc, + mp_size size_a, mp_size size_b) +{ + mp_size a, b; + mp_word w; + + for (a = 0; a < size_a; ++a, ++dc, ++da) { + mp_digit *dct = dc; + mp_digit *dbt = db; + + if (*da == 0) + continue; + + w = 0; + for (b = 0; b < size_b; ++b, ++dbt, ++dct) { + w = (mp_word)*da * (mp_word)*dbt + w + (mp_word)*dct; + + *dct = LOWER_HALF(w); + w = UPPER_HALF(w); + } + + *dct = (mp_digit)w; + } +} + +STATIC int s_ksqr(mp_digit *da, mp_digit *dc, mp_size size_a) +{ + if (multiply_threshold && size_a > multiply_threshold) { + mp_size bot_size = (size_a + 1) / 2; + mp_digit *a_top = da + bot_size; + mp_digit *t1, *t2, *t3, carry; + mp_size at_size = size_a - bot_size; + mp_size buf_size = 2 * bot_size; + + if ((t1 = s_alloc(4 * buf_size)) == NULL) return 0; + t2 = t1 + buf_size; + t3 = t2 + buf_size; + ZERO(t1, 4 * buf_size); + + (void) s_ksqr(da, t1, bot_size); /* t1 = a0 ^ 2 */ + (void) s_ksqr(a_top, t2, at_size); /* t2 = a1 ^ 2 */ + + (void) s_kmul(da, a_top, t3, bot_size, at_size); /* t3 = a0 * a1 */ + + /* Quick multiply t3 by 2, shifting left (can't overflow) */ + { + int i, top = bot_size + at_size; + mp_word w, save = 0; + + for (i = 0; i < top; ++i) { + w = t3[i]; + w = (w << 1) | save; + t3[i] = LOWER_HALF(w); + save = UPPER_HALF(w); + } + t3[i] = LOWER_HALF(save); + } + + /* Assemble the output value */ + COPY(t1, dc, 2 * bot_size); + carry = s_uadd(t3, dc + bot_size, dc + bot_size, + buf_size + 1, buf_size); + assert(carry == 0); + + carry = s_uadd(t2, dc + 2*bot_size, dc + 2*bot_size, + buf_size, buf_size); + assert(carry == 0); + + s_free(t1); /* note that t2 and t2 are internal pointers only */ + + } + else { + s_usqr(da, dc, size_a); + } + + return 1; +} + +STATIC void s_usqr(mp_digit *da, mp_digit *dc, mp_size size_a) +{ + mp_size i, j; + mp_word w; + + for (i = 0; i < size_a; ++i, dc += 2, ++da) { + mp_digit *dct = dc, *dat = da; + + if (*da == 0) + continue; + + /* Take care of the first digit, no rollover */ + w = (mp_word)*dat * (mp_word)*dat + (mp_word)*dct; + *dct = LOWER_HALF(w); + w = UPPER_HALF(w); + ++dat; ++dct; + + for (j = i + 1; j < size_a; ++j, ++dat, ++dct) { + mp_word t = (mp_word)*da * (mp_word)*dat; + mp_word u = w + (mp_word)*dct, ov = 0; + + /* Check if doubling t will overflow a word */ + if (HIGH_BIT_SET(t)) + ov = 1; + + w = t + t; + + /* Check if adding u to w will overflow a word */ + if (ADD_WILL_OVERFLOW(w, u)) + ov = 1; + + w += u; + + *dct = LOWER_HALF(w); + w = UPPER_HALF(w); + if (ov) { + w += MP_DIGIT_MAX; /* MP_RADIX */ + ++w; + } + } + + w = w + *dct; + *dct = (mp_digit)w; + while ((w = UPPER_HALF(w)) != 0) { + ++dct; w = w + *dct; + *dct = LOWER_HALF(w); + } + + assert(w == 0); + } +} + +STATIC void s_dadd(mp_int a, mp_digit b) +{ + mp_word w = 0; + mp_digit *da = MP_DIGITS(a); + mp_size ua = MP_USED(a); + + w = (mp_word)*da + b; + *da++ = LOWER_HALF(w); + w = UPPER_HALF(w); + + for (ua -= 1; ua > 0; --ua, ++da) { + w = (mp_word)*da + w; + + *da = LOWER_HALF(w); + w = UPPER_HALF(w); + } + + if (w) { + *da = (mp_digit)w; + MP_USED(a) += 1; + } +} + +STATIC void s_dmul(mp_int a, mp_digit b) +{ + mp_word w = 0; + mp_digit *da = MP_DIGITS(a); + mp_size ua = MP_USED(a); + + while (ua > 0) { + w = (mp_word)*da * b + w; + *da++ = LOWER_HALF(w); + w = UPPER_HALF(w); + --ua; + } + + if (w) { + *da = (mp_digit)w; + MP_USED(a) += 1; + } +} + +STATIC void s_dbmul(mp_digit *da, mp_digit b, mp_digit *dc, mp_size size_a) +{ + mp_word w = 0; + + while (size_a > 0) { + w = (mp_word)*da++ * (mp_word)b + w; + + *dc++ = LOWER_HALF(w); + w = UPPER_HALF(w); + --size_a; + } + + if (w) + *dc = LOWER_HALF(w); +} + +STATIC mp_digit s_ddiv(mp_int a, mp_digit b) +{ + mp_word w = 0, qdigit; + mp_size ua = MP_USED(a); + mp_digit *da = MP_DIGITS(a) + ua - 1; + + for (/* */; ua > 0; --ua, --da) { + w = (w << MP_DIGIT_BIT) | *da; + + if (w >= b) { + qdigit = w / b; + w = w % b; + } + else { + qdigit = 0; + } + + *da = (mp_digit)qdigit; + } + + CLAMP(a); + return (mp_digit)w; +} + +STATIC void s_qdiv(mp_int z, mp_size p2) +{ + mp_size ndig = p2 / MP_DIGIT_BIT, nbits = p2 % MP_DIGIT_BIT; + mp_size uz = MP_USED(z); + + if (ndig) { + mp_size mark; + mp_digit *to, *from; + + if (ndig >= uz) { + mp_int_zero(z); + return; + } + + to = MP_DIGITS(z); from = to + ndig; + + for (mark = ndig; mark < uz; ++mark) + *to++ = *from++; + + MP_USED(z) = uz - ndig; + } + + if (nbits) { + mp_digit d = 0, *dz, save; + mp_size up = MP_DIGIT_BIT - nbits; + + uz = MP_USED(z); + dz = MP_DIGITS(z) + uz - 1; + + for (/* */; uz > 0; --uz, --dz) { + save = *dz; + + *dz = (*dz >> nbits) | (d << up); + d = save; + } + + CLAMP(z); + } + + if (MP_USED(z) == 1 && z->digits[0] == 0) + MP_SIGN(z) = MP_ZPOS; +} + +STATIC void s_qmod(mp_int z, mp_size p2) +{ + mp_size start = p2 / MP_DIGIT_BIT + 1, rest = p2 % MP_DIGIT_BIT; + mp_size uz = MP_USED(z); + mp_digit mask = (1 << rest) - 1; + + if (start <= uz) { + MP_USED(z) = start; + z->digits[start - 1] &= mask; + CLAMP(z); + } +} + +STATIC int s_qmul(mp_int z, mp_size p2) +{ + mp_size uz, need, rest, extra, i; + mp_digit *from, *to, d; + + if (p2 == 0) + return 1; + + uz = MP_USED(z); + need = p2 / MP_DIGIT_BIT; rest = p2 % MP_DIGIT_BIT; + + /* Figure out if we need an extra digit at the top end; this occurs if the + topmost `rest' bits of the high-order digit of z are not zero, meaning + they will be shifted off the end if not preserved */ + extra = 0; + if (rest != 0) { + mp_digit *dz = MP_DIGITS(z) + uz - 1; + + if ((*dz >> (MP_DIGIT_BIT - rest)) != 0) + extra = 1; + } + + if (!s_pad(z, uz + need + extra)) + return 0; + + /* If we need to shift by whole digits, do that in one pass, then + to back and shift by partial digits. + */ + if (need > 0) { + from = MP_DIGITS(z) + uz - 1; + to = from + need; + + for (i = 0; i < uz; ++i) + *to-- = *from--; + + ZERO(MP_DIGITS(z), need); + uz += need; + } + + if (rest) { + d = 0; + for (i = need, from = MP_DIGITS(z) + need; i < uz; ++i, ++from) { + mp_digit save = *from; + + *from = (*from << rest) | (d >> (MP_DIGIT_BIT - rest)); + d = save; + } + + d >>= (MP_DIGIT_BIT - rest); + if (d != 0) { + *from = d; + uz += extra; + } + } + + MP_USED(z) = uz; + CLAMP(z); + + return 1; +} + +/* Compute z = 2^p2 - |z|; requires that 2^p2 >= |z| + The sign of the result is always zero/positive. + */ +STATIC int s_qsub(mp_int z, mp_size p2) +{ + mp_digit hi = (1 << (p2 % MP_DIGIT_BIT)), *zp; + mp_size tdig = (p2 / MP_DIGIT_BIT), pos; + mp_word w = 0; + + if (!s_pad(z, tdig + 1)) + return 0; + + for (pos = 0, zp = MP_DIGITS(z); pos < tdig; ++pos, ++zp) { + w = ((mp_word) MP_DIGIT_MAX + 1) - w - (mp_word)*zp; + + *zp = LOWER_HALF(w); + w = UPPER_HALF(w) ? 0 : 1; + } + + w = ((mp_word) MP_DIGIT_MAX + 1 + hi) - w - (mp_word)*zp; + *zp = LOWER_HALF(w); + + assert(UPPER_HALF(w) != 0); /* no borrow out should be possible */ + + MP_SIGN(z) = MP_ZPOS; + CLAMP(z); + + return 1; +} + +STATIC int s_dp2k(mp_int z) +{ + int k = 0; + mp_digit *dp = MP_DIGITS(z), d; + + if (MP_USED(z) == 1 && *dp == 0) + return 1; + + while (*dp == 0) { + k += MP_DIGIT_BIT; + ++dp; + } + + d = *dp; + while ((d & 1) == 0) { + d >>= 1; + ++k; + } + + return k; +} + +STATIC int s_isp2(mp_int z) +{ + mp_size uz = MP_USED(z), k = 0; + mp_digit *dz = MP_DIGITS(z), d; + + while (uz > 1) { + if (*dz++ != 0) + return -1; + k += MP_DIGIT_BIT; + --uz; + } + + d = *dz; + while (d > 1) { + if (d & 1) + return -1; + ++k; d >>= 1; + } + + return (int) k; +} + +STATIC int s_2expt(mp_int z, mp_small k) +{ + mp_size ndig, rest; + mp_digit *dz; + + ndig = (k + MP_DIGIT_BIT) / MP_DIGIT_BIT; + rest = k % MP_DIGIT_BIT; + + if (!s_pad(z, ndig)) + return 0; + + dz = MP_DIGITS(z); + ZERO(dz, ndig); + *(dz + ndig - 1) = (1 << rest); + MP_USED(z) = ndig; + + return 1; +} + +STATIC int s_norm(mp_int a, mp_int b) +{ + mp_digit d = b->digits[MP_USED(b) - 1]; + int k = 0; + + while (d < (mp_digit) (1 << (MP_DIGIT_BIT - 1))) { /* d < (MP_RADIX / 2) */ + d <<= 1; + ++k; + } + + /* These multiplications can't fail */ + if (k != 0) { + (void) s_qmul(a, (mp_size) k); + (void) s_qmul(b, (mp_size) k); + } + + return k; +} + +STATIC mp_result s_brmu(mp_int z, mp_int m) +{ + mp_size um = MP_USED(m) * 2; + + if (!s_pad(z, um)) + return MP_MEMORY; + + s_2expt(z, MP_DIGIT_BIT * um); + return mp_int_div(z, m, z, NULL); +} + +STATIC int s_reduce(mp_int x, mp_int m, mp_int mu, mp_int q1, mp_int q2) +{ + mp_size um = MP_USED(m), umb_p1, umb_m1; + + umb_p1 = (um + 1) * MP_DIGIT_BIT; + umb_m1 = (um - 1) * MP_DIGIT_BIT; + + if (mp_int_copy(x, q1) != MP_OK) + return 0; + + /* Compute q2 = floor((floor(x / b^(k-1)) * mu) / b^(k+1)) */ + s_qdiv(q1, umb_m1); + UMUL(q1, mu, q2); + s_qdiv(q2, umb_p1); + + /* Set x = x mod b^(k+1) */ + s_qmod(x, umb_p1); + + /* Now, q is a guess for the quotient a / m. + Compute x - q * m mod b^(k+1), replacing x. This may be off + by a factor of 2m, but no more than that. + */ + UMUL(q2, m, q1); + s_qmod(q1, umb_p1); + (void) mp_int_sub(x, q1, x); /* can't fail */ + + /* The result may be < 0; if it is, add b^(k+1) to pin it in the proper + range. */ + if ((CMPZ(x) < 0) && !s_qsub(x, umb_p1)) + return 0; + + /* If x > m, we need to back it off until it is in range. This will be + required at most twice. */ + if (mp_int_compare(x, m) >= 0) { + (void) mp_int_sub(x, m, x); + if (mp_int_compare(x, m) >= 0) + (void) mp_int_sub(x, m, x); + } + + /* At this point, x has been properly reduced. */ + return 1; +} + +/* Perform modular exponentiation using Barrett's method, where mu is the + reduction constant for m. Assumes a < m, b > 0. */ +STATIC mp_result s_embar(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c) +{ + mp_digit *db, *dbt, umu, d; + mp_result res; + DECLARE_TEMP(3); + + umu = MP_USED(mu); db = MP_DIGITS(b); dbt = db + MP_USED(b) - 1; + + while (last__ < 3) { + SETUP(mp_int_init_size(LAST_TEMP(), 4 * umu)); + ZERO(MP_DIGITS(TEMP(last__ - 1)), MP_ALLOC(TEMP(last__ - 1))); + } + + (void) mp_int_set_value(c, 1); + + /* Take care of low-order digits */ + while (db < dbt) { + int i; + + for (d = *db, i = MP_DIGIT_BIT; i > 0; --i, d >>= 1) { + if (d & 1) { + /* The use of a second temporary avoids allocation */ + UMUL(c, a, TEMP(0)); + if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) { + res = MP_MEMORY; goto CLEANUP; + } + mp_int_copy(TEMP(0), c); + } + + + USQR(a, TEMP(0)); + assert(MP_SIGN(TEMP(0)) == MP_ZPOS); + if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) { + res = MP_MEMORY; goto CLEANUP; + } + assert(MP_SIGN(TEMP(0)) == MP_ZPOS); + mp_int_copy(TEMP(0), a); + } + + ++db; + } + + /* Take care of highest-order digit */ + d = *dbt; + for (;;) { + if (d & 1) { + UMUL(c, a, TEMP(0)); + if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) { + res = MP_MEMORY; goto CLEANUP; + } + mp_int_copy(TEMP(0), c); + } + + d >>= 1; + if (!d) break; + + USQR(a, TEMP(0)); + if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) { + res = MP_MEMORY; goto CLEANUP; + } + (void) mp_int_copy(TEMP(0), a); + } + + CLEANUP_TEMP(); + return res; +} + +#if 0 +/* + The s_udiv function produces incorrect results. For example, with test + div:11141460315522012760862883825:48318382095:0,230584300062375935 + commenting out the function for now and using s_udiv_knuth instead. + STATIC mp_result s_udiv(mp_int a, mp_int b); +*/ +/* Precondition: a >= b and b > 0 + Postcondition: a' = a / b, b' = a % b + */ +STATIC mp_result s_udiv(mp_int a, mp_int b) +{ + mpz_t q, r, t; + mp_size ua, ub, qpos = 0; + mp_digit *da, btop; + mp_result res = MP_OK; + int k, skip = 0; + + /* Force signs to positive */ + MP_SIGN(a) = MP_ZPOS; + MP_SIGN(b) = MP_ZPOS; + + /* Normalize, per Knuth */ + k = s_norm(a, b); + + ua = MP_USED(a); ub = MP_USED(b); btop = b->digits[ub - 1]; + if ((res = mp_int_init_size(&q, ua)) != MP_OK) return res; + if ((res = mp_int_init_size(&t, ua + 1)) != MP_OK) goto CLEANUP; + + da = MP_DIGITS(a); + r.digits = da + ua - 1; /* The contents of r are shared with a */ + r.used = 1; + r.sign = MP_ZPOS; + r.alloc = MP_ALLOC(a); + ZERO(t.digits, t.alloc); + + /* Solve for quotient digits, store in q.digits in reverse order */ + while (r.digits >= da) { + assert(qpos <= q.alloc); + + if (s_ucmp(b, &r) > 0) { + r.digits -= 1; + r.used += 1; + + if (++skip > 1 && qpos > 0) + q.digits[qpos++] = 0; + + CLAMP(&r); + } + else { + mp_word pfx = r.digits[r.used - 1]; + mp_word qdigit; + + if (r.used > 1 && pfx < btop) { + pfx <<= MP_DIGIT_BIT / 2; + pfx <<= MP_DIGIT_BIT / 2; + pfx |= r.digits[r.used - 2]; + } + + qdigit = pfx / btop; + if (qdigit > MP_DIGIT_MAX) { + qdigit = MP_DIGIT_MAX; + } + + s_dbmul(MP_DIGITS(b), (mp_digit) qdigit, t.digits, ub); + t.used = ub + 1; CLAMP(&t); + while (s_ucmp(&t, &r) > 0) { + --qdigit; + (void) mp_int_sub(&t, b, &t); /* cannot fail */ + } + + s_usub(r.digits, t.digits, r.digits, r.used, t.used); + CLAMP(&r); + + q.digits[qpos++] = (mp_digit) qdigit; + ZERO(t.digits, t.used); + skip = 0; + } + } + + /* Put quotient digits in the correct order, and discard extra zeroes */ + q.used = qpos; + REV(mp_digit, q.digits, qpos); + CLAMP(&q); + + /* Denormalize the remainder */ + CLAMP(a); + if (k != 0) + s_qdiv(a, k); + + mp_int_copy(a, b); /* ok: 0 <= r < b */ + mp_int_copy(&q, a); /* ok: q <= a */ + + mp_int_clear(&t); + CLEANUP: + mp_int_clear(&q); + return res; +} +#endif + +/* Division of nonnegative integers + + This function implements division algorithm for unsigned multi-precision + integers. The algorithm is based on Algorithm D from Knuth's "The Art of + Computer Programming", 3rd ed. 1998, pg 272-273. + + We diverge from Knuth's algorithm in that we do not perform the subtraction + from the remainder until we have determined that we have the correct + quotient digit. This makes our algorithm less efficient that Knuth because + we might have to perform multiple multiplication and comparison steps before + the subtraction. The advantage is that it is easy to implement and ensure + correctness without worrying about underflow from the subtraction. + + inputs: u a n+m digit integer in base b (b is 2^MP_DIGIT_BIT) + v a n digit integer in base b (b is 2^MP_DIGIT_BIT) + n >= 1 + m >= 0 + outputs: u / v stored in u + u % v stored in v + */ +STATIC mp_result s_udiv_knuth(mp_int u, mp_int v) { + mpz_t q, r, t; + mp_result + res = MP_OK; + int k,j; + mp_size m,n; + + /* Force signs to positive */ + MP_SIGN(u) = MP_ZPOS; + MP_SIGN(v) = MP_ZPOS; + + /* Use simple division algorithm when v is only one digit long */ + if (MP_USED(v) == 1) { + mp_digit d, rem; + d = v->digits[0]; + rem = s_ddiv(u, d); + mp_int_set_value(v, rem); + return MP_OK; + } + + /************************************************************/ + /* Algorithm D */ + /************************************************************/ + /* The n and m variables are defined as used by Knuth. + u is an n digit number with digits u_{n-1}..u_0. + v is an n+m digit number with digits from v_{m+n-1}..v_0. + We require that n > 1 and m >= 0 */ + n = MP_USED(v); + m = MP_USED(u) - n; + assert(n > 1); + assert(m >= 0); + + /************************************************************/ + /* D1: Normalize. + The normalization step provides the necessary condition for Theorem B, + which states that the quotient estimate for q_j, call it qhat + + qhat = u_{j+n}u_{j+n-1} / v_{n-1} + + is bounded by + + qhat - 2 <= q_j <= qhat. + + That is, qhat is always greater than the actual quotient digit q, + and it is never more than two larger than the actual quotient digit. */ + k = s_norm(u, v); + + /* Extend size of u by one if needed. + + The algorithm begins with a value of u that has one more digit of input. + The normalization step sets u_{m+n}..u_0 = 2^k * u_{m+n-1}..u_0. If the + multiplication did not increase the number of digits of u, we need to add + a leading zero here. + */ + if (k == 0 || MP_USED(u) != m + n + 1) { + if (!s_pad(u, m+n+1)) + return MP_MEMORY; + u->digits[m+n] = 0; + u->used = m+n+1; + } + + /* Add a leading 0 to v. + + The multiplication in step D4 multiplies qhat * 0v_{n-1}..v_0. We need to + add the leading zero to v here to ensure that the multiplication will + produce the full n+1 digit result. */ + if (!s_pad(v, n+1)) return MP_MEMORY; v->digits[n] = 0; + + /* Initialize temporary variables q and t. + q allocates space for m+1 digits to store the quotient digits + t allocates space for n+1 digits to hold the result of q_j*v */ + if ((res = mp_int_init_size(&q, m + 1)) != MP_OK) return res; + if ((res = mp_int_init_size(&t, n + 1)) != MP_OK) goto CLEANUP; + + /************************************************************/ + /* D2: Initialize j */ + j = m; + r.digits = MP_DIGITS(u) + j; /* The contents of r are shared with u */ + r.used = n + 1; + r.sign = MP_ZPOS; + r.alloc = MP_ALLOC(u); + ZERO(t.digits, t.alloc); + + /* Calculate the m+1 digits of the quotient result */ + for (; j >= 0; j--) { + /************************************************************/ + /* D3: Calculate q' */ + /* r->digits is aligned to position j of the number u */ + mp_word pfx, qhat; + pfx = r.digits[n]; + pfx <<= MP_DIGIT_BIT / 2; + pfx <<= MP_DIGIT_BIT / 2; + pfx |= r.digits[n-1]; /* pfx = u_{j+n}{j+n-1} */ + + qhat = pfx / v->digits[n-1]; + /* Check to see if qhat > b, and decrease qhat if so. + Theorem B guarantess that qhat is at most 2 larger than the + actual value, so it is possible that qhat is greater than + the maximum value that will fit in a digit */ + if (qhat > MP_DIGIT_MAX) + qhat = MP_DIGIT_MAX; + + /************************************************************/ + /* D4,D5,D6: Multiply qhat * v and test for a correct value of q + + We proceed a bit different than the way described by Knuth. This way is + simpler but less efficent. Instead of doing the multiply and subtract + then checking for underflow, we first do the multiply of qhat * v and + see if it is larger than the current remainder r. If it is larger, we + decrease qhat by one and try again. We may need to decrease qhat one + more time before we get a value that is smaller than r. + + This way is less efficent than Knuth becuase we do more multiplies, but + we do not need to worry about underflow this way. */ + /* t = qhat * v */ + s_dbmul(MP_DIGITS(v), (mp_digit) qhat, t.digits, n+1); t.used = n + 1; + CLAMP(&t); + + /* Clamp r for the comparison. Comparisons do not like leading zeros. */ + CLAMP(&r); + if (s_ucmp(&t, &r) > 0) { /* would the remainder be negative? */ + qhat -= 1; /* try a smaller q */ + s_dbmul(MP_DIGITS(v), (mp_digit) qhat, t.digits, n+1); + t.used = n + 1; CLAMP(&t); + if (s_ucmp(&t, &r) > 0) { /* would the remainder be negative? */ + assert(qhat > 0); + qhat -= 1; /* try a smaller q */ + s_dbmul(MP_DIGITS(v), (mp_digit) qhat, t.digits, n+1); + t.used = n + 1; CLAMP(&t); + } + assert(s_ucmp(&t, &r) <= 0 && "The mathematics failed us."); + } + /* Unclamp r. The D algorithm expects r = u_{j+n}..u_j to always be n+1 + digits long. */ + r.used = n + 1; + + /************************************************************/ + /* D4: Multiply and subtract */ + /* note: The multiply was completed above so we only need to subtract here. + **/ + s_usub(r.digits, t.digits, r.digits, r.used, t.used); + + /************************************************************/ + /* D5: Test remainder */ + /* note: Not needed because we always check that qhat is the correct value + * before performing the subtract. + * Value cast to mp_digit to prevent warning, qhat has been clamped to MP_DIGIT_MAX */ + q.digits[j] = (mp_digit)qhat; + + /************************************************************/ + /* D6: Add back */ + /* note: Not needed because we always check that qhat is the correct value + * before performing the subtract. */ + + /************************************************************/ + /* D7: Loop on j */ + r.digits--; + ZERO(t.digits, t.alloc); + } + + /* Get rid of leading zeros in q */ + q.used = m + 1; + CLAMP(&q); + + /* Denormalize the remainder */ + CLAMP(u); /* use u here because the r.digits pointer is off-by-one */ + if (k != 0) + s_qdiv(u, k); + + mp_int_copy(u, v); /* ok: 0 <= r < v */ + mp_int_copy(&q, u); /* ok: q <= u */ + + mp_int_clear(&t); + CLEANUP: + mp_int_clear(&q); + return res; +} + +STATIC int s_outlen(mp_int z, mp_size r) +{ + mp_result bits; + double raw; + + assert(r >= MP_MIN_RADIX && r <= MP_MAX_RADIX); + + bits = mp_int_count_bits(z); + raw = (double)bits * s_log2[r]; + + return (int)(raw + 0.999999); +} + +STATIC mp_size s_inlen(int len, mp_size r) +{ + double raw = (double)len / s_log2[r]; + mp_size bits = (mp_size)(raw + 0.5); + + return (mp_size)((bits + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT) + 1; +} + +STATIC int s_ch2val(char c, int r) +{ + int out; + + if (isdigit((unsigned char) c)) + out = c - '0'; + else if (r > 10 && isalpha((unsigned char) c)) + out = toupper(c) - 'A' + 10; + else + return -1; + + return (out >= r) ? -1 : out; +} + +STATIC char s_val2ch(int v, int caps) +{ + assert(v >= 0); + + if (v < 10) + return v + '0'; + else { + char out = (v - 10) + 'a'; + + if (caps) + return toupper(out); + else + return out; + } +} + +STATIC void s_2comp(unsigned char *buf, int len) +{ + int i; + unsigned short s = 1; + + for (i = len - 1; i >= 0; --i) { + unsigned char c = ~buf[i]; + + s = c + s; + c = s & UCHAR_MAX; + s >>= CHAR_BIT; + + buf[i] = c; + } + + /* last carry out is ignored */ +} + +STATIC mp_result s_tobin(mp_int z, unsigned char *buf, int *limpos, int pad) +{ + mp_size uz; + mp_digit *dz; + int pos = 0, limit = *limpos; + + uz = MP_USED(z); dz = MP_DIGITS(z); + while (uz > 0 && pos < limit) { + mp_digit d = *dz++; + int i; + + for (i = sizeof(mp_digit); i > 0 && pos < limit; --i) { + buf[pos++] = (unsigned char)d; + d >>= CHAR_BIT; + + /* Don't write leading zeroes */ + if (d == 0 && uz == 1) + i = 0; /* exit loop without signaling truncation */ + } + + /* Detect truncation (loop exited with pos >= limit) */ + if (i > 0) break; + + --uz; + } + + if (pad != 0 && (buf[pos - 1] >> (CHAR_BIT - 1))) { + if (pos < limit) + buf[pos++] = 0; + else + uz = 1; + } + + /* Digits are in reverse order, fix that */ + REV(unsigned char, buf, pos); + + /* Return the number of bytes actually written */ + *limpos = pos; + + return (uz == 0) ? MP_OK : MP_TRUNC; +} + +#if DEBUG +void s_print(char *tag, mp_int z) +{ + int i; + + fprintf(stderr, "%s: %c ", tag, + (MP_SIGN(z) == MP_NEG) ? '-' : '+'); + + for (i = MP_USED(z) - 1; i >= 0; --i) + fprintf(stderr, "%0*X", (int)(MP_DIGIT_BIT / 4), z->digits[i]); + + fputc('\n', stderr); + +} + +void s_print_buf(char *tag, mp_digit *buf, mp_size num) +{ + int i; + + fprintf(stderr, "%s: ", tag); + + for (i = num - 1; i >= 0; --i) + fprintf(stderr, "%0*X", (int)(MP_DIGIT_BIT / 4), buf[i]); + + fputc('\n', stderr); +} +#endif + +/* Here there be dragons */ Index: lib/Analysis/isl/imath/imrat.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath/imrat.h @@ -0,0 +1,124 @@ +/* + Name: imrat.h + Purpose: Arbitrary precision rational arithmetic routines. + Author: M. J. Fromberger + + Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#ifndef IMRAT_H_ +#define IMRAT_H_ + +#include "imath.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mpq { + mpz_t num; /* Numerator */ + mpz_t den; /* Denominator, <> 0 */ +} mpq_t, *mp_rat; + +#define MP_NUMER_P(Q) (&((Q)->num)) /* Pointer to numerator */ +#define MP_DENOM_P(Q) (&((Q)->den)) /* Pointer to denominator */ + +/* Rounding constants */ +typedef enum { + MP_ROUND_DOWN, + MP_ROUND_HALF_UP, + MP_ROUND_UP, + MP_ROUND_HALF_DOWN +} mp_round_mode; + +mp_result mp_rat_init(mp_rat r); +mp_rat mp_rat_alloc(void); +mp_result mp_rat_reduce(mp_rat r); +mp_result mp_rat_init_size(mp_rat r, mp_size n_prec, mp_size d_prec); +mp_result mp_rat_init_copy(mp_rat r, mp_rat old); +mp_result mp_rat_set_value(mp_rat r, mp_small numer, mp_small denom); +mp_result mp_rat_set_uvalue(mp_rat r, mp_usmall numer, mp_usmall denom); +void mp_rat_clear(mp_rat r); +void mp_rat_free(mp_rat r); +mp_result mp_rat_numer(mp_rat r, mp_int z); /* z = num(r) */ +mp_int mp_rat_numer_ref(mp_rat r); /* &num(r) */ +mp_result mp_rat_denom(mp_rat r, mp_int z); /* z = den(r) */ +mp_int mp_rat_denom_ref(mp_rat r); /* &den(r) */ +mp_sign mp_rat_sign(mp_rat r); + +mp_result mp_rat_copy(mp_rat a, mp_rat c); /* c = a */ +void mp_rat_zero(mp_rat r); /* r = 0 */ +mp_result mp_rat_abs(mp_rat a, mp_rat c); /* c = |a| */ +mp_result mp_rat_neg(mp_rat a, mp_rat c); /* c = -a */ +mp_result mp_rat_recip(mp_rat a, mp_rat c); /* c = 1 / a */ +mp_result mp_rat_add(mp_rat a, mp_rat b, mp_rat c); /* c = a + b */ +mp_result mp_rat_sub(mp_rat a, mp_rat b, mp_rat c); /* c = a - b */ +mp_result mp_rat_mul(mp_rat a, mp_rat b, mp_rat c); /* c = a * b */ +mp_result mp_rat_div(mp_rat a, mp_rat b, mp_rat c); /* c = a / b */ + +mp_result mp_rat_add_int(mp_rat a, mp_int b, mp_rat c); /* c = a + b */ +mp_result mp_rat_sub_int(mp_rat a, mp_int b, mp_rat c); /* c = a - b */ +mp_result mp_rat_mul_int(mp_rat a, mp_int b, mp_rat c); /* c = a * b */ +mp_result mp_rat_div_int(mp_rat a, mp_int b, mp_rat c); /* c = a / b */ +mp_result mp_rat_expt(mp_rat a, mp_small b, mp_rat c); /* c = a ^ b */ + +int mp_rat_compare(mp_rat a, mp_rat b); /* a <=> b */ +int mp_rat_compare_unsigned(mp_rat a, mp_rat b); /* |a| <=> |b| */ +int mp_rat_compare_zero(mp_rat r); /* r <=> 0 */ +int mp_rat_compare_value(mp_rat r, mp_small n, mp_small d); /* r <=> n/d */ +int mp_rat_is_integer(mp_rat r); + +/* Convert to integers, if representable (returns MP_RANGE if not). */ +mp_result mp_rat_to_ints(mp_rat r, mp_small *num, mp_small *den); + +/* Convert to nul-terminated string with the specified radix, writing + at most limit characters including the nul terminator. */ +mp_result mp_rat_to_string(mp_rat r, mp_size radix, char *str, int limit); + +/* Convert to decimal format in the specified radix and precision, + writing at most limit characters including a nul terminator. */ +mp_result mp_rat_to_decimal(mp_rat r, mp_size radix, mp_size prec, + mp_round_mode round, char *str, int limit); + +/* Return the number of characters required to represent r in the given + radix. May over-estimate. */ +mp_result mp_rat_string_len(mp_rat r, mp_size radix); + +/* Return the number of characters required to represent r in decimal + format with the given radix and precision. May over-estimate. */ +mp_result mp_rat_decimal_len(mp_rat r, mp_size radix, mp_size prec); + +/* Read zero-terminated string into r */ +mp_result mp_rat_read_string(mp_rat r, mp_size radix, const char *str); +mp_result mp_rat_read_cstring(mp_rat r, mp_size radix, const char *str, + char **end); +mp_result mp_rat_read_ustring(mp_rat r, mp_size radix, const char *str, + char **end); + +/* Read zero-terminated string in decimal format into r */ +mp_result mp_rat_read_decimal(mp_rat r, mp_size radix, const char *str); +mp_result mp_rat_read_cdecimal(mp_rat r, mp_size radix, const char *str, + char **end); + +#ifdef __cplusplus +} +#endif +#endif /* IMRAT_H_ */ Index: lib/Analysis/isl/imath/imrat.c =================================================================== --- /dev/null +++ lib/Analysis/isl/imath/imrat.c @@ -0,0 +1,958 @@ +/* + Name: imrat.c + Purpose: Arbitrary precision rational arithmetic routines. + Author: M. J. Fromberger + + Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#include "imrat.h" +#include +#include +#include +#include + +#define TEMP(K) (temp + (K)) +#define SETUP(E, C) \ +do{if((res = (E)) != MP_OK) goto CLEANUP; ++(C);}while(0) + +/* Argument checking: + Use CHECK() where a return value is required; NRCHECK() elsewhere */ +#define CHECK(TEST) assert(TEST) +#define NRCHECK(TEST) assert(TEST) + +/* Reduce the given rational, in place, to lowest terms and canonical form. + Zero is represented as 0/1, one as 1/1. Signs are adjusted so that the sign + of the numerator is definitive. */ +static mp_result s_rat_reduce(mp_rat r); + +/* Common code for addition and subtraction operations on rationals. */ +static mp_result s_rat_combine(mp_rat a, mp_rat b, mp_rat c, + mp_result (*comb_f)(mp_int, mp_int, mp_int)); + +mp_result mp_rat_init(mp_rat r) +{ + mp_result res; + + if ((res = mp_int_init(MP_NUMER_P(r))) != MP_OK) + return res; + if ((res = mp_int_init(MP_DENOM_P(r))) != MP_OK) { + mp_int_clear(MP_NUMER_P(r)); + return res; + } + + return mp_int_set_value(MP_DENOM_P(r), 1); +} + +mp_rat mp_rat_alloc(void) +{ + mp_rat out = malloc(sizeof(*out)); + + if (out != NULL) { + if (mp_rat_init(out) != MP_OK) { + free(out); + return NULL; + } + } + + return out; +} + +mp_result mp_rat_reduce(mp_rat r) { + return s_rat_reduce(r); +} + +mp_result mp_rat_init_size(mp_rat r, mp_size n_prec, mp_size d_prec) +{ + mp_result res; + + if ((res = mp_int_init_size(MP_NUMER_P(r), n_prec)) != MP_OK) + return res; + if ((res = mp_int_init_size(MP_DENOM_P(r), d_prec)) != MP_OK) { + mp_int_clear(MP_NUMER_P(r)); + return res; + } + + return mp_int_set_value(MP_DENOM_P(r), 1); +} + +mp_result mp_rat_init_copy(mp_rat r, mp_rat old) +{ + mp_result res; + + if ((res = mp_int_init_copy(MP_NUMER_P(r), MP_NUMER_P(old))) != MP_OK) + return res; + if ((res = mp_int_init_copy(MP_DENOM_P(r), MP_DENOM_P(old))) != MP_OK) + mp_int_clear(MP_NUMER_P(r)); + + return res; +} + +mp_result mp_rat_set_value(mp_rat r, mp_small numer, mp_small denom) +{ + mp_result res; + + if (denom == 0) + return MP_UNDEF; + + if ((res = mp_int_set_value(MP_NUMER_P(r), numer)) != MP_OK) + return res; + if ((res = mp_int_set_value(MP_DENOM_P(r), denom)) != MP_OK) + return res; + + return s_rat_reduce(r); +} + +mp_result mp_rat_set_uvalue(mp_rat r, mp_usmall numer, mp_usmall denom) +{ + mp_result res; + + if (denom == 0) + return MP_UNDEF; + + if ((res = mp_int_set_uvalue(MP_NUMER_P(r), numer)) != MP_OK) + return res; + if ((res = mp_int_set_uvalue(MP_DENOM_P(r), denom)) != MP_OK) + return res; + + return s_rat_reduce(r); +} + +void mp_rat_clear(mp_rat r) +{ + mp_int_clear(MP_NUMER_P(r)); + mp_int_clear(MP_DENOM_P(r)); + +} + +void mp_rat_free(mp_rat r) +{ + NRCHECK(r != NULL); + + if (r->num.digits != NULL) + mp_rat_clear(r); + + free(r); +} + +mp_result mp_rat_numer(mp_rat r, mp_int z) +{ + return mp_int_copy(MP_NUMER_P(r), z); +} + +mp_int mp_rat_numer_ref(mp_rat r) +{ + return MP_NUMER_P(r); +} + + +mp_result mp_rat_denom(mp_rat r, mp_int z) +{ + return mp_int_copy(MP_DENOM_P(r), z); +} + +mp_int mp_rat_denom_ref(mp_rat r) +{ + return MP_DENOM_P(r); +} + +mp_sign mp_rat_sign(mp_rat r) +{ + return MP_SIGN(MP_NUMER_P(r)); +} + +mp_result mp_rat_copy(mp_rat a, mp_rat c) +{ + mp_result res; + + if ((res = mp_int_copy(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK) + return res; + + res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c)); + return res; +} + +void mp_rat_zero(mp_rat r) +{ + mp_int_zero(MP_NUMER_P(r)); + mp_int_set_value(MP_DENOM_P(r), 1); + +} + +mp_result mp_rat_abs(mp_rat a, mp_rat c) +{ + mp_result res; + + if ((res = mp_int_abs(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK) + return res; + + res = mp_int_abs(MP_DENOM_P(a), MP_DENOM_P(c)); + return res; +} + +mp_result mp_rat_neg(mp_rat a, mp_rat c) +{ + mp_result res; + + if ((res = mp_int_neg(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK) + return res; + + res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c)); + return res; +} + +mp_result mp_rat_recip(mp_rat a, mp_rat c) +{ + mp_result res; + + if (mp_rat_compare_zero(a) == 0) + return MP_UNDEF; + + if ((res = mp_rat_copy(a, c)) != MP_OK) + return res; + + mp_int_swap(MP_NUMER_P(c), MP_DENOM_P(c)); + + /* Restore the signs of the swapped elements */ + { + mp_sign tmp = MP_SIGN(MP_NUMER_P(c)); + + MP_SIGN(MP_NUMER_P(c)) = MP_SIGN(MP_DENOM_P(c)); + MP_SIGN(MP_DENOM_P(c)) = tmp; + } + + return MP_OK; +} + +mp_result mp_rat_add(mp_rat a, mp_rat b, mp_rat c) +{ + return s_rat_combine(a, b, c, mp_int_add); + +} + +mp_result mp_rat_sub(mp_rat a, mp_rat b, mp_rat c) +{ + return s_rat_combine(a, b, c, mp_int_sub); + +} + +mp_result mp_rat_mul(mp_rat a, mp_rat b, mp_rat c) +{ + mp_result res; + + if ((res = mp_int_mul(MP_NUMER_P(a), MP_NUMER_P(b), MP_NUMER_P(c))) != MP_OK) + return res; + + if (mp_int_compare_zero(MP_NUMER_P(c)) != 0) { + if ((res = mp_int_mul(MP_DENOM_P(a), MP_DENOM_P(b), MP_DENOM_P(c))) != MP_OK) + return res; + } + + return s_rat_reduce(c); +} + +mp_result mp_rat_div(mp_rat a, mp_rat b, mp_rat c) +{ + mp_result res = MP_OK; + + if (mp_rat_compare_zero(b) == 0) + return MP_UNDEF; + + if (c == a || c == b) { + mpz_t tmp; + + if ((res = mp_int_init(&tmp)) != MP_OK) return res; + if ((res = mp_int_mul(MP_NUMER_P(a), MP_DENOM_P(b), &tmp)) != MP_OK) + goto CLEANUP; + if ((res = mp_int_mul(MP_DENOM_P(a), MP_NUMER_P(b), MP_DENOM_P(c))) != MP_OK) + goto CLEANUP; + res = mp_int_copy(&tmp, MP_NUMER_P(c)); + + CLEANUP: + mp_int_clear(&tmp); + } + else { + if ((res = mp_int_mul(MP_NUMER_P(a), MP_DENOM_P(b), MP_NUMER_P(c))) != MP_OK) + return res; + if ((res = mp_int_mul(MP_DENOM_P(a), MP_NUMER_P(b), MP_DENOM_P(c))) != MP_OK) + return res; + } + + if (res != MP_OK) + return res; + else + return s_rat_reduce(c); +} + +mp_result mp_rat_add_int(mp_rat a, mp_int b, mp_rat c) +{ + mpz_t tmp; + mp_result res; + + if ((res = mp_int_init_copy(&tmp, b)) != MP_OK) + return res; + + if ((res = mp_int_mul(&tmp, MP_DENOM_P(a), &tmp)) != MP_OK) + goto CLEANUP; + + if ((res = mp_rat_copy(a, c)) != MP_OK) + goto CLEANUP; + + if ((res = mp_int_add(MP_NUMER_P(c), &tmp, MP_NUMER_P(c))) != MP_OK) + goto CLEANUP; + + res = s_rat_reduce(c); + + CLEANUP: + mp_int_clear(&tmp); + return res; +} + +mp_result mp_rat_sub_int(mp_rat a, mp_int b, mp_rat c) +{ + mpz_t tmp; + mp_result res; + + if ((res = mp_int_init_copy(&tmp, b)) != MP_OK) + return res; + + if ((res = mp_int_mul(&tmp, MP_DENOM_P(a), &tmp)) != MP_OK) + goto CLEANUP; + + if ((res = mp_rat_copy(a, c)) != MP_OK) + goto CLEANUP; + + if ((res = mp_int_sub(MP_NUMER_P(c), &tmp, MP_NUMER_P(c))) != MP_OK) + goto CLEANUP; + + res = s_rat_reduce(c); + + CLEANUP: + mp_int_clear(&tmp); + return res; +} + +mp_result mp_rat_mul_int(mp_rat a, mp_int b, mp_rat c) +{ + mp_result res; + + if ((res = mp_rat_copy(a, c)) != MP_OK) + return res; + + if ((res = mp_int_mul(MP_NUMER_P(c), b, MP_NUMER_P(c))) != MP_OK) + return res; + + return s_rat_reduce(c); +} + +mp_result mp_rat_div_int(mp_rat a, mp_int b, mp_rat c) +{ + mp_result res; + + if (mp_int_compare_zero(b) == 0) + return MP_UNDEF; + + if ((res = mp_rat_copy(a, c)) != MP_OK) + return res; + + if ((res = mp_int_mul(MP_DENOM_P(c), b, MP_DENOM_P(c))) != MP_OK) + return res; + + return s_rat_reduce(c); +} + +mp_result mp_rat_expt(mp_rat a, mp_small b, mp_rat c) +{ + mp_result res; + + /* Special cases for easy powers. */ + if (b == 0) + return mp_rat_set_value(c, 1, 1); + else if(b == 1) + return mp_rat_copy(a, c); + + /* Since rationals are always stored in lowest terms, it is not necessary to + reduce again when raising to an integer power. */ + if ((res = mp_int_expt(MP_NUMER_P(a), b, MP_NUMER_P(c))) != MP_OK) + return res; + + return mp_int_expt(MP_DENOM_P(a), b, MP_DENOM_P(c)); +} + +int mp_rat_compare(mp_rat a, mp_rat b) +{ + /* Quick check for opposite signs. Works because the sign of the numerator + is always definitive. */ + if (MP_SIGN(MP_NUMER_P(a)) != MP_SIGN(MP_NUMER_P(b))) { + if (MP_SIGN(MP_NUMER_P(a)) == MP_ZPOS) + return 1; + else + return -1; + } + else { + /* Compare absolute magnitudes; if both are positive, the answer stands, + otherwise it needs to be reflected about zero. */ + int cmp = mp_rat_compare_unsigned(a, b); + + if (MP_SIGN(MP_NUMER_P(a)) == MP_ZPOS) + return cmp; + else + return -cmp; + } +} + +int mp_rat_compare_unsigned(mp_rat a, mp_rat b) +{ + /* If the denominators are equal, we can quickly compare numerators without + multiplying. Otherwise, we actually have to do some work. */ + if (mp_int_compare_unsigned(MP_DENOM_P(a), MP_DENOM_P(b)) == 0) + return mp_int_compare_unsigned(MP_NUMER_P(a), MP_NUMER_P(b)); + + else { + mpz_t temp[2]; + mp_result res; + int cmp = INT_MAX, last = 0; + + /* t0 = num(a) * den(b), t1 = num(b) * den(a) */ + SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(a)), last); + SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(b)), last); + + if ((res = mp_int_mul(TEMP(0), MP_DENOM_P(b), TEMP(0))) != MP_OK || + (res = mp_int_mul(TEMP(1), MP_DENOM_P(a), TEMP(1))) != MP_OK) + goto CLEANUP; + + cmp = mp_int_compare_unsigned(TEMP(0), TEMP(1)); + + CLEANUP: + while (--last >= 0) + mp_int_clear(TEMP(last)); + + return cmp; + } +} + +int mp_rat_compare_zero(mp_rat r) +{ + return mp_int_compare_zero(MP_NUMER_P(r)); +} + +int mp_rat_compare_value(mp_rat r, mp_small n, mp_small d) +{ + mpq_t tmp; + mp_result res; + int out = INT_MAX; + + if ((res = mp_rat_init(&tmp)) != MP_OK) + return out; + if ((res = mp_rat_set_value(&tmp, n, d)) != MP_OK) + goto CLEANUP; + + out = mp_rat_compare(r, &tmp); + + CLEANUP: + mp_rat_clear(&tmp); + return out; +} + +int mp_rat_is_integer(mp_rat r) +{ + return (mp_int_compare_value(MP_DENOM_P(r), 1) == 0); +} + +mp_result mp_rat_to_ints(mp_rat r, mp_small *num, mp_small *den) +{ + mp_result res; + + if ((res = mp_int_to_int(MP_NUMER_P(r), num)) != MP_OK) + return res; + + res = mp_int_to_int(MP_DENOM_P(r), den); + return res; +} + +mp_result mp_rat_to_string(mp_rat r, mp_size radix, char *str, int limit) +{ + char *start; + int len; + mp_result res; + + /* Write the numerator. The sign of the rational number is written by the + underlying integer implementation. */ + if ((res = mp_int_to_string(MP_NUMER_P(r), radix, str, limit)) != MP_OK) + return res; + + /* If the value is zero, don't bother writing any denominator */ + if (mp_int_compare_zero(MP_NUMER_P(r)) == 0) + return MP_OK; + + /* Locate the end of the numerator, and make sure we are not going to exceed + the limit by writing a slash. */ + len = strlen(str); + start = str + len; + limit -= len; + if(limit == 0) + return MP_TRUNC; + + *start++ = '/'; + limit -= 1; + + res = mp_int_to_string(MP_DENOM_P(r), radix, start, limit); + return res; +} + +mp_result mp_rat_to_decimal(mp_rat r, mp_size radix, mp_size prec, + mp_round_mode round, char *str, int limit) +{ + mpz_t temp[3]; + mp_result res; + char *start = str; + int len, lead_0, left = limit, last = 0; + + SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(r)), last); + SETUP(mp_int_init(TEMP(last)), last); + SETUP(mp_int_init(TEMP(last)), last); + + /* Get the unsigned integer part by dividing denominator into the absolute + value of the numerator. */ + mp_int_abs(TEMP(0), TEMP(0)); + if ((res = mp_int_div(TEMP(0), MP_DENOM_P(r), TEMP(0), TEMP(1))) != MP_OK) + goto CLEANUP; + + /* Now: T0 = integer portion, unsigned; + T1 = remainder, from which fractional part is computed. */ + + /* Count up leading zeroes after the radix point. */ + for (lead_0 = 0; lead_0 < prec && mp_int_compare(TEMP(1), MP_DENOM_P(r)) < 0; + ++lead_0) { + if ((res = mp_int_mul_value(TEMP(1), radix, TEMP(1))) != MP_OK) + goto CLEANUP; + } + + /* Multiply remainder by a power of the radix sufficient to get the right + number of significant figures. */ + if (prec > lead_0) { + if ((res = mp_int_expt_value(radix, prec - lead_0, TEMP(2))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_mul(TEMP(1), TEMP(2), TEMP(1))) != MP_OK) + goto CLEANUP; + } + if ((res = mp_int_div(TEMP(1), MP_DENOM_P(r), TEMP(1), TEMP(2))) != MP_OK) + goto CLEANUP; + + /* Now: T1 = significant digits of fractional part; + T2 = leftovers, to use for rounding. + + At this point, what we do depends on the rounding mode. The default is + MP_ROUND_DOWN, for which everything is as it should be already. + */ + switch (round) { + int cmp; + + case MP_ROUND_UP: + if (mp_int_compare_zero(TEMP(2)) != 0) { + if (prec == 0) + res = mp_int_add_value(TEMP(0), 1, TEMP(0)); + else + res = mp_int_add_value(TEMP(1), 1, TEMP(1)); + } + break; + + case MP_ROUND_HALF_UP: + case MP_ROUND_HALF_DOWN: + if ((res = mp_int_mul_pow2(TEMP(2), 1, TEMP(2))) != MP_OK) + goto CLEANUP; + + cmp = mp_int_compare(TEMP(2), MP_DENOM_P(r)); + + if (round == MP_ROUND_HALF_UP) + cmp += 1; + + if (cmp > 0) { + if (prec == 0) + res = mp_int_add_value(TEMP(0), 1, TEMP(0)); + else + res = mp_int_add_value(TEMP(1), 1, TEMP(1)); + } + break; + + case MP_ROUND_DOWN: + break; /* No action required */ + + default: + return MP_BADARG; /* Invalid rounding specifier */ + } + + /* The sign of the output should be the sign of the numerator, but if all the + displayed digits will be zero due to the precision, a negative shouldn't + be shown. */ + if (MP_SIGN(MP_NUMER_P(r)) == MP_NEG && + (mp_int_compare_zero(TEMP(0)) != 0 || + mp_int_compare_zero(TEMP(1)) != 0)) { + *start++ = '-'; + left -= 1; + } + + if ((res = mp_int_to_string(TEMP(0), radix, start, left)) != MP_OK) + goto CLEANUP; + + len = strlen(start); + start += len; + left -= len; + + if (prec == 0) + goto CLEANUP; + + *start++ = '.'; + left -= 1; + + if (left < prec + 1) { + res = MP_TRUNC; + goto CLEANUP; + } + + memset(start, '0', lead_0 - 1); + left -= lead_0; + start += lead_0 - 1; + + res = mp_int_to_string(TEMP(1), radix, start, left); + + CLEANUP: + while (--last >= 0) + mp_int_clear(TEMP(last)); + + return res; +} + +mp_result mp_rat_string_len(mp_rat r, mp_size radix) +{ + mp_result n_len, d_len = 0; + + n_len = mp_int_string_len(MP_NUMER_P(r), radix); + + if (mp_int_compare_zero(MP_NUMER_P(r)) != 0) + d_len = mp_int_string_len(MP_DENOM_P(r), radix); + + /* Though simplistic, this formula is correct. Space for the sign flag is + included in n_len, and the space for the NUL that is counted in n_len + counts for the separator here. The space for the NUL counted in d_len + counts for the final terminator here. */ + + return n_len + d_len; + +} + +mp_result mp_rat_decimal_len(mp_rat r, mp_size radix, mp_size prec) +{ + int z_len, f_len; + + z_len = mp_int_string_len(MP_NUMER_P(r), radix); + + if (prec == 0) + f_len = 1; /* terminator only */ + else + f_len = 1 + prec + 1; /* decimal point, digits, terminator */ + + return z_len + f_len; +} + +mp_result mp_rat_read_string(mp_rat r, mp_size radix, const char *str) +{ + return mp_rat_read_cstring(r, radix, str, NULL); +} + +mp_result mp_rat_read_cstring(mp_rat r, mp_size radix, const char *str, + char **end) +{ + mp_result res; + char *endp; + + if ((res = mp_int_read_cstring(MP_NUMER_P(r), radix, str, &endp)) != MP_OK && + (res != MP_TRUNC)) + return res; + + /* Skip whitespace between numerator and (possible) separator */ + while (isspace((unsigned char) *endp)) + ++endp; + + /* If there is no separator, we will stop reading at this point. */ + if (*endp != '/') { + mp_int_set_value(MP_DENOM_P(r), 1); + if (end != NULL) + *end = endp; + return res; + } + + ++endp; /* skip separator */ + if ((res = mp_int_read_cstring(MP_DENOM_P(r), radix, endp, end)) != MP_OK) + return res; + + /* Make sure the value is well-defined */ + if (mp_int_compare_zero(MP_DENOM_P(r)) == 0) + return MP_UNDEF; + + /* Reduce to lowest terms */ + return s_rat_reduce(r); +} + +/* Read a string and figure out what format it's in. The radix may be supplied + as zero to use "default" behaviour. + + This function will accept either a/b notation or decimal notation. + */ +mp_result mp_rat_read_ustring(mp_rat r, mp_size radix, const char *str, + char **end) +{ + char *endp; + mp_result res; + + if (radix == 0) + radix = 10; /* default to decimal input */ + + if ((res = mp_rat_read_cstring(r, radix, str, &endp)) != MP_OK) { + if (res == MP_TRUNC) { + if (*endp == '.') + res = mp_rat_read_cdecimal(r, radix, str, &endp); + } + else + return res; + } + + if (end != NULL) + *end = endp; + + return res; +} + +mp_result mp_rat_read_decimal(mp_rat r, mp_size radix, const char *str) +{ + return mp_rat_read_cdecimal(r, radix, str, NULL); +} + +mp_result mp_rat_read_cdecimal(mp_rat r, mp_size radix, const char *str, + char **end) +{ + mp_result res; + mp_sign osign; + char *endp; + + while (isspace((unsigned char) *str)) + ++str; + + switch (*str) { + case '-': + osign = MP_NEG; + break; + default: + osign = MP_ZPOS; + } + + if ((res = mp_int_read_cstring(MP_NUMER_P(r), radix, str, &endp)) != MP_OK && + (res != MP_TRUNC)) + return res; + + /* This needs to be here. */ + (void) mp_int_set_value(MP_DENOM_P(r), 1); + + if (*endp != '.') { + if (end != NULL) + *end = endp; + return res; + } + + /* If the character following the decimal point is whitespace or a sign flag, + we will consider this a truncated value. This special case is because + mp_int_read_string() will consider whitespace or sign flags to be valid + starting characters for a value, and we do not want them following the + decimal point. + + Once we have done this check, it is safe to read in the value of the + fractional piece as a regular old integer. + */ + ++endp; + if (*endp == '\0') { + if (end != NULL) + *end = endp; + return MP_OK; + } + else if(isspace((unsigned char) *endp) || *endp == '-' || *endp == '+') { + return MP_TRUNC; + } + else { + mpz_t frac; + mp_result save_res; + char *save = endp; + int num_lz = 0; + + /* Make a temporary to hold the part after the decimal point. */ + if ((res = mp_int_init(&frac)) != MP_OK) + return res; + + if ((res = mp_int_read_cstring(&frac, radix, endp, &endp)) != MP_OK && + (res != MP_TRUNC)) + goto CLEANUP; + + /* Save this response for later. */ + save_res = res; + + if (mp_int_compare_zero(&frac) == 0) + goto FINISHED; + + /* Discard trailing zeroes (somewhat inefficiently) */ + while (mp_int_divisible_value(&frac, radix)) + if ((res = mp_int_div_value(&frac, radix, &frac, NULL)) != MP_OK) + goto CLEANUP; + + /* Count leading zeros after the decimal point */ + while (save[num_lz] == '0') + ++num_lz; + + /* Find the least power of the radix that is at least as large as the + significant value of the fractional part, ignoring leading zeroes. */ + (void) mp_int_set_value(MP_DENOM_P(r), radix); + + while (mp_int_compare(MP_DENOM_P(r), &frac) < 0) { + if ((res = mp_int_mul_value(MP_DENOM_P(r), radix, MP_DENOM_P(r))) != MP_OK) + goto CLEANUP; + } + + /* Also shift by enough to account for leading zeroes */ + while (num_lz > 0) { + if ((res = mp_int_mul_value(MP_DENOM_P(r), radix, MP_DENOM_P(r))) != MP_OK) + goto CLEANUP; + + --num_lz; + } + + /* Having found this power, shift the numerator leftward that many, digits, + and add the nonzero significant digits of the fractional part to get the + result. */ + if ((res = mp_int_mul(MP_NUMER_P(r), MP_DENOM_P(r), MP_NUMER_P(r))) != MP_OK) + goto CLEANUP; + + { /* This addition needs to be unsigned. */ + MP_SIGN(MP_NUMER_P(r)) = MP_ZPOS; + if ((res = mp_int_add(MP_NUMER_P(r), &frac, MP_NUMER_P(r))) != MP_OK) + goto CLEANUP; + + MP_SIGN(MP_NUMER_P(r)) = osign; + } + if ((res = s_rat_reduce(r)) != MP_OK) + goto CLEANUP; + + /* At this point, what we return depends on whether reading the fractional + part was truncated or not. That information is saved from when we + called mp_int_read_string() above. */ + FINISHED: + res = save_res; + if (end != NULL) + *end = endp; + + CLEANUP: + mp_int_clear(&frac); + + return res; + } +} + +/* Private functions for internal use. Make unchecked assumptions about format + and validity of inputs. */ + +static mp_result s_rat_reduce(mp_rat r) +{ + mpz_t gcd; + mp_result res = MP_OK; + + if (mp_int_compare_zero(MP_NUMER_P(r)) == 0) { + mp_int_set_value(MP_DENOM_P(r), 1); + return MP_OK; + } + + /* If the greatest common divisor of the numerator and denominator is greater + than 1, divide it out. */ + if ((res = mp_int_init(&gcd)) != MP_OK) + return res; + + if ((res = mp_int_gcd(MP_NUMER_P(r), MP_DENOM_P(r), &gcd)) != MP_OK) + goto CLEANUP; + + if (mp_int_compare_value(&gcd, 1) != 0) { + if ((res = mp_int_div(MP_NUMER_P(r), &gcd, MP_NUMER_P(r), NULL)) != MP_OK) + goto CLEANUP; + if ((res = mp_int_div(MP_DENOM_P(r), &gcd, MP_DENOM_P(r), NULL)) != MP_OK) + goto CLEANUP; + } + + /* Fix up the signs of numerator and denominator */ + if (MP_SIGN(MP_NUMER_P(r)) == MP_SIGN(MP_DENOM_P(r))) + MP_SIGN(MP_NUMER_P(r)) = MP_SIGN(MP_DENOM_P(r)) = MP_ZPOS; + else { + MP_SIGN(MP_NUMER_P(r)) = MP_NEG; + MP_SIGN(MP_DENOM_P(r)) = MP_ZPOS; + } + + CLEANUP: + mp_int_clear(&gcd); + + return res; +} + +static mp_result s_rat_combine(mp_rat a, mp_rat b, mp_rat c, + mp_result (*comb_f)(mp_int, mp_int, mp_int)) +{ + mp_result res; + + /* Shortcut when denominators are already common */ + if (mp_int_compare(MP_DENOM_P(a), MP_DENOM_P(b)) == 0) { + if ((res = (comb_f)(MP_NUMER_P(a), MP_NUMER_P(b), MP_NUMER_P(c))) != MP_OK) + return res; + if ((res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c))) != MP_OK) + return res; + + return s_rat_reduce(c); + } + else { + mpz_t temp[2]; + int last = 0; + + SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(a)), last); + SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(b)), last); + + if ((res = mp_int_mul(TEMP(0), MP_DENOM_P(b), TEMP(0))) != MP_OK) + goto CLEANUP; + if ((res = mp_int_mul(TEMP(1), MP_DENOM_P(a), TEMP(1))) != MP_OK) + goto CLEANUP; + if ((res = (comb_f)(TEMP(0), TEMP(1), MP_NUMER_P(c))) != MP_OK) + goto CLEANUP; + + res = mp_int_mul(MP_DENOM_P(a), MP_DENOM_P(b), MP_DENOM_P(c)); + + CLEANUP: + while (--last >= 0) + mp_int_clear(TEMP(last)); + + if (res == MP_OK) + return s_rat_reduce(c); + else + return res; + } +} + +/* Here there be dragons */ Index: lib/Analysis/isl/imath_wrap/gmp_compat.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/gmp_compat.h @@ -0,0 +1,2 @@ +#include "wrap.h" +#include "../imath/gmp_compat.h" Index: lib/Analysis/isl/imath_wrap/gmp_compat.c =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/gmp_compat.c @@ -0,0 +1,2 @@ +#include "wrap.h" +#include "../imath/gmp_compat.c" Index: lib/Analysis/isl/imath_wrap/imath.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/imath.h @@ -0,0 +1,2 @@ +#include "wrap.h" +#include "../imath/imath.h" Index: lib/Analysis/isl/imath_wrap/imath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/imath.c @@ -0,0 +1,2 @@ +#include "wrap.h" +#include "../imath/imath.c" Index: lib/Analysis/isl/imath_wrap/imrat.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/imrat.h @@ -0,0 +1,2 @@ +#include "wrap.h" +#include "../imath/imrat.h" Index: lib/Analysis/isl/imath_wrap/imrat.c =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/imrat.c @@ -0,0 +1,2 @@ +#include "wrap.h" +#include "../imath/imrat.c" Index: lib/Analysis/isl/imath_wrap/wrap.h =================================================================== --- /dev/null +++ lib/Analysis/isl/imath_wrap/wrap.h @@ -0,0 +1,172 @@ +#ifndef ISL_IMATH_WRAP +#define ISL_IMATH_WRAP + +#define MP_BADARG ISL_MP_BADARG +#define MP_FALSE ISL_MP_FALSE +#define MP_MEMORY ISL_MP_MEMORY +#define MP_MINERR ISL_MP_MINERR +#define MP_NEG ISL_MP_NEG +#define MP_OK ISL_MP_OK +#define MP_RANGE ISL_MP_RANGE +#define MP_TRUE ISL_MP_TRUE +#define MP_TRUNC ISL_MP_TRUNC +#define MP_UNDEF ISL_MP_UNDEF +#define MP_ZPOS ISL_MP_ZPOS + +#define impq_canonicalize isl_impq_canonicalize +#define impq_clear isl_impq_clear +#define impq_cmp isl_impq_cmp +#define impq_denref isl_impq_denref +#define impq_get_str isl_impq_get_str +#define impq_init isl_impq_init +#define impq_mul isl_impq_mul +#define impq_numref isl_impq_numref +#define impq_set isl_impq_set +#define impq_set_str isl_impq_set_str +#define impq_set_ui isl_impq_set_ui +#define impq_sgn isl_impq_sgn +#define impz_abs isl_impz_abs +#define impz_add isl_impz_add +#define impz_addmul isl_impz_addmul +#define impz_add_ui isl_impz_add_ui +#define impz_cdiv_q isl_impz_cdiv_q +#define impz_clear isl_impz_clear +#define impz_cmp isl_impz_cmp +#define impz_cmpabs isl_impz_cmpabs +#define impz_cmp_si isl_impz_cmp_si +#define impz_divexact isl_impz_divexact +#define impz_divexact_ui isl_impz_divexact_ui +#define impz_divisible_p isl_impz_divisible_p +#define impz_export isl_impz_export +#define impz_fdiv_q isl_impz_fdiv_q +#define impz_fdiv_q_ui isl_impz_fdiv_q_ui +#define impz_fdiv_r isl_impz_fdiv_r +#define impz_gcd isl_impz_gcd +#define impz_get_si isl_impz_get_si +#define impz_get_str isl_impz_get_str +#define impz_get_ui isl_impz_get_ui +#define impz_import isl_impz_import +#define impz_init isl_impz_init +#define impz_lcm isl_impz_lcm +#define impz_mul isl_impz_mul +#define impz_mul_2exp isl_impz_mul_2exp +#define impz_mul_ui isl_impz_mul_ui +#define impz_neg isl_impz_neg +#define impz_pow_ui isl_impz_pow_ui +#define impz_set isl_impz_set +#define impz_set_si isl_impz_set_si +#define impz_set_str isl_impz_set_str +#define impz_set_ui isl_impz_set_ui +#define impz_sgn isl_impz_sgn +#define impz_sizeinbase isl_impz_sizeinbase +#define impz_sub isl_impz_sub +#define impz_submul isl_impz_submul +#define impz_sub_ui isl_impz_sub_ui +#define impz_swap isl_impz_swap +#define impz_tdiv_q isl_impz_tdiv_q +#define mp_error_string isl_mp_error_string +#define mp_int_abs isl_mp_int_abs +#define mp_int_add isl_mp_int_add +#define mp_int_add_value isl_mp_int_add_value +#define mp_int_alloc isl_mp_int_alloc +#define mp_int_binary_len isl_mp_int_binary_len +#define mp_int_clear isl_mp_int_clear +#define mp_int_compare isl_mp_int_compare +#define mp_int_compare_unsigned isl_mp_int_compare_unsigned +#define mp_int_compare_uvalue isl_mp_int_compare_uvalue +#define mp_int_compare_value isl_mp_int_compare_value +#define mp_int_compare_zero isl_mp_int_compare_zero +#define mp_int_copy isl_mp_int_copy +#define mp_int_count_bits isl_mp_int_count_bits +#define mp_int_div isl_mp_int_div +#define mp_int_divisible_value isl_mp_int_divisible_value +#define mp_int_div_pow2 isl_mp_int_div_pow2 +#define mp_int_div_value isl_mp_int_div_value +#define mp_int_egcd isl_mp_int_egcd +#define mp_int_expt isl_mp_int_expt +#define mp_int_expt_full isl_mp_int_expt_full +#define mp_int_exptmod isl_mp_int_exptmod +#define mp_int_exptmod_bvalue isl_mp_int_exptmod_bvalue +#define mp_int_exptmod_evalue isl_mp_int_exptmod_evalue +#define mp_int_exptmod_known isl_mp_int_exptmod_known +#define mp_int_expt_value isl_mp_int_expt_value +#define mp_int_free isl_mp_int_free +#define mp_int_gcd isl_mp_int_gcd +#define mp_int_init isl_mp_int_init +#define mp_int_init_copy isl_mp_int_init_copy +#define mp_int_init_size isl_mp_int_init_size +#define mp_int_init_uvalue isl_mp_int_init_uvalue +#define mp_int_init_value isl_mp_int_init_value +#define mp_int_invmod isl_mp_int_invmod +#define mp_int_is_pow2 isl_mp_int_is_pow2 +#define mp_int_lcm isl_mp_int_lcm +#define mp_int_mod isl_mp_int_mod +#define mp_int_mul isl_mp_int_mul +#define mp_int_mul_pow2 isl_mp_int_mul_pow2 +#define mp_int_mul_value isl_mp_int_mul_value +#define mp_int_neg isl_mp_int_neg +#define mp_int_read_binary isl_mp_int_read_binary +#define mp_int_read_cstring isl_mp_int_read_cstring +#define mp_int_read_string isl_mp_int_read_string +#define mp_int_read_unsigned isl_mp_int_read_unsigned +#define mp_int_redux_const isl_mp_int_redux_const +#define mp_int_root isl_mp_int_root +#define mp_int_set_uvalue isl_mp_int_set_uvalue +#define mp_int_set_value isl_mp_int_set_value +#define mp_int_sqr isl_mp_int_sqr +#define mp_int_string_len isl_mp_int_string_len +#define mp_int_sub isl_mp_int_sub +#define mp_int_sub_value isl_mp_int_sub_value +#define mp_int_swap isl_mp_int_swap +#define mp_int_to_binary isl_mp_int_to_binary +#define mp_int_to_int isl_mp_int_to_int +#define mp_int_to_string isl_mp_int_to_string +#define mp_int_to_uint isl_mp_int_to_uint +#define mp_int_to_unsigned isl_mp_int_to_unsigned +#define mp_int_unsigned_len isl_mp_int_unsigned_len +#define mp_int_zero isl_mp_int_zero +#define mp_rat_abs isl_mp_rat_abs +#define mp_rat_add isl_mp_rat_add +#define mp_rat_add_int isl_mp_rat_add_int +#define mp_rat_alloc isl_mp_rat_alloc +#define mp_rat_clear isl_mp_rat_clear +#define mp_rat_compare isl_mp_rat_compare +#define mp_rat_compare_unsigned isl_mp_rat_compare_unsigned +#define mp_rat_compare_value isl_mp_rat_compare_value +#define mp_rat_compare_zero isl_mp_rat_compare_zero +#define mp_rat_copy isl_mp_rat_copy +#define mp_rat_decimal_len isl_mp_rat_decimal_len +#define mp_rat_denom isl_mp_rat_denom +#define mp_rat_denom_ref isl_mp_rat_denom_ref +#define mp_rat_div isl_mp_rat_div +#define mp_rat_div_int isl_mp_rat_div_int +#define mp_rat_expt isl_mp_rat_expt +#define mp_rat_free isl_mp_rat_free +#define mp_rat_init isl_mp_rat_init +#define mp_rat_init_copy isl_mp_rat_init_copy +#define mp_rat_init_size isl_mp_rat_init_size +#define mp_rat_is_integer isl_mp_rat_is_integer +#define mp_rat_mul isl_mp_rat_mul +#define mp_rat_mul_int isl_mp_rat_mul_int +#define mp_rat_neg isl_mp_rat_neg +#define mp_rat_numer isl_mp_rat_numer +#define mp_rat_numer_ref isl_mp_rat_numer_ref +#define mp_rat_read_cdecimal isl_mp_rat_read_cdecimal +#define mp_rat_read_cstring isl_mp_rat_read_cstring +#define mp_rat_read_decimal isl_mp_rat_read_decimal +#define mp_rat_read_string isl_mp_rat_read_string +#define mp_rat_read_ustring isl_mp_rat_read_ustring +#define mp_rat_recip isl_mp_rat_recip +#define mp_rat_reduce isl_mp_rat_reduce +#define mp_rat_set_uvalue isl_mp_rat_set_uvalue +#define mp_rat_set_value isl_mp_rat_set_value +#define mp_rat_sign isl_mp_rat_sign +#define mp_rat_string_len isl_mp_rat_string_len +#define mp_rat_sub isl_mp_rat_sub +#define mp_rat_sub_int isl_mp_rat_sub_int +#define mp_rat_to_decimal isl_mp_rat_to_decimal +#define mp_rat_to_ints isl_mp_rat_to_ints +#define mp_rat_to_string isl_mp_rat_to_string +#define mp_rat_zero isl_mp_rat_zero + +#endif Index: lib/Analysis/isl/include/isl/aff.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/aff.h @@ -0,0 +1,984 @@ +#ifndef ISL_AFF_H +#define ISL_AFF_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_aff *isl_aff_zero_on_domain(__isl_take isl_local_space *ls); +__isl_give isl_aff *isl_aff_val_on_domain(__isl_take isl_local_space *ls, + __isl_take isl_val *val); +__isl_give isl_aff *isl_aff_var_on_domain(__isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos); +__isl_give isl_aff *isl_aff_nan_on_domain(__isl_take isl_local_space *ls); + +__isl_give isl_aff *isl_aff_copy(__isl_keep isl_aff *aff); +__isl_null isl_aff *isl_aff_free(__isl_take isl_aff *aff); + +isl_ctx *isl_aff_get_ctx(__isl_keep isl_aff *aff); +uint32_t isl_aff_get_hash(__isl_keep isl_aff *aff); + +int isl_aff_dim(__isl_keep isl_aff *aff, enum isl_dim_type type); +isl_bool isl_aff_involves_dims(__isl_keep isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_space *isl_aff_get_domain_space(__isl_keep isl_aff *aff); +__isl_give isl_space *isl_aff_get_space(__isl_keep isl_aff *aff); +__isl_give isl_local_space *isl_aff_get_domain_local_space( + __isl_keep isl_aff *aff); +__isl_give isl_local_space *isl_aff_get_local_space(__isl_keep isl_aff *aff); + +const char *isl_aff_get_dim_name(__isl_keep isl_aff *aff, + enum isl_dim_type type, unsigned pos); +__isl_give isl_val *isl_aff_get_constant_val(__isl_keep isl_aff *aff); +__isl_give isl_val *isl_aff_get_coefficient_val(__isl_keep isl_aff *aff, + enum isl_dim_type type, int pos); +int isl_aff_coefficient_sgn(__isl_keep isl_aff *aff, + enum isl_dim_type type, int pos); +__isl_give isl_val *isl_aff_get_denominator_val(__isl_keep isl_aff *aff); +__isl_give isl_aff *isl_aff_set_constant_si(__isl_take isl_aff *aff, int v); +__isl_give isl_aff *isl_aff_set_constant_val(__isl_take isl_aff *aff, + __isl_take isl_val *v); +__isl_give isl_aff *isl_aff_set_coefficient_si(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, int v); +__isl_give isl_aff *isl_aff_set_coefficient_val(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, __isl_take isl_val *v); +__isl_give isl_aff *isl_aff_add_constant_si(__isl_take isl_aff *aff, int v); +__isl_give isl_aff *isl_aff_add_constant_val(__isl_take isl_aff *aff, + __isl_take isl_val *v); +__isl_give isl_aff *isl_aff_add_constant_num_si(__isl_take isl_aff *aff, int v); +__isl_give isl_aff *isl_aff_add_coefficient_si(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, int v); +__isl_give isl_aff *isl_aff_add_coefficient_val(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, __isl_take isl_val *v); + +isl_bool isl_aff_is_cst(__isl_keep isl_aff *aff); + +__isl_give isl_aff *isl_aff_set_tuple_id(__isl_take isl_aff *aff, + enum isl_dim_type type, __isl_take isl_id *id); +__isl_give isl_aff *isl_aff_set_dim_name(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned pos, const char *s); +__isl_give isl_aff *isl_aff_set_dim_id(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); + +int isl_aff_find_dim_by_name(__isl_keep isl_aff *aff, enum isl_dim_type type, + const char *name); + +isl_bool isl_aff_plain_is_equal(__isl_keep isl_aff *aff1, + __isl_keep isl_aff *aff2); +isl_bool isl_aff_plain_is_zero(__isl_keep isl_aff *aff); +isl_bool isl_aff_is_nan(__isl_keep isl_aff *aff); + +__isl_give isl_aff *isl_aff_get_div(__isl_keep isl_aff *aff, int pos); + +__isl_give isl_aff *isl_aff_neg(__isl_take isl_aff *aff); +__isl_give isl_aff *isl_aff_ceil(__isl_take isl_aff *aff); +__isl_give isl_aff *isl_aff_floor(__isl_take isl_aff *aff); +__isl_give isl_aff *isl_aff_mod_val(__isl_take isl_aff *aff, + __isl_take isl_val *mod); + +__isl_give isl_aff *isl_aff_mul(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_aff *isl_aff_div(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_export +__isl_give isl_aff *isl_aff_add(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_aff *isl_aff_sub(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + +__isl_give isl_aff *isl_aff_scale_val(__isl_take isl_aff *aff, + __isl_take isl_val *v); +__isl_give isl_aff *isl_aff_scale_down_ui(__isl_take isl_aff *aff, unsigned f); +__isl_give isl_aff *isl_aff_scale_down_val(__isl_take isl_aff *aff, + __isl_take isl_val *v); + +__isl_give isl_aff *isl_aff_insert_dims(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_aff *isl_aff_add_dims(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned n); +__isl_give isl_aff *isl_aff_move_dims(__isl_take isl_aff *aff, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_aff *isl_aff_drop_dims(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_aff *isl_aff_project_domain_on_params(__isl_take isl_aff *aff); + +__isl_give isl_aff *isl_aff_align_params(__isl_take isl_aff *aff, + __isl_take isl_space *model); + +__isl_give isl_aff *isl_aff_gist(__isl_take isl_aff *aff, + __isl_take isl_set *context); +__isl_give isl_aff *isl_aff_gist_params(__isl_take isl_aff *aff, + __isl_take isl_set *context); + +__isl_give isl_aff *isl_aff_pullback_aff(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_overload +__isl_give isl_aff *isl_aff_pullback_multi_aff(__isl_take isl_aff *aff, + __isl_take isl_multi_aff *ma); + +__isl_give isl_basic_set *isl_aff_zero_basic_set(__isl_take isl_aff *aff); +__isl_give isl_basic_set *isl_aff_neg_basic_set(__isl_take isl_aff *aff); + +__isl_give isl_basic_set *isl_aff_eq_basic_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_set *isl_aff_eq_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_basic_set *isl_aff_le_basic_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_set *isl_aff_le_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_basic_set *isl_aff_ge_basic_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +__isl_give isl_set *isl_aff_ge_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + +__isl_constructor +__isl_give isl_aff *isl_aff_read_from_str(isl_ctx *ctx, const char *str); +__isl_give char *isl_aff_to_str(__isl_keep isl_aff *aff); +__isl_give isl_printer *isl_printer_print_aff(__isl_take isl_printer *p, + __isl_keep isl_aff *aff); +void isl_aff_dump(__isl_keep isl_aff *aff); + +isl_ctx *isl_pw_aff_get_ctx(__isl_keep isl_pw_aff *pwaff); +uint32_t isl_pw_aff_get_hash(__isl_keep isl_pw_aff *pa); +__isl_give isl_space *isl_pw_aff_get_domain_space(__isl_keep isl_pw_aff *pwaff); +__isl_give isl_space *isl_pw_aff_get_space(__isl_keep isl_pw_aff *pwaff); + +__isl_constructor +__isl_give isl_pw_aff *isl_pw_aff_from_aff(__isl_take isl_aff *aff); +__isl_give isl_pw_aff *isl_pw_aff_empty(__isl_take isl_space *dim); +__isl_give isl_pw_aff *isl_pw_aff_alloc(__isl_take isl_set *set, + __isl_take isl_aff *aff); +__isl_give isl_pw_aff *isl_pw_aff_zero_on_domain( + __isl_take isl_local_space *ls); +__isl_give isl_pw_aff *isl_pw_aff_var_on_domain(__isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos); +__isl_give isl_pw_aff *isl_pw_aff_nan_on_domain(__isl_take isl_local_space *ls); +__isl_give isl_pw_aff *isl_pw_aff_val_on_domain(__isl_take isl_set *domain, + __isl_take isl_val *v); + +__isl_give isl_pw_aff *isl_set_indicator_function(__isl_take isl_set *set); + +const char *isl_pw_aff_get_dim_name(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type, unsigned pos); +isl_bool isl_pw_aff_has_dim_id(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type, unsigned pos); +__isl_give isl_id *isl_pw_aff_get_dim_id(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type, unsigned pos); +__isl_give isl_pw_aff *isl_pw_aff_set_dim_id(__isl_take isl_pw_aff *pma, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); + +int isl_pw_aff_find_dim_by_name(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type, const char *name); + +isl_bool isl_pw_aff_is_empty(__isl_keep isl_pw_aff *pwaff); +isl_bool isl_pw_aff_involves_nan(__isl_keep isl_pw_aff *pa); +int isl_pw_aff_plain_cmp(__isl_keep isl_pw_aff *pa1, + __isl_keep isl_pw_aff *pa2); +isl_bool isl_pw_aff_plain_is_equal(__isl_keep isl_pw_aff *pwaff1, + __isl_keep isl_pw_aff *pwaff2); +int isl_pw_aff_is_equal(__isl_keep isl_pw_aff *pa1, __isl_keep isl_pw_aff *pa2); + +__isl_give isl_pw_aff *isl_pw_aff_union_min(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_pw_aff *isl_pw_aff_union_max(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_export +__isl_give isl_pw_aff *isl_pw_aff_union_add(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + +__isl_give isl_pw_aff *isl_pw_aff_copy(__isl_keep isl_pw_aff *pwaff); +__isl_null isl_pw_aff *isl_pw_aff_free(__isl_take isl_pw_aff *pwaff); + +unsigned isl_pw_aff_dim(__isl_keep isl_pw_aff *pwaff, enum isl_dim_type type); +isl_bool isl_pw_aff_involves_dims(__isl_keep isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned first, unsigned n); + +isl_bool isl_pw_aff_is_cst(__isl_keep isl_pw_aff *pwaff); + +__isl_give isl_pw_aff *isl_pw_aff_align_params(__isl_take isl_pw_aff *pwaff, + __isl_take isl_space *model); + +isl_bool isl_pw_aff_has_tuple_id(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type); +__isl_give isl_id *isl_pw_aff_get_tuple_id(__isl_keep isl_pw_aff *pa, + enum isl_dim_type type); +__isl_give isl_pw_aff *isl_pw_aff_set_tuple_id(__isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, __isl_take isl_id *id); +__isl_give isl_pw_aff *isl_pw_aff_reset_tuple_id(__isl_take isl_pw_aff *pa, + enum isl_dim_type type); +__isl_give isl_pw_aff *isl_pw_aff_reset_user(__isl_take isl_pw_aff *pa); + +__isl_give isl_set *isl_pw_aff_params(__isl_take isl_pw_aff *pwa); +__isl_give isl_set *isl_pw_aff_domain(__isl_take isl_pw_aff *pwaff); +__isl_give isl_pw_aff *isl_pw_aff_from_range(__isl_take isl_pw_aff *pwa); + +__isl_give isl_pw_aff *isl_pw_aff_min(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_pw_aff *isl_pw_aff_max(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_pw_aff *isl_pw_aff_mul(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_pw_aff *isl_pw_aff_div(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); +__isl_export +__isl_give isl_pw_aff *isl_pw_aff_add(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_pw_aff *isl_pw_aff_sub(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_pw_aff *isl_pw_aff_neg(__isl_take isl_pw_aff *pwaff); +__isl_give isl_pw_aff *isl_pw_aff_ceil(__isl_take isl_pw_aff *pwaff); +__isl_give isl_pw_aff *isl_pw_aff_floor(__isl_take isl_pw_aff *pwaff); +__isl_give isl_pw_aff *isl_pw_aff_mod_val(__isl_take isl_pw_aff *pa, + __isl_take isl_val *mod); +__isl_give isl_pw_aff *isl_pw_aff_tdiv_q(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); +__isl_give isl_pw_aff *isl_pw_aff_tdiv_r(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + +__isl_give isl_pw_aff *isl_pw_aff_intersect_params(__isl_take isl_pw_aff *pa, + __isl_take isl_set *set); +__isl_give isl_pw_aff *isl_pw_aff_intersect_domain(__isl_take isl_pw_aff *pa, + __isl_take isl_set *set); +__isl_give isl_pw_aff *isl_pw_aff_subtract_domain(__isl_take isl_pw_aff *pa, + __isl_take isl_set *set); + +__isl_give isl_pw_aff *isl_pw_aff_cond(__isl_take isl_pw_aff *cond, + __isl_take isl_pw_aff *pwaff_true, __isl_take isl_pw_aff *pwaff_false); + +__isl_give isl_pw_aff *isl_pw_aff_scale_val(__isl_take isl_pw_aff *pa, + __isl_take isl_val *v); +__isl_give isl_pw_aff *isl_pw_aff_scale_down_val(__isl_take isl_pw_aff *pa, + __isl_take isl_val *f); + +__isl_give isl_pw_aff *isl_pw_aff_insert_dims(__isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_pw_aff *isl_pw_aff_add_dims(__isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned n); +__isl_give isl_pw_aff *isl_pw_aff_move_dims(__isl_take isl_pw_aff *pa, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_pw_aff *isl_pw_aff_drop_dims(__isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_pw_aff *isl_pw_aff_coalesce(__isl_take isl_pw_aff *pwqp); +__isl_give isl_pw_aff *isl_pw_aff_gist(__isl_take isl_pw_aff *pwaff, + __isl_take isl_set *context); +__isl_give isl_pw_aff *isl_pw_aff_gist_params(__isl_take isl_pw_aff *pwaff, + __isl_take isl_set *context); + +__isl_overload +__isl_give isl_pw_aff *isl_pw_aff_pullback_multi_aff( + __isl_take isl_pw_aff *pa, __isl_take isl_multi_aff *ma); +__isl_overload +__isl_give isl_pw_aff *isl_pw_aff_pullback_pw_multi_aff( + __isl_take isl_pw_aff *pa, __isl_take isl_pw_multi_aff *pma); +__isl_overload +__isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff( + __isl_take isl_pw_aff *pa, __isl_take isl_multi_pw_aff *mpa); + +int isl_pw_aff_n_piece(__isl_keep isl_pw_aff *pwaff); +isl_stat isl_pw_aff_foreach_piece(__isl_keep isl_pw_aff *pwaff, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_aff *aff, + void *user), void *user); + +__isl_give isl_set *isl_set_from_pw_aff(__isl_take isl_pw_aff *pwaff); +__isl_give isl_map *isl_map_from_pw_aff(__isl_take isl_pw_aff *pwaff); + +__isl_give isl_set *isl_pw_aff_pos_set(__isl_take isl_pw_aff *pa); +__isl_give isl_set *isl_pw_aff_nonneg_set(__isl_take isl_pw_aff *pwaff); +__isl_give isl_set *isl_pw_aff_zero_set(__isl_take isl_pw_aff *pwaff); +__isl_give isl_set *isl_pw_aff_non_zero_set(__isl_take isl_pw_aff *pwaff); + +__isl_give isl_set *isl_pw_aff_eq_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_set *isl_pw_aff_ne_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_set *isl_pw_aff_le_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_set *isl_pw_aff_lt_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_set *isl_pw_aff_ge_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); +__isl_give isl_set *isl_pw_aff_gt_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2); + +__isl_give isl_map *isl_pw_aff_eq_map(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); +__isl_give isl_map *isl_pw_aff_lt_map(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); +__isl_give isl_map *isl_pw_aff_gt_map(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2); + +__isl_constructor +__isl_give isl_pw_aff *isl_pw_aff_read_from_str(isl_ctx *ctx, const char *str); +__isl_give char *isl_pw_aff_to_str(__isl_keep isl_pw_aff *pa); +__isl_give isl_printer *isl_printer_print_pw_aff(__isl_take isl_printer *p, + __isl_keep isl_pw_aff *pwaff); +void isl_pw_aff_dump(__isl_keep isl_pw_aff *pwaff); + +__isl_give isl_pw_aff *isl_pw_aff_list_min(__isl_take isl_pw_aff_list *list); +__isl_give isl_pw_aff *isl_pw_aff_list_max(__isl_take isl_pw_aff_list *list); + +__isl_give isl_set *isl_pw_aff_list_eq_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); +__isl_give isl_set *isl_pw_aff_list_ne_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); +__isl_give isl_set *isl_pw_aff_list_le_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); +__isl_give isl_set *isl_pw_aff_list_lt_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); +__isl_give isl_set *isl_pw_aff_list_ge_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); +__isl_give isl_set *isl_pw_aff_list_gt_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2); + +ISL_DECLARE_MULTI(aff) +ISL_DECLARE_MULTI_NEG(aff) +ISL_DECLARE_MULTI_DIMS(aff) +ISL_DECLARE_MULTI_WITH_DOMAIN(aff) + +__isl_constructor +__isl_give isl_multi_aff *isl_multi_aff_from_aff(__isl_take isl_aff *aff); +__isl_give isl_multi_aff *isl_multi_aff_identity(__isl_take isl_space *space); +__isl_give isl_multi_aff *isl_multi_aff_domain_map(__isl_take isl_space *space); +__isl_give isl_multi_aff *isl_multi_aff_range_map(__isl_take isl_space *space); +__isl_give isl_multi_aff *isl_multi_aff_project_out_map( + __isl_take isl_space *space, enum isl_dim_type type, + unsigned first, unsigned n); + +__isl_give isl_multi_aff *isl_multi_aff_multi_val_on_space( + __isl_take isl_space *space, __isl_take isl_multi_val *mv); + +__isl_give isl_multi_aff *isl_multi_aff_floor(__isl_take isl_multi_aff *ma); + +__isl_give isl_multi_aff *isl_multi_aff_gist_params( + __isl_take isl_multi_aff *maff, __isl_take isl_set *context); +__isl_give isl_multi_aff *isl_multi_aff_gist(__isl_take isl_multi_aff *maff, + __isl_take isl_set *context); + +__isl_give isl_multi_aff *isl_multi_aff_lift(__isl_take isl_multi_aff *maff, + __isl_give isl_local_space **ls); + +__isl_overload +__isl_give isl_multi_aff *isl_multi_aff_pullback_multi_aff( + __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2); + +__isl_give isl_multi_aff *isl_multi_aff_move_dims(__isl_take isl_multi_aff *ma, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); + +__isl_give isl_set *isl_multi_aff_lex_lt_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); +__isl_give isl_set *isl_multi_aff_lex_le_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); +__isl_give isl_set *isl_multi_aff_lex_gt_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); +__isl_give isl_set *isl_multi_aff_lex_ge_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2); + +__isl_give char *isl_multi_aff_to_str(__isl_keep isl_multi_aff *ma); +__isl_give isl_printer *isl_printer_print_multi_aff(__isl_take isl_printer *p, + __isl_keep isl_multi_aff *maff); + +__isl_constructor +__isl_give isl_multi_aff *isl_multi_aff_read_from_str(isl_ctx *ctx, + const char *str); +void isl_multi_aff_dump(__isl_keep isl_multi_aff *maff); + +ISL_DECLARE_MULTI(pw_aff) +ISL_DECLARE_MULTI_NEG(pw_aff) +ISL_DECLARE_MULTI_DIMS(pw_aff) +ISL_DECLARE_MULTI_WITH_DOMAIN(pw_aff) + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_zero(__isl_take isl_space *space); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity( + __isl_take isl_space *space); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_map( + __isl_take isl_space *space); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_out_map( + __isl_take isl_space *space, enum isl_dim_type type, + unsigned first, unsigned n); +__isl_constructor +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_aff( + __isl_take isl_multi_aff *ma); +__isl_constructor +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_pw_aff( + __isl_take isl_pw_aff *pa); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_alloc(__isl_take isl_set *set, + __isl_take isl_multi_aff *maff); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_copy( + __isl_keep isl_pw_multi_aff *pma); +__isl_null isl_pw_multi_aff *isl_pw_multi_aff_free( + __isl_take isl_pw_multi_aff *pma); + +unsigned isl_pw_multi_aff_dim(__isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); +__isl_give isl_pw_aff *isl_pw_multi_aff_get_pw_aff( + __isl_keep isl_pw_multi_aff *pma, int pos); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_pw_aff( + __isl_take isl_pw_multi_aff *pma, unsigned pos, + __isl_take isl_pw_aff *pa); + +isl_ctx *isl_pw_multi_aff_get_ctx(__isl_keep isl_pw_multi_aff *pma); +__isl_give isl_space *isl_pw_multi_aff_get_domain_space( + __isl_keep isl_pw_multi_aff *pma); +__isl_give isl_space *isl_pw_multi_aff_get_space( + __isl_keep isl_pw_multi_aff *pma); +isl_bool isl_pw_multi_aff_has_tuple_name(__isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); +const char *isl_pw_multi_aff_get_tuple_name(__isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); +__isl_give isl_id *isl_pw_multi_aff_get_tuple_id( + __isl_keep isl_pw_multi_aff *pma, enum isl_dim_type type); +isl_bool isl_pw_multi_aff_has_tuple_id(__isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_tuple_id( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, __isl_take isl_id *id); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_tuple_id( + __isl_take isl_pw_multi_aff *pma, enum isl_dim_type type); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_user( + __isl_take isl_pw_multi_aff *pma); + +int isl_pw_multi_aff_find_dim_by_name(__isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type, const char *name); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_drop_dims( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_set *isl_pw_multi_aff_domain(__isl_take isl_pw_multi_aff *pma); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_empty(__isl_take isl_space *space); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_domain( + __isl_take isl_set *set); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_multi_val_on_domain( + __isl_take isl_set *domain, __isl_take isl_multi_val *mv); + +const char *isl_pw_multi_aff_get_dim_name(__isl_keep isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned pos); +__isl_give isl_id *isl_pw_multi_aff_get_dim_id( + __isl_keep isl_pw_multi_aff *pma, enum isl_dim_type type, + unsigned pos); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_dim_id( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); + +isl_bool isl_pw_multi_aff_plain_is_equal(__isl_keep isl_pw_multi_aff *pma1, + __isl_keep isl_pw_multi_aff *pma2); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_fix_si( + __isl_take isl_pw_multi_aff *pma, enum isl_dim_type type, + unsigned pos, int value); + +__isl_export +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_add( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_neg( + __isl_take isl_pw_multi_aff *pma); + +__isl_export +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_sub( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_val( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_val *v); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_down_val( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_val *v); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_multi_val( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_val *mv); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmin( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmax( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2); + +__isl_give isl_multi_aff *isl_multi_aff_flatten_domain( + __isl_take isl_multi_aff *ma); + +__isl_export +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); +__isl_export +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_flat_range_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); +__isl_export +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_params( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_domain( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_subtract_domain( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_domain_on_params( + __isl_take isl_pw_multi_aff *pma); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_align_params( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_space *model); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_coalesce( + __isl_take isl_pw_multi_aff *pma); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_gist_params( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_gist( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set); + +__isl_overload +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_pullback_multi_aff( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_aff *ma); +__isl_overload +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_pullback_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); + +isl_stat isl_pw_multi_aff_foreach_piece(__isl_keep isl_pw_multi_aff *pma, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_multi_aff *maff, + void *user), void *user); + +__isl_give isl_map *isl_map_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma); +__isl_give isl_set *isl_set_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma); + +__isl_give char *isl_pw_multi_aff_to_str(__isl_keep isl_pw_multi_aff *pma); +__isl_give isl_printer *isl_printer_print_pw_multi_aff(__isl_take isl_printer *p, + __isl_keep isl_pw_multi_aff *pma); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_set(__isl_take isl_set *set); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_map(__isl_take isl_map *map); + +__isl_constructor +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_read_from_str(isl_ctx *ctx, + const char *str); +void isl_pw_multi_aff_dump(__isl_keep isl_pw_multi_aff *pma); + + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_empty( + __isl_take isl_space *space); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_aff( + __isl_take isl_aff *aff); +__isl_constructor +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_domain( + __isl_take isl_union_set *uset); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_multi_val_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_copy( + __isl_keep isl_union_pw_multi_aff *upma); +__isl_null isl_union_pw_multi_aff *isl_union_pw_multi_aff_free( + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_union_pw_multi_aff *isl_union_set_identity_union_pw_multi_aff( + __isl_take isl_union_set *uset); + +__isl_give isl_union_pw_aff *isl_union_pw_multi_aff_get_union_pw_aff( + __isl_keep isl_union_pw_multi_aff *upma, int pos); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_add_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_pw_multi_aff *pma); + +isl_ctx *isl_union_pw_multi_aff_get_ctx( + __isl_keep isl_union_pw_multi_aff *upma); +__isl_give isl_space *isl_union_pw_multi_aff_get_space( + __isl_keep isl_union_pw_multi_aff *upma); + +unsigned isl_union_pw_multi_aff_dim(__isl_keep isl_union_pw_multi_aff *upma, + enum isl_dim_type type); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_set_dim_name( + __isl_take isl_union_pw_multi_aff *upma, + enum isl_dim_type type, unsigned pos, const char *s); + +int isl_union_pw_multi_aff_find_dim_by_name( + __isl_keep isl_union_pw_multi_aff *upma, enum isl_dim_type type, + const char *name); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_drop_dims( + __isl_take isl_union_pw_multi_aff *upma, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_reset_user( + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_coalesce( + __isl_take isl_union_pw_multi_aff *upma); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_gist_params( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_set *context); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_gist( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_union_set *context); + +__isl_overload +__isl_give isl_union_pw_multi_aff * +isl_union_pw_multi_aff_pullback_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_align_params( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_space *model); + +int isl_union_pw_multi_aff_n_pw_multi_aff( + __isl_keep isl_union_pw_multi_aff *upma); + +isl_stat isl_union_pw_multi_aff_foreach_pw_multi_aff( + __isl_keep isl_union_pw_multi_aff *upma, + isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, void *user), + void *user); +__isl_give isl_pw_multi_aff *isl_union_pw_multi_aff_extract_pw_multi_aff( + __isl_keep isl_union_pw_multi_aff *upma, __isl_take isl_space *space); + +isl_bool isl_union_pw_multi_aff_plain_is_equal( + __isl_keep isl_union_pw_multi_aff *upma1, + __isl_keep isl_union_pw_multi_aff *upma2); + +__isl_give isl_union_set *isl_union_pw_multi_aff_domain( + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_neg( + __isl_take isl_union_pw_multi_aff *upma); + +__isl_export +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_add( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); +__isl_export +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_union_add( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_sub( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_val( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_val *val); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_down_val( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_val *val); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_multi_val( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_multi_val *mv); + +__isl_export +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_flat_range_product( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_intersect_params( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_set *set); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_intersect_domain( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_union_set *uset); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_subtract_domain( + __isl_take isl_union_pw_multi_aff *upma, + __isl_take isl_union_set *uset); + +__isl_overload +__isl_give isl_union_map *isl_union_map_from_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_printer *isl_printer_print_union_pw_multi_aff( + __isl_take isl_printer *p, __isl_keep isl_union_pw_multi_aff *upma); + +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_set( + __isl_take isl_union_set *uset); +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_map( + __isl_take isl_union_map *umap); + +__isl_constructor +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_read_from_str( + isl_ctx *ctx, const char *str); +void isl_union_pw_multi_aff_dump(__isl_keep isl_union_pw_multi_aff *upma); +__isl_give char *isl_union_pw_multi_aff_to_str( + __isl_keep isl_union_pw_multi_aff *upma); + +uint32_t isl_multi_pw_aff_get_hash(__isl_keep isl_multi_pw_aff *mpa); + +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_identity( + __isl_take isl_space *space); +__isl_constructor +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_multi_aff( + __isl_take isl_multi_aff *ma); +__isl_constructor +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_aff( + __isl_take isl_pw_aff *pa); +__isl_give isl_set *isl_multi_pw_aff_domain(__isl_take isl_multi_pw_aff *mpa); +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_intersect_params( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *set); +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_intersect_domain( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *domain); + +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_coalesce( + __isl_take isl_multi_pw_aff *mpa); +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_gist( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *set); +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_gist_params( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *set); + +isl_bool isl_multi_pw_aff_is_cst(__isl_keep isl_multi_pw_aff *mpa); +isl_bool isl_multi_pw_aff_is_equal(__isl_keep isl_multi_pw_aff *mpa1, + __isl_keep isl_multi_pw_aff *mpa2); + +__isl_overload +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma); +__isl_overload +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_pw_multi_aff( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_multi_aff *pma); +__isl_overload +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2); + +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_move_dims( + __isl_take isl_multi_pw_aff *pma, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); + +__isl_give isl_set *isl_set_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa); +__isl_give isl_map *isl_map_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa); +__isl_constructor +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma); + +__isl_give isl_map *isl_multi_pw_aff_eq_map(__isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); +__isl_give isl_map *isl_multi_pw_aff_lex_lt_map( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2); +__isl_give isl_map *isl_multi_pw_aff_lex_gt_map( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2); + +__isl_constructor +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give char *isl_multi_pw_aff_to_str(__isl_keep isl_multi_pw_aff *mpa); +__isl_give isl_printer *isl_printer_print_multi_pw_aff( + __isl_take isl_printer *p, __isl_keep isl_multi_pw_aff *mpa); +void isl_multi_pw_aff_dump(__isl_keep isl_multi_pw_aff *mpa); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_copy( + __isl_keep isl_union_pw_aff *upa); +__isl_null isl_union_pw_aff *isl_union_pw_aff_free( + __isl_take isl_union_pw_aff *upa); + +isl_ctx *isl_union_pw_aff_get_ctx(__isl_keep isl_union_pw_aff *upa); +__isl_give isl_space *isl_union_pw_aff_get_space( + __isl_keep isl_union_pw_aff *upa); + +unsigned isl_union_pw_aff_dim(__isl_keep isl_union_pw_aff *upa, + enum isl_dim_type type); +__isl_give isl_union_pw_aff *isl_union_pw_aff_set_dim_name( + __isl_take isl_union_pw_aff *upa, enum isl_dim_type type, + unsigned pos, const char *s); + +int isl_union_pw_aff_find_dim_by_name(__isl_keep isl_union_pw_aff *upa, + enum isl_dim_type type, const char *name); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_drop_dims( + __isl_take isl_union_pw_aff *upa, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_union_pw_aff *isl_union_pw_aff_reset_user( + __isl_take isl_union_pw_aff *upa); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_empty( + __isl_take isl_space *space); +__isl_constructor +__isl_give isl_union_pw_aff *isl_union_pw_aff_from_pw_aff( + __isl_take isl_pw_aff *pa); +__isl_give isl_union_pw_aff *isl_union_pw_aff_val_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_val *v); +__isl_give isl_union_pw_aff *isl_union_pw_aff_aff_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_aff *aff); +__isl_give isl_union_pw_aff *isl_union_pw_aff_add_pw_aff( + __isl_take isl_union_pw_aff *upa, __isl_take isl_pw_aff *pa); + +__isl_constructor +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa); + +int isl_union_pw_aff_n_pw_aff(__isl_keep isl_union_pw_aff *upa); + +isl_stat isl_union_pw_aff_foreach_pw_aff(__isl_keep isl_union_pw_aff *upa, + isl_stat (*fn)(__isl_take isl_pw_aff *pa, void *user), void *user); +__isl_give isl_pw_aff *isl_union_pw_aff_extract_pw_aff( + __isl_keep isl_union_pw_aff *upa, __isl_take isl_space *space); + +isl_bool isl_union_pw_aff_plain_is_equal(__isl_keep isl_union_pw_aff *upa1, + __isl_keep isl_union_pw_aff *upa2); + +__isl_give isl_union_set *isl_union_pw_aff_domain( + __isl_take isl_union_pw_aff *upa); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_neg( + __isl_take isl_union_pw_aff *upa); + +__isl_export +__isl_give isl_union_pw_aff *isl_union_pw_aff_add( + __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2); +__isl_export +__isl_give isl_union_pw_aff *isl_union_pw_aff_union_add( + __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2); +__isl_give isl_union_pw_aff *isl_union_pw_aff_sub( + __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_coalesce( + __isl_take isl_union_pw_aff *upa); +__isl_give isl_union_pw_aff *isl_union_pw_aff_gist( + __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *context); +__isl_give isl_union_pw_aff *isl_union_pw_aff_gist_params( + __isl_take isl_union_pw_aff *upa, __isl_take isl_set *context); + +__isl_overload +__isl_give isl_union_pw_aff *isl_union_pw_aff_pullback_union_pw_multi_aff( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_floor( + __isl_take isl_union_pw_aff *upa); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_scale_val( + __isl_take isl_union_pw_aff *upa, __isl_take isl_val *v); +__isl_give isl_union_pw_aff *isl_union_pw_aff_scale_down_val( + __isl_take isl_union_pw_aff *upa, __isl_take isl_val *v); +__isl_give isl_union_pw_aff *isl_union_pw_aff_mod_val( + __isl_take isl_union_pw_aff *upa, __isl_take isl_val *f); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_align_params( + __isl_take isl_union_pw_aff *upa, __isl_take isl_space *model); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_params( + __isl_take isl_union_pw_aff *upa, __isl_take isl_set *set); +__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain( + __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset); +__isl_give isl_union_pw_aff *isl_union_pw_aff_subtract_domain( + __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset); + +__isl_give isl_union_pw_aff *isl_union_pw_aff_set_dim_name( + __isl_take isl_union_pw_aff *upa, + enum isl_dim_type type, unsigned pos, const char *s); + +__isl_give isl_union_set *isl_union_pw_aff_zero_union_set( + __isl_take isl_union_pw_aff *upa); + +__isl_give isl_union_map *isl_union_map_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa); + +__isl_constructor +__isl_give isl_union_pw_aff *isl_union_pw_aff_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give char *isl_union_pw_aff_to_str(__isl_keep isl_union_pw_aff *upa); +__isl_give isl_printer *isl_printer_print_union_pw_aff( + __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa); + +ISL_DECLARE_MULTI(union_pw_aff) +ISL_DECLARE_MULTI_NEG(union_pw_aff) + +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_aff( + __isl_take isl_multi_aff *ma); +__isl_constructor +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa); +__isl_constructor +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_val_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_aff_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_multi_aff *ma); + +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_floor( + __isl_take isl_multi_union_pw_aff *mupa); + +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_domain( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_union_set *uset); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_params( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *params); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_range( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *set); + +__isl_give isl_union_set *isl_multi_union_pw_aff_domain( + __isl_take isl_multi_union_pw_aff *mupa); + +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_coalesce( + __isl_take isl_multi_union_pw_aff *aff); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_gist( + __isl_take isl_multi_union_pw_aff *aff, + __isl_take isl_union_set *context); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_gist_params( + __isl_take isl_multi_union_pw_aff *aff, __isl_take isl_set *context); + +__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_aff *aff); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma); +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_pw_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_pw_multi_aff *pma); + +__isl_overload +__isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_pullback_union_pw_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_union_pw_multi_aff * +isl_union_pw_multi_aff_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa); + +__isl_export +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_union_add( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2); + +__isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_from_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_union_map( + __isl_take isl_union_map *umap); +__isl_overload +__isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa); + +__isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set( + __isl_take isl_multi_union_pw_aff *mupa); + +__isl_give isl_multi_pw_aff *isl_multi_union_pw_aff_extract_multi_pw_aff( + __isl_keep isl_multi_union_pw_aff *mupa, __isl_take isl_space *space); + +__isl_constructor +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_read_from_str( + isl_ctx *ctx, const char *str); +__isl_give char *isl_multi_union_pw_aff_to_str( + __isl_keep isl_multi_union_pw_aff *mupa); +__isl_give isl_printer *isl_printer_print_multi_union_pw_aff( + __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa); +void isl_multi_union_pw_aff_dump(__isl_keep isl_multi_union_pw_aff *mupa); + +ISL_DECLARE_LIST_FN(union_pw_aff) +ISL_DECLARE_LIST_FN(union_pw_multi_aff) + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/aff_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/aff_type.h @@ -0,0 +1,50 @@ +#ifndef ISL_AFF_TYPE_H +#define ISL_AFF_TYPE_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct __isl_subclass(isl_multi_aff) __isl_subclass(isl_pw_aff) isl_aff; +typedef struct isl_aff isl_aff; + +ISL_DECLARE_LIST(aff) + +struct __isl_subclass(isl_pw_multi_aff) __isl_subclass(isl_multi_pw_aff) + __isl_subclass(isl_union_pw_aff) isl_pw_aff; +typedef struct isl_pw_aff isl_pw_aff; + +ISL_DECLARE_LIST(pw_aff) + +struct __isl_subclass(isl_multi_union_pw_aff) + __isl_subclass(isl_union_pw_multi_aff) isl_union_pw_aff; +typedef struct isl_union_pw_aff isl_union_pw_aff; + +ISL_DECLARE_LIST_TYPE(union_pw_aff) + +struct __isl_subclass(isl_pw_multi_aff) __isl_subclass(isl_multi_pw_aff) + isl_multi_aff; +typedef struct isl_multi_aff isl_multi_aff; + +struct __isl_subclass(isl_union_pw_multi_aff) __isl_subclass(isl_multi_pw_aff) + isl_pw_multi_aff; +typedef struct isl_pw_multi_aff isl_pw_multi_aff; + +struct __isl_export isl_union_pw_multi_aff; +typedef struct isl_union_pw_multi_aff isl_union_pw_multi_aff; + +ISL_DECLARE_LIST_TYPE(union_pw_multi_aff) + +struct __isl_subclass(isl_multi_union_pw_aff) isl_multi_pw_aff; +typedef struct isl_multi_pw_aff isl_multi_pw_aff; + +struct __isl_export isl_multi_union_pw_aff; +typedef struct isl_multi_union_pw_aff isl_multi_union_pw_aff; + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/arg.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/arg.h @@ -0,0 +1,324 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_ARG_H +#define ISL_ARG_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_arg_choice { + const char *name; + unsigned value; +}; + +struct isl_arg_flags { + const char *name; + unsigned mask; + unsigned value; +}; + +enum isl_arg_type { + isl_arg_end, + isl_arg_alias, + isl_arg_arg, + isl_arg_bool, + isl_arg_child, + isl_arg_choice, + isl_arg_flags, + isl_arg_footer, + isl_arg_int, + isl_arg_user, + isl_arg_long, + isl_arg_ulong, + isl_arg_str, + isl_arg_str_list, + isl_arg_version +}; + +struct isl_args; + +struct isl_arg { + enum isl_arg_type type; + char short_name; + const char *long_name; + const char *argument_name; + size_t offset; + const char *help_msg; +#define ISL_ARG_SINGLE_DASH (1 << 0) +#define ISL_ARG_BOOL_ARG (1 << 1) +#define ISL_ARG_HIDDEN (1 << 2) + unsigned flags; + union { + struct { + struct isl_arg_choice *choice; + unsigned default_value; + unsigned default_selected; + int (*set)(void *opt, unsigned val); + } choice; + struct { + struct isl_arg_flags *flags; + unsigned default_value; + } flags; + struct { + unsigned default_value; + int (*set)(void *opt, unsigned val); + } b; + struct { + int default_value; + } i; + struct { + long default_value; + long default_selected; + int (*set)(void *opt, long val); + } l; + struct { + unsigned long default_value; + } ul; + struct { + const char *default_value; + } str; + struct { + size_t offset_n; + } str_list; + struct { + struct isl_args *child; + } child; + struct { + void (*print_version)(void); + } version; + struct { + int (*init)(void*); + void (*clear)(void*); + } user; + } u; +}; + +struct isl_args { + size_t options_size; + struct isl_arg *args; +}; + +#define ISL_ARGS_START(s,name) \ + struct isl_arg name ## LIST[]; \ + struct isl_args name = { sizeof(s), name ## LIST }; \ + struct isl_arg name ## LIST[] = { +#define ISL_ARGS_END \ + { isl_arg_end } }; + +#define ISL_ARG_ALIAS(l) { \ + .type = isl_arg_alias, \ + .long_name = l, \ +}, +#define ISL_ARG_ARG(st,f,a,d) { \ + .type = isl_arg_arg, \ + .argument_name = a, \ + .offset = offsetof(st, f), \ + .u = { .str = { .default_value = d } } \ +}, +#define ISL_ARG_FOOTER(h) { \ + .type = isl_arg_footer, \ + .help_msg = h, \ +}, +#define ISL_ARG_CHOICE(st,f,s,l,c,d,h) { \ + .type = isl_arg_choice, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .choice = { .choice = c, .default_value = d, \ + .default_selected = d, .set = NULL } } \ +}, +#define ISL_ARG_OPT_CHOICE(st,f,s,l,c,d,ds,h) { \ + .type = isl_arg_choice, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .choice = { .choice = c, .default_value = d, \ + .default_selected = ds, .set = NULL } } \ +}, +#define ISL_ARG_PHANTOM_USER_CHOICE_F(s,l,c,setter,d,h,fl) { \ + .type = isl_arg_choice, \ + .short_name = s, \ + .long_name = l, \ + .offset = -1, \ + .help_msg = h, \ + .flags = fl, \ + .u = { .choice = { .choice = c, .default_value = d, \ + .default_selected = d, .set = setter } } \ +}, +#define ISL_ARG_USER_OPT_CHOICE(st,f,s,l,c,setter,d,ds,h) { \ + .type = isl_arg_choice, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .choice = { .choice = c, .default_value = d, \ + .default_selected = ds, .set = setter } } \ +}, +#define _ISL_ARG_BOOL_F(o,s,l,setter,d,h,fl) { \ + .type = isl_arg_bool, \ + .short_name = s, \ + .long_name = l, \ + .offset = o, \ + .help_msg = h, \ + .flags = fl, \ + .u = { .b = { .default_value = d, .set = setter } } \ +}, +#define ISL_ARG_BOOL_F(st,f,s,l,d,h,fl) \ + _ISL_ARG_BOOL_F(offsetof(st, f),s,l,NULL,d,h,fl) +#define ISL_ARG_BOOL(st,f,s,l,d,h) \ + ISL_ARG_BOOL_F(st,f,s,l,d,h,0) +#define ISL_ARG_PHANTOM_BOOL_F(s,l,setter,h,fl) \ + _ISL_ARG_BOOL_F(-1,s,l,setter,0,h,fl) +#define ISL_ARG_PHANTOM_BOOL(s,l,setter,h) \ + ISL_ARG_PHANTOM_BOOL_F(s,l,setter,h,0) +#define ISL_ARG_INT_F(st,f,s,l,a,d,h,fl) { \ + .type = isl_arg_int, \ + .short_name = s, \ + .long_name = l, \ + .argument_name = a, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .flags = fl, \ + .u = { .ul = { .default_value = d } } \ +}, +#define ISL_ARG_INT(st,f,s,l,a,d,h) \ + ISL_ARG_INT_F(st,f,s,l,a,d,h,0) +#define ISL_ARG_LONG(st,f,s,lo,d,h) { \ + .type = isl_arg_long, \ + .short_name = s, \ + .long_name = lo, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .l = { .default_value = d, .default_selected = d, \ + .set = NULL } } \ +}, +#define ISL_ARG_USER_LONG(st,f,s,lo,setter,d,h) { \ + .type = isl_arg_long, \ + .short_name = s, \ + .long_name = lo, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .l = { .default_value = d, .default_selected = d, \ + .set = setter } } \ +}, +#define ISL_ARG_OPT_LONG(st,f,s,lo,d,ds,h) { \ + .type = isl_arg_long, \ + .short_name = s, \ + .long_name = lo, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .l = { .default_value = d, .default_selected = ds, \ + .set = NULL } } \ +}, +#define ISL_ARG_ULONG(st,f,s,l,d,h) { \ + .type = isl_arg_ulong, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .ul = { .default_value = d } } \ +}, +#define ISL_ARG_STR_F(st,f,s,l,a,d,h,fl) { \ + .type = isl_arg_str, \ + .short_name = s, \ + .long_name = l, \ + .argument_name = a, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .flags = fl, \ + .u = { .str = { .default_value = d } } \ +}, +#define ISL_ARG_STR(st,f,s,l,a,d,h) \ + ISL_ARG_STR_F(st,f,s,l,a,d,h,0) +#define ISL_ARG_STR_LIST(st,f_n,f_l,s,l,a,h) { \ + .type = isl_arg_str_list, \ + .short_name = s, \ + .long_name = l, \ + .argument_name = a, \ + .offset = offsetof(st, f_l), \ + .help_msg = h, \ + .u = { .str_list = { .offset_n = offsetof(st, f_n) } } \ +}, +#define _ISL_ARG_CHILD(o,l,c,h,fl) { \ + .type = isl_arg_child, \ + .long_name = l, \ + .offset = o, \ + .help_msg = h, \ + .flags = fl, \ + .u = { .child = { .child = c } } \ +}, +#define ISL_ARG_CHILD(st,f,l,c,h) \ + _ISL_ARG_CHILD(offsetof(st, f),l,c,h,0) +#define ISL_ARG_GROUP_F(l,c,h,fl) \ + _ISL_ARG_CHILD(-1,l,c,h,fl) +#define ISL_ARG_GROUP(l,c,h) \ + ISL_ARG_GROUP_F(l,c,h,0) +#define ISL_ARG_FLAGS(st,f,s,l,c,d,h) { \ + .type = isl_arg_flags, \ + .short_name = s, \ + .long_name = l, \ + .offset = offsetof(st, f), \ + .help_msg = h, \ + .u = { .flags = { .flags = c, .default_value = d } } \ +}, +#define ISL_ARG_USER(st,f,i,c) { \ + .type = isl_arg_user, \ + .offset = offsetof(st, f), \ + .u = { .user = { .init = i, .clear = c} } \ +}, +#define ISL_ARG_VERSION(print) { \ + .type = isl_arg_version, \ + .u = { .version = { .print_version = print } } \ +}, + +#define ISL_ARG_ALL (1 << 0) +#define ISL_ARG_SKIP_HELP (1 << 1) + +void isl_args_set_defaults(struct isl_args *args, void *opt); +void isl_args_free(struct isl_args *args, void *opt); +int isl_args_parse(struct isl_args *args, int argc, char **argv, void *opt, + unsigned flags); + +#define ISL_ARG_DECL(prefix,st,args) \ +extern struct isl_args args; \ +st *prefix ## _new_with_defaults(void); \ +void prefix ## _free(st *opt); \ +int prefix ## _parse(st *opt, int argc, char **argv, unsigned flags); + +#define ISL_ARG_DEF(prefix,st,args) \ +st *prefix ## _new_with_defaults() \ +{ \ + st *opt = (st *)calloc(1, sizeof(st)); \ + if (opt) \ + isl_args_set_defaults(&(args), opt); \ + return opt; \ +} \ + \ +void prefix ## _free(st *opt) \ +{ \ + isl_args_free(&(args), opt); \ +} \ + \ +int prefix ## _parse(st *opt, int argc, char **argv, unsigned flags) \ +{ \ + return isl_args_parse(&(args), argc, argv, opt, flags); \ +} + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/ast.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/ast.h @@ -0,0 +1,187 @@ +#ifndef ISL_AST_H +#define ISL_AST_H + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +isl_stat isl_options_set_ast_iterator_type(isl_ctx *ctx, const char *val); +const char *isl_options_get_ast_iterator_type(isl_ctx *ctx); + +isl_stat isl_options_set_ast_always_print_block(isl_ctx *ctx, int val); +int isl_options_get_ast_always_print_block(isl_ctx *ctx); + +__isl_give isl_ast_expr *isl_ast_expr_from_val(__isl_take isl_val *v); +__isl_give isl_ast_expr *isl_ast_expr_from_id(__isl_take isl_id *id); +__isl_give isl_ast_expr *isl_ast_expr_neg(__isl_take isl_ast_expr *expr); +__isl_give isl_ast_expr *isl_ast_expr_add(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_sub(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_mul(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_div(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_pdiv_q(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_pdiv_r(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_and(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_and_then(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_or(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_or_else(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_le(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_lt(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_ge(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_gt(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_eq(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2); +__isl_give isl_ast_expr *isl_ast_expr_access(__isl_take isl_ast_expr *array, + __isl_take isl_ast_expr_list *indices); +__isl_give isl_ast_expr *isl_ast_expr_call(__isl_take isl_ast_expr *function, + __isl_take isl_ast_expr_list *arguments); +__isl_give isl_ast_expr *isl_ast_expr_address_of(__isl_take isl_ast_expr *expr); + +__isl_give isl_ast_expr *isl_ast_expr_copy(__isl_keep isl_ast_expr *expr); +__isl_null isl_ast_expr *isl_ast_expr_free(__isl_take isl_ast_expr *expr); + +isl_ctx *isl_ast_expr_get_ctx(__isl_keep isl_ast_expr *expr); +enum isl_ast_expr_type isl_ast_expr_get_type(__isl_keep isl_ast_expr *expr); +__isl_give isl_val *isl_ast_expr_get_val(__isl_keep isl_ast_expr *expr); +__isl_give isl_id *isl_ast_expr_get_id(__isl_keep isl_ast_expr *expr); + +enum isl_ast_op_type isl_ast_expr_get_op_type(__isl_keep isl_ast_expr *expr); +int isl_ast_expr_get_op_n_arg(__isl_keep isl_ast_expr *expr); +__isl_give isl_ast_expr *isl_ast_expr_get_op_arg(__isl_keep isl_ast_expr *expr, + int pos); +__isl_give isl_ast_expr *isl_ast_expr_set_op_arg(__isl_take isl_ast_expr *expr, + int pos, __isl_take isl_ast_expr *arg); + +isl_bool isl_ast_expr_is_equal(__isl_keep isl_ast_expr *expr1, + __isl_keep isl_ast_expr *expr2); + +__isl_give isl_ast_expr *isl_ast_expr_substitute_ids( + __isl_take isl_ast_expr *expr, __isl_take isl_id_to_ast_expr *id2expr); + +__isl_give isl_printer *isl_printer_print_ast_expr(__isl_take isl_printer *p, + __isl_keep isl_ast_expr *expr); +void isl_ast_expr_dump(__isl_keep isl_ast_expr *expr); +__isl_give char *isl_ast_expr_to_str(__isl_keep isl_ast_expr *expr); + +__isl_give isl_ast_node *isl_ast_node_alloc_user(__isl_take isl_ast_expr *expr); +__isl_give isl_ast_node *isl_ast_node_copy(__isl_keep isl_ast_node *node); +__isl_null isl_ast_node *isl_ast_node_free(__isl_take isl_ast_node *node); + +isl_ctx *isl_ast_node_get_ctx(__isl_keep isl_ast_node *node); +enum isl_ast_node_type isl_ast_node_get_type(__isl_keep isl_ast_node *node); + +__isl_give isl_ast_node *isl_ast_node_set_annotation( + __isl_take isl_ast_node *node, __isl_take isl_id *annotation); +__isl_give isl_id *isl_ast_node_get_annotation(__isl_keep isl_ast_node *node); + +__isl_give isl_ast_expr *isl_ast_node_for_get_iterator( + __isl_keep isl_ast_node *node); +__isl_give isl_ast_expr *isl_ast_node_for_get_init( + __isl_keep isl_ast_node *node); +__isl_give isl_ast_expr *isl_ast_node_for_get_cond( + __isl_keep isl_ast_node *node); +__isl_give isl_ast_expr *isl_ast_node_for_get_inc( + __isl_keep isl_ast_node *node); +__isl_give isl_ast_node *isl_ast_node_for_get_body( + __isl_keep isl_ast_node *node); +isl_bool isl_ast_node_for_is_degenerate(__isl_keep isl_ast_node *node); + +__isl_give isl_ast_expr *isl_ast_node_if_get_cond( + __isl_keep isl_ast_node *node); +__isl_give isl_ast_node *isl_ast_node_if_get_then( + __isl_keep isl_ast_node *node); +isl_bool isl_ast_node_if_has_else(__isl_keep isl_ast_node *node); +__isl_give isl_ast_node *isl_ast_node_if_get_else( + __isl_keep isl_ast_node *node); + +__isl_give isl_ast_node_list *isl_ast_node_block_get_children( + __isl_keep isl_ast_node *node); + +__isl_give isl_id *isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node); +__isl_give isl_ast_node *isl_ast_node_mark_get_node( + __isl_keep isl_ast_node *node); + +__isl_give isl_ast_expr *isl_ast_node_user_get_expr( + __isl_keep isl_ast_node *node); + +isl_stat isl_ast_node_foreach_descendant_top_down( + __isl_keep isl_ast_node *node, + isl_bool (*fn)(__isl_keep isl_ast_node *node, void *user), void *user); + +__isl_give isl_printer *isl_printer_print_ast_node(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node); +void isl_ast_node_dump(__isl_keep isl_ast_node *node); + +__isl_give isl_ast_print_options *isl_ast_print_options_alloc(isl_ctx *ctx); +__isl_give isl_ast_print_options *isl_ast_print_options_copy( + __isl_keep isl_ast_print_options *options); +__isl_null isl_ast_print_options *isl_ast_print_options_free( + __isl_take isl_ast_print_options *options); +isl_ctx *isl_ast_print_options_get_ctx( + __isl_keep isl_ast_print_options *options); + +__isl_give isl_ast_print_options *isl_ast_print_options_set_print_user( + __isl_take isl_ast_print_options *options, + __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user), + void *user); +__isl_give isl_ast_print_options *isl_ast_print_options_set_print_for( + __isl_take isl_ast_print_options *options, + __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user), + void *user); + +isl_stat isl_options_set_ast_print_macro_once(isl_ctx *ctx, int val); +int isl_options_get_ast_print_macro_once(isl_ctx *ctx); + +isl_stat isl_ast_expr_foreach_ast_op_type(__isl_keep isl_ast_expr *expr, + isl_stat (*fn)(enum isl_ast_op_type type, void *user), void *user); +isl_stat isl_ast_node_foreach_ast_op_type(__isl_keep isl_ast_node *node, + isl_stat (*fn)(enum isl_ast_op_type type, void *user), void *user); +__isl_give isl_printer *isl_ast_op_type_set_print_name( + __isl_take isl_printer *p, enum isl_ast_op_type type, + __isl_keep const char *name); +__isl_give isl_printer *isl_ast_op_type_print_macro( + enum isl_ast_op_type type, __isl_take isl_printer *p); +__isl_give isl_printer *isl_ast_expr_print_macros( + __isl_keep isl_ast_expr *expr, __isl_take isl_printer *p); +__isl_give isl_printer *isl_ast_node_print_macros( + __isl_keep isl_ast_node *node, __isl_take isl_printer *p); +__isl_give isl_printer *isl_ast_node_print(__isl_keep isl_ast_node *node, + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options); +__isl_give isl_printer *isl_ast_node_for_print(__isl_keep isl_ast_node *node, + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options); +__isl_give isl_printer *isl_ast_node_if_print(__isl_keep isl_ast_node *node, + __isl_take isl_printer *p, + __isl_take isl_ast_print_options *options); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/ast_build.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/ast_build.h @@ -0,0 +1,119 @@ +#ifndef ISL_AST_CONTEXT_H +#define ISL_AST_CONTEXT_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_ast_build; +typedef struct isl_ast_build isl_ast_build; + + +isl_stat isl_options_set_ast_build_atomic_upper_bound(isl_ctx *ctx, int val); +int isl_options_get_ast_build_atomic_upper_bound(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_prefer_pdiv(isl_ctx *ctx, int val); +int isl_options_get_ast_build_prefer_pdiv(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_detect_min_max(isl_ctx *ctx, int val); +int isl_options_get_ast_build_detect_min_max(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_exploit_nested_bounds(isl_ctx *ctx, int val); +int isl_options_get_ast_build_exploit_nested_bounds(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_group_coscheduled(isl_ctx *ctx, int val); +int isl_options_get_ast_build_group_coscheduled(isl_ctx *ctx); + +#define ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT 0 +#define ISL_AST_BUILD_SEPARATION_BOUNDS_IMPLICIT 1 +isl_stat isl_options_set_ast_build_separation_bounds(isl_ctx *ctx, int val); +int isl_options_get_ast_build_separation_bounds(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_scale_strides(isl_ctx *ctx, int val); +int isl_options_get_ast_build_scale_strides(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_allow_else(isl_ctx *ctx, int val); +int isl_options_get_ast_build_allow_else(isl_ctx *ctx); + +isl_stat isl_options_set_ast_build_allow_or(isl_ctx *ctx, int val); +int isl_options_get_ast_build_allow_or(isl_ctx *ctx); + +isl_ctx *isl_ast_build_get_ctx(__isl_keep isl_ast_build *build); + +__isl_give isl_ast_build *isl_ast_build_alloc(isl_ctx *ctx); +__isl_give isl_ast_build *isl_ast_build_from_context(__isl_take isl_set *set); + +__isl_give isl_space *isl_ast_build_get_schedule_space( + __isl_keep isl_ast_build *build); +__isl_give isl_union_map *isl_ast_build_get_schedule( + __isl_keep isl_ast_build *build); + +__isl_give isl_ast_build *isl_ast_build_restrict( + __isl_take isl_ast_build *build, __isl_take isl_set *set); + +__isl_give isl_ast_build *isl_ast_build_copy( + __isl_keep isl_ast_build *build); +__isl_null isl_ast_build *isl_ast_build_free( + __isl_take isl_ast_build *build); + +__isl_give isl_ast_build *isl_ast_build_set_options( + __isl_take isl_ast_build *build, + __isl_take isl_union_map *options); +__isl_give isl_ast_build *isl_ast_build_set_iterators( + __isl_take isl_ast_build *build, + __isl_take isl_id_list *iterators); +__isl_give isl_ast_build *isl_ast_build_set_at_each_domain( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user), void *user); +__isl_give isl_ast_build *isl_ast_build_set_before_each_for( + __isl_take isl_ast_build *build, + __isl_give isl_id *(*fn)(__isl_keep isl_ast_build *build, + void *user), void *user); +__isl_give isl_ast_build *isl_ast_build_set_after_each_for( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user), void *user); +__isl_give isl_ast_build *isl_ast_build_set_before_each_mark( + __isl_take isl_ast_build *build, + isl_stat (*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build, + void *user), void *user); +__isl_give isl_ast_build *isl_ast_build_set_after_each_mark( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user), void *user); +__isl_give isl_ast_build *isl_ast_build_set_create_leaf( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_build *build, + void *user), void *user); + +__isl_give isl_ast_expr *isl_ast_build_expr_from_set( + __isl_keep isl_ast_build *build, __isl_take isl_set *set); +__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa); +__isl_give isl_ast_expr *isl_ast_build_access_from_pw_multi_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_ast_expr *isl_ast_build_access_from_multi_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa); +__isl_give isl_ast_expr *isl_ast_build_call_from_pw_multi_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_ast_expr *isl_ast_build_call_from_multi_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa); + +__isl_give isl_ast_node *isl_ast_build_node_from_schedule( + __isl_keep isl_ast_build *build, __isl_take isl_schedule *schedule); +__isl_give isl_ast_node *isl_ast_build_node_from_schedule_map( + __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule); +__isl_give isl_ast_node *isl_ast_build_ast_from_schedule( + __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/ast_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/ast_type.h @@ -0,0 +1,80 @@ +#ifndef ISL_AST_TYPE_H +#define ISL_AST_TYPE_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_ast_expr; +typedef struct isl_ast_expr isl_ast_expr; + +struct isl_ast_node; +typedef struct isl_ast_node isl_ast_node; + +enum isl_ast_op_type { + isl_ast_op_error = -1, + isl_ast_op_and, + isl_ast_op_and_then, + isl_ast_op_or, + isl_ast_op_or_else, + isl_ast_op_max, + isl_ast_op_min, + isl_ast_op_minus, + isl_ast_op_add, + isl_ast_op_sub, + isl_ast_op_mul, + isl_ast_op_div, + isl_ast_op_fdiv_q, /* Round towards -infty */ + isl_ast_op_pdiv_q, /* Dividend is non-negative */ + isl_ast_op_pdiv_r, /* Dividend is non-negative */ + isl_ast_op_zdiv_r, /* Result only compared against zero */ + isl_ast_op_cond, + isl_ast_op_select, + isl_ast_op_eq, + isl_ast_op_le, + isl_ast_op_lt, + isl_ast_op_ge, + isl_ast_op_gt, + isl_ast_op_call, + isl_ast_op_access, + isl_ast_op_member, + isl_ast_op_address_of +}; + +enum isl_ast_expr_type { + isl_ast_expr_error = -1, + isl_ast_expr_op, + isl_ast_expr_id, + isl_ast_expr_int +}; + +enum isl_ast_node_type { + isl_ast_node_error = -1, + isl_ast_node_for = 1, + isl_ast_node_if, + isl_ast_node_block, + isl_ast_node_mark, + isl_ast_node_user +}; + +enum isl_ast_loop_type { + isl_ast_loop_error = -1, + isl_ast_loop_default = 0, + isl_ast_loop_atomic, + isl_ast_loop_unroll, + isl_ast_loop_separate +}; + +struct isl_ast_print_options; +typedef struct isl_ast_print_options isl_ast_print_options; + +ISL_DECLARE_LIST(ast_expr) +ISL_DECLARE_LIST(ast_node) + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/band.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/band.h @@ -0,0 +1,56 @@ +#ifndef ISL_BAND_H +#define ISL_BAND_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_band; +typedef struct isl_band isl_band; + +ISL_DECLARE_LIST(band) + +__isl_give isl_band *isl_band_copy(__isl_keep isl_band *band); +__isl_null isl_band *isl_band_free(__isl_take isl_band *band); + +isl_ctx *isl_band_get_ctx(__isl_keep isl_band *band); + +int isl_band_has_children(__isl_keep isl_band *band); +__isl_give isl_band_list *isl_band_get_children( + __isl_keep isl_band *band); + +__isl_give isl_union_map *isl_band_get_prefix_schedule( + __isl_keep isl_band *band); +__isl_give isl_union_map *isl_band_get_partial_schedule( + __isl_keep isl_band *band); +__isl_give isl_union_map *isl_band_get_suffix_schedule( + __isl_keep isl_band *band); + +isl_stat isl_options_set_tile_scale_tile_loops(isl_ctx *ctx, int val); +int isl_options_get_tile_scale_tile_loops(isl_ctx *ctx); +isl_stat isl_options_set_tile_shift_point_loops(isl_ctx *ctx, int val); +int isl_options_get_tile_shift_point_loops(isl_ctx *ctx); + +int isl_band_tile(__isl_keep isl_band *band, __isl_take isl_vec *sizes); +int isl_band_split(__isl_keep isl_band *band, int pos); + +int isl_band_n_member(__isl_keep isl_band *band); +int isl_band_member_is_coincident(__isl_keep isl_band *band, int pos); + +int isl_band_list_foreach_band(__isl_keep isl_band_list *list, + int (*fn)(__isl_keep isl_band *band, void *user), void *user); + +__isl_give isl_printer *isl_printer_print_band(__isl_take isl_printer *p, + __isl_keep isl_band *band); +void isl_band_dump(__isl_keep isl_band *band); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/constraint.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/constraint.h @@ -0,0 +1,150 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_CONSTRAINT_H +#define ISL_CONSTRAINT_H + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_constraint; +typedef struct isl_constraint isl_constraint; + +ISL_DECLARE_LIST(constraint) + +isl_ctx *isl_constraint_get_ctx(__isl_keep isl_constraint *c); + +__isl_give isl_constraint *isl_constraint_alloc_equality( + __isl_take isl_local_space *ls); +__isl_give isl_constraint *isl_constraint_alloc_inequality( + __isl_take isl_local_space *ls); +__isl_give isl_constraint *isl_equality_alloc(__isl_take isl_local_space *ls); +__isl_give isl_constraint *isl_inequality_alloc(__isl_take isl_local_space *ls); + +struct isl_constraint *isl_constraint_cow(struct isl_constraint *c); +struct isl_constraint *isl_constraint_copy(struct isl_constraint *c); +__isl_null isl_constraint *isl_constraint_free(__isl_take isl_constraint *c); + +int isl_basic_map_n_constraint(__isl_keep isl_basic_map *bmap); +int isl_basic_set_n_constraint(__isl_keep isl_basic_set *bset); +isl_stat isl_basic_map_foreach_constraint(__isl_keep isl_basic_map *bmap, + isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user); +isl_stat isl_basic_set_foreach_constraint(__isl_keep isl_basic_set *bset, + isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user); +__isl_give isl_constraint_list *isl_basic_map_get_constraint_list( + __isl_keep isl_basic_map *bmap); +__isl_give isl_constraint_list *isl_basic_set_get_constraint_list( + __isl_keep isl_basic_set *bset); +int isl_constraint_is_equal(struct isl_constraint *constraint1, + struct isl_constraint *constraint2); + +isl_stat isl_basic_set_foreach_bound_pair(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, + isl_stat (*fn)(__isl_take isl_constraint *lower, + __isl_take isl_constraint *upper, + __isl_take isl_basic_set *bset, void *user), void *user); + +__isl_give isl_basic_map *isl_basic_map_add_constraint( + __isl_take isl_basic_map *bmap, __isl_take isl_constraint *constraint); +__isl_give isl_basic_set *isl_basic_set_add_constraint( + __isl_take isl_basic_set *bset, __isl_take isl_constraint *constraint); +__isl_give isl_map *isl_map_add_constraint(__isl_take isl_map *map, + __isl_take isl_constraint *constraint); +__isl_give isl_set *isl_set_add_constraint(__isl_take isl_set *set, + __isl_take isl_constraint *constraint); + +int isl_basic_map_has_defining_equality( + __isl_keep isl_basic_map *bmap, enum isl_dim_type type, int pos, + __isl_give isl_constraint **c); +int isl_basic_set_has_defining_equality( + struct isl_basic_set *bset, enum isl_dim_type type, int pos, + struct isl_constraint **constraint); +int isl_basic_set_has_defining_inequalities( + struct isl_basic_set *bset, enum isl_dim_type type, int pos, + struct isl_constraint **lower, + struct isl_constraint **upper); + +__isl_give isl_space *isl_constraint_get_space( + __isl_keep isl_constraint *constraint); +__isl_give isl_local_space *isl_constraint_get_local_space( + __isl_keep isl_constraint *constraint); +int isl_constraint_dim(struct isl_constraint *constraint, + enum isl_dim_type type); + +isl_bool isl_constraint_involves_dims(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned first, unsigned n); + +const char *isl_constraint_get_dim_name(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos); +__isl_give isl_val *isl_constraint_get_constant_val( + __isl_keep isl_constraint *constraint); +__isl_give isl_val *isl_constraint_get_coefficient_val( + __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos); +__isl_give isl_constraint *isl_constraint_set_constant_si( + __isl_take isl_constraint *constraint, int v); +__isl_give isl_constraint *isl_constraint_set_constant_val( + __isl_take isl_constraint *constraint, __isl_take isl_val *v); +__isl_give isl_constraint *isl_constraint_set_coefficient_si( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, int v); +__isl_give isl_constraint *isl_constraint_set_coefficient_val( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, __isl_take isl_val *v); + +__isl_give isl_aff *isl_constraint_get_div(__isl_keep isl_constraint *constraint, + int pos); + +struct isl_constraint *isl_constraint_negate(struct isl_constraint *constraint); + +isl_bool isl_constraint_is_equality(__isl_keep isl_constraint *constraint); +int isl_constraint_is_div_constraint(__isl_keep isl_constraint *constraint); + +isl_bool isl_constraint_is_lower_bound(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos); +isl_bool isl_constraint_is_upper_bound(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos); + +__isl_give isl_basic_map *isl_basic_map_from_constraint( + __isl_take isl_constraint *constraint); +struct isl_basic_set *isl_basic_set_from_constraint( + struct isl_constraint *constraint); + +__isl_give isl_aff *isl_constraint_get_bound( + __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos); +__isl_give isl_aff *isl_constraint_get_aff( + __isl_keep isl_constraint *constraint); +__isl_give isl_constraint *isl_equality_from_aff(__isl_take isl_aff *aff); +__isl_give isl_constraint *isl_inequality_from_aff(__isl_take isl_aff *aff); + +__isl_give isl_basic_set *isl_basic_set_drop_constraint( + __isl_take isl_basic_set *bset, __isl_take isl_constraint *constraint); + +int isl_constraint_plain_cmp(__isl_keep isl_constraint *c1, + __isl_keep isl_constraint *c2); +int isl_constraint_cmp_last_non_zero(__isl_keep isl_constraint *c1, + __isl_keep isl_constraint *c2); + +__isl_give isl_printer *isl_printer_print_constraint(__isl_take isl_printer *p, + __isl_keep isl_constraint *c); +void isl_constraint_dump(__isl_keep isl_constraint *c); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/ctx.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/ctx.h @@ -0,0 +1,258 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_CTX_H +#define ISL_CTX_H + +#include +#include + +#include + +#ifndef __isl_give +#define __isl_give +#endif +#ifndef __isl_take +#define __isl_take +#endif +#ifndef __isl_keep +#define __isl_keep +#endif +#ifndef __isl_null +#define __isl_null +#endif +#ifndef __isl_export +#define __isl_export +#endif +#ifndef __isl_overload +#define __isl_overload +#endif +#ifndef __isl_constructor +#define __isl_constructor +#endif +#ifndef __isl_subclass +#define __isl_subclass(super) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Nearly all isa functions require a struct isl_ctx allocated using + * isl_ctx_alloc. This ctx contains (or will contain) options that + * control the behavior of the library and some caches. + * + * An object allocated within a given ctx should never be used inside + * another ctx. Functions for moving objects from one ctx to another + * will be added as the need arises. + * + * A given context should only be used inside a single thread. + * A global context for synchronization between different threads + * as well as functions for moving a context to a different thread + * will be added as the need arises. + * + * If anything goes wrong (out of memory, failed assertion), then + * the library will currently simply abort. This will be made + * configurable in the future. + * Users of the library should expect functions that return + * a pointer to a structure, to return NULL, indicating failure. + * Any function accepting a pointer to a structure will treat + * a NULL argument as a failure, resulting in the function freeing + * the remaining structures (if any) and returning NULL itself + * (in case of pointer return type). + * The only exception is the isl_ctx argument, which should never be NULL. + */ +struct isl_stats { + long gbr_solved_lps; +}; +enum isl_error { + isl_error_none = 0, + isl_error_abort, + isl_error_alloc, + isl_error_unknown, + isl_error_internal, + isl_error_invalid, + isl_error_quota, + isl_error_unsupported +}; +typedef enum { + isl_stat_error = -1, + isl_stat_ok = 0 +} isl_stat; +typedef enum { + isl_bool_error = -1, + isl_bool_false = 0, + isl_bool_true = 1 +} isl_bool; +isl_bool isl_bool_not(isl_bool b); +struct isl_ctx; +typedef struct isl_ctx isl_ctx; + +/* Some helper macros */ + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +#define ISL_DEPRECATED __attribute__((__deprecated__)) +#else +#define ISL_DEPRECATED +#endif + +#define ISL_FL_INIT(l, f) (l) = (f) /* Specific flags location. */ +#define ISL_FL_SET(l, f) ((l) |= (f)) +#define ISL_FL_CLR(l, f) ((l) &= ~(f)) +#define ISL_FL_ISSET(l, f) (!!((l) & (f))) + +#define ISL_F_INIT(p, f) ISL_FL_INIT((p)->flags, f) /* Structure element flags. */ +#define ISL_F_SET(p, f) ISL_FL_SET((p)->flags, f) +#define ISL_F_CLR(p, f) ISL_FL_CLR((p)->flags, f) +#define ISL_F_ISSET(p, f) ISL_FL_ISSET((p)->flags, f) + +void *isl_malloc_or_die(isl_ctx *ctx, size_t size); +void *isl_calloc_or_die(isl_ctx *ctx, size_t nmemb, size_t size); +void *isl_realloc_or_die(isl_ctx *ctx, void *ptr, size_t size); + +#define isl_alloc(ctx,type,size) ((type *)isl_malloc_or_die(ctx, size)) +#define isl_calloc(ctx,type,size) ((type *)isl_calloc_or_die(ctx,\ + 1, size)) +#define isl_realloc(ctx,ptr,type,size) ((type *)isl_realloc_or_die(ctx,\ + ptr, size)) +#define isl_alloc_type(ctx,type) isl_alloc(ctx,type,sizeof(type)) +#define isl_calloc_type(ctx,type) isl_calloc(ctx,type,sizeof(type)) +#define isl_realloc_type(ctx,ptr,type) isl_realloc(ctx,ptr,type,sizeof(type)) +#define isl_alloc_array(ctx,type,n) isl_alloc(ctx,type,(n)*sizeof(type)) +#define isl_calloc_array(ctx,type,n) ((type *)isl_calloc_or_die(ctx,\ + n, sizeof(type))) +#define isl_realloc_array(ctx,ptr,type,n) \ + isl_realloc(ctx,ptr,type,(n)*sizeof(type)) + +#define isl_die(ctx,errno,msg,code) \ + do { \ + isl_handle_error(ctx, errno, msg, __FILE__, __LINE__); \ + code; \ + } while (0) + +void isl_handle_error(isl_ctx *ctx, enum isl_error error, const char *msg, + const char *file, int line); + +#define isl_assert4(ctx,test,code,errno) \ + do { \ + if (test) \ + break; \ + isl_die(ctx, errno, "Assertion \"" #test "\" failed", code); \ + } while (0) +#define isl_assert(ctx,test,code) \ + isl_assert4(ctx,test,code,isl_error_unknown) + +#define isl_min(a,b) ((a < b) ? (a) : (b)) + +/* struct isl_ctx functions */ + +struct isl_options *isl_ctx_options(isl_ctx *ctx); + +isl_ctx *isl_ctx_alloc_with_options(struct isl_args *args, + __isl_take void *opt); +isl_ctx *isl_ctx_alloc(void); +void *isl_ctx_peek_options(isl_ctx *ctx, struct isl_args *args); +int isl_ctx_parse_options(isl_ctx *ctx, int argc, char **argv, unsigned flags); +void isl_ctx_ref(struct isl_ctx *ctx); +void isl_ctx_deref(struct isl_ctx *ctx); +void isl_ctx_free(isl_ctx *ctx); + +void isl_ctx_abort(isl_ctx *ctx); +void isl_ctx_resume(isl_ctx *ctx); +int isl_ctx_aborted(isl_ctx *ctx); + +void isl_ctx_set_max_operations(isl_ctx *ctx, unsigned long max_operations); +unsigned long isl_ctx_get_max_operations(isl_ctx *ctx); +void isl_ctx_reset_operations(isl_ctx *ctx); + +#define ISL_ARG_CTX_DECL(prefix,st,args) \ +st *isl_ctx_peek_ ## prefix(isl_ctx *ctx); + +#define ISL_ARG_CTX_DEF(prefix,st,args) \ +st *isl_ctx_peek_ ## prefix(isl_ctx *ctx) \ +{ \ + return (st *)isl_ctx_peek_options(ctx, &(args)); \ +} + +#define ISL_CTX_GET_INT_DEF(prefix,st,args,field) \ +int prefix ## _get_ ## field(isl_ctx *ctx) \ +{ \ + st *options; \ + options = isl_ctx_peek_ ## prefix(ctx); \ + if (!options) \ + isl_die(ctx, isl_error_invalid, \ + "isl_ctx does not reference " #prefix, \ + return -1); \ + return options->field; \ +} + +#define ISL_CTX_SET_INT_DEF(prefix,st,args,field) \ +isl_stat prefix ## _set_ ## field(isl_ctx *ctx, int val) \ +{ \ + st *options; \ + options = isl_ctx_peek_ ## prefix(ctx); \ + if (!options) \ + isl_die(ctx, isl_error_invalid, \ + "isl_ctx does not reference " #prefix, \ + return isl_stat_error); \ + options->field = val; \ + return isl_stat_ok; \ +} + +#define ISL_CTX_GET_STR_DEF(prefix,st,args,field) \ +const char *prefix ## _get_ ## field(isl_ctx *ctx) \ +{ \ + st *options; \ + options = isl_ctx_peek_ ## prefix(ctx); \ + if (!options) \ + isl_die(ctx, isl_error_invalid, \ + "isl_ctx does not reference " #prefix, \ + return NULL); \ + return options->field; \ +} + +#define ISL_CTX_SET_STR_DEF(prefix,st,args,field) \ +isl_stat prefix ## _set_ ## field(isl_ctx *ctx, const char *val) \ +{ \ + st *options; \ + options = isl_ctx_peek_ ## prefix(ctx); \ + if (!options) \ + isl_die(ctx, isl_error_invalid, \ + "isl_ctx does not reference " #prefix, \ + return isl_stat_error); \ + if (!val) \ + return isl_stat_error; \ + free(options->field); \ + options->field = strdup(val); \ + if (!options->field) \ + return isl_stat_error; \ + return isl_stat_ok; \ +} + +#define ISL_CTX_GET_BOOL_DEF(prefix,st,args,field) \ + ISL_CTX_GET_INT_DEF(prefix,st,args,field) + +#define ISL_CTX_SET_BOOL_DEF(prefix,st,args,field) \ + ISL_CTX_SET_INT_DEF(prefix,st,args,field) + +#define ISL_CTX_GET_CHOICE_DEF(prefix,st,args,field) \ + ISL_CTX_GET_INT_DEF(prefix,st,args,field) + +#define ISL_CTX_SET_CHOICE_DEF(prefix,st,args,field) \ + ISL_CTX_SET_INT_DEF(prefix,st,args,field) + +enum isl_error isl_ctx_last_error(isl_ctx *ctx); +void isl_ctx_reset_error(isl_ctx *ctx); +void isl_ctx_set_error(isl_ctx *ctx, enum isl_error error); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/aff_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/aff_int.h @@ -0,0 +1,45 @@ +#ifndef ISL_DEPRECATED_AFF_INT_H +#define ISL_DEPRECATED_AFF_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int isl_aff_get_constant(__isl_keep isl_aff *aff, isl_int *v); +int isl_aff_get_coefficient(__isl_keep isl_aff *aff, + enum isl_dim_type type, int pos, isl_int *v); +int isl_aff_get_denominator(__isl_keep isl_aff *aff, isl_int *v); +__isl_give isl_aff *isl_aff_set_constant(__isl_take isl_aff *aff, isl_int v); +__isl_give isl_aff *isl_aff_set_coefficient(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, isl_int v); +__isl_give isl_aff *isl_aff_set_denominator(__isl_take isl_aff *aff, isl_int v); +__isl_give isl_aff *isl_aff_add_constant(__isl_take isl_aff *aff, isl_int v); +__isl_give isl_aff *isl_aff_add_constant_num(__isl_take isl_aff *aff, + isl_int v); +__isl_give isl_aff *isl_aff_add_coefficient(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, isl_int v); + +__isl_give isl_aff *isl_aff_mod(__isl_take isl_aff *aff, isl_int mod); + +__isl_give isl_aff *isl_aff_scale(__isl_take isl_aff *aff, isl_int f); +__isl_give isl_aff *isl_aff_scale_down(__isl_take isl_aff *aff, isl_int f); + +__isl_give isl_pw_aff *isl_pw_aff_mod(__isl_take isl_pw_aff *pwaff, + isl_int mod); + +__isl_give isl_pw_aff *isl_pw_aff_scale(__isl_take isl_pw_aff *pwaff, + isl_int f); +__isl_give isl_pw_aff *isl_pw_aff_scale_down(__isl_take isl_pw_aff *pwaff, + isl_int f); + +__isl_give isl_multi_aff *isl_multi_aff_scale(__isl_take isl_multi_aff *maff, + isl_int f); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/ast_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/ast_int.h @@ -0,0 +1,17 @@ +#ifndef ISL_DEPRECATED_AST_INT_H +#define ISL_DEPRECATED_AST_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int isl_ast_expr_get_int(__isl_keep isl_ast_expr *expr, isl_int *v); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/constraint_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/constraint_int.h @@ -0,0 +1,25 @@ +#ifndef ISL_DEPRECATED_CONSTRAINT_INT_H +#define ISL_DEPRECATED_CONSTRAINT_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +void isl_constraint_get_constant(__isl_keep isl_constraint *constraint, + isl_int *v); +void isl_constraint_get_coefficient(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, int pos, isl_int *v); +__isl_give isl_constraint *isl_constraint_set_constant( + __isl_take isl_constraint *constraint, isl_int v); +__isl_give isl_constraint *isl_constraint_set_coefficient( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, isl_int v); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/ilp_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/ilp_int.h @@ -0,0 +1,23 @@ +#ifndef ISL_DEPRECATED_ILP_INT_H +#define ISL_DEPRECATED_ILP_INT_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +enum isl_lp_result isl_basic_set_max(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj, isl_int *opt); +enum isl_lp_result isl_set_min(__isl_keep isl_set *set, + __isl_keep isl_aff *obj, isl_int *opt); +enum isl_lp_result isl_set_max(__isl_keep isl_set *set, + __isl_keep isl_aff *obj, isl_int *opt); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/int.h @@ -0,0 +1,136 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_DEPRECATED_INT_H +#define ISL_DEPRECATED_INT_H + +#include +#include +#include +#if defined(__cplusplus) +#include +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef mp_get_memory_functions +void mp_get_memory_functions( + void *(**alloc_func_ptr) (size_t), + void *(**realloc_func_ptr) (void *, size_t, size_t), + void (**free_func_ptr) (void *, size_t)); +#endif + +/* isl_int is the basic integer type. It currently always corresponds + * to a gmp mpz_t, but in the future, different types such as long long + * or cln::cl_I will be supported. + */ +typedef mpz_t isl_int; + +#define isl_int_init(i) mpz_init(i) +#define isl_int_clear(i) mpz_clear(i) + +#define isl_int_set(r,i) mpz_set(r,i) +#define isl_int_set_gmp(r,i) mpz_set(r,i) +#define isl_int_set_si(r,i) mpz_set_si(r,i) +#define isl_int_set_ui(r,i) mpz_set_ui(r,i) +#define isl_int_get_gmp(i,g) mpz_set(g,i) +#define isl_int_get_si(r) mpz_get_si(r) +#define isl_int_get_ui(r) mpz_get_ui(r) +#define isl_int_get_d(r) mpz_get_d(r) +#define isl_int_get_str(r) mpz_get_str(0, 10, r) +typedef void (*isl_int_print_gmp_free_t)(void *, size_t); +#define isl_int_free_str(s) \ + do { \ + isl_int_print_gmp_free_t gmp_free; \ + mp_get_memory_functions(NULL, NULL, &gmp_free); \ + (*gmp_free)(s, strlen(s) + 1); \ + } while (0) +#define isl_int_abs(r,i) mpz_abs(r,i) +#define isl_int_neg(r,i) mpz_neg(r,i) +#define isl_int_swap(i,j) mpz_swap(i,j) +#define isl_int_swap_or_set(i,j) mpz_swap(i,j) +#define isl_int_add_ui(r,i,j) mpz_add_ui(r,i,j) +#define isl_int_sub_ui(r,i,j) mpz_sub_ui(r,i,j) + +#define isl_int_add(r,i,j) mpz_add(r,i,j) +#define isl_int_sub(r,i,j) mpz_sub(r,i,j) +#define isl_int_mul(r,i,j) mpz_mul(r,i,j) +#define isl_int_mul_2exp(r,i,j) mpz_mul_2exp(r,i,j) +#define isl_int_mul_ui(r,i,j) mpz_mul_ui(r,i,j) +#define isl_int_pow_ui(r,i,j) mpz_pow_ui(r,i,j) +#define isl_int_addmul(r,i,j) mpz_addmul(r,i,j) +#define isl_int_submul(r,i,j) mpz_submul(r,i,j) + +#define isl_int_gcd(r,i,j) mpz_gcd(r,i,j) +#define isl_int_lcm(r,i,j) mpz_lcm(r,i,j) +#define isl_int_divexact(r,i,j) mpz_divexact(r,i,j) +#define isl_int_divexact_ui(r,i,j) mpz_divexact_ui(r,i,j) +#define isl_int_tdiv_q(r,i,j) mpz_tdiv_q(r,i,j) +#define isl_int_cdiv_q(r,i,j) mpz_cdiv_q(r,i,j) +#define isl_int_fdiv_q(r,i,j) mpz_fdiv_q(r,i,j) +#define isl_int_fdiv_r(r,i,j) mpz_fdiv_r(r,i,j) +#define isl_int_fdiv_q_ui(r,i,j) mpz_fdiv_q_ui(r,i,j) + +#define isl_int_read(r,s) mpz_set_str(r,s,10) +#define isl_int_print(out,i,width) \ + do { \ + char *s; \ + s = mpz_get_str(0, 10, i); \ + fprintf(out, "%*s", width, s); \ + isl_int_free_str(s); \ + } while (0) + +#define isl_int_sgn(i) mpz_sgn(i) +#define isl_int_cmp(i,j) mpz_cmp(i,j) +#define isl_int_cmp_si(i,si) mpz_cmp_si(i,si) +#define isl_int_eq(i,j) (mpz_cmp(i,j) == 0) +#define isl_int_ne(i,j) (mpz_cmp(i,j) != 0) +#define isl_int_lt(i,j) (mpz_cmp(i,j) < 0) +#define isl_int_le(i,j) (mpz_cmp(i,j) <= 0) +#define isl_int_gt(i,j) (mpz_cmp(i,j) > 0) +#define isl_int_ge(i,j) (mpz_cmp(i,j) >= 0) +#define isl_int_abs_eq(i,j) (mpz_cmpabs(i,j) == 0) +#define isl_int_abs_ne(i,j) (mpz_cmpabs(i,j) != 0) +#define isl_int_abs_lt(i,j) (mpz_cmpabs(i,j) < 0) +#define isl_int_abs_gt(i,j) (mpz_cmpabs(i,j) > 0) +#define isl_int_abs_ge(i,j) (mpz_cmpabs(i,j) >= 0) + + +#define isl_int_is_zero(i) (isl_int_sgn(i) == 0) +#define isl_int_is_one(i) (isl_int_cmp_si(i,1) == 0) +#define isl_int_is_negone(i) (isl_int_cmp_si(i,-1) == 0) +#define isl_int_is_pos(i) (isl_int_sgn(i) > 0) +#define isl_int_is_neg(i) (isl_int_sgn(i) < 0) +#define isl_int_is_nonpos(i) (isl_int_sgn(i) <= 0) +#define isl_int_is_nonneg(i) (isl_int_sgn(i) >= 0) +#define isl_int_is_divisible_by(i,j) mpz_divisible_p(i,j) + +uint32_t isl_gmp_hash(mpz_t v, uint32_t hash); +#define isl_int_hash(v,h) isl_gmp_hash(v,h) + +#if defined(__cplusplus) +} +#endif + +#if defined(__cplusplus) +extern "C" { typedef void (*isl_gmp_free_t)(void *, size_t); } + +static inline std::ostream &operator<<(std::ostream &os, isl_int i) +{ + char *s; + s = mpz_get_str(0, 10, i); + os << s; + isl_int_free_str(s); + return os; +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/map_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/map_int.h @@ -0,0 +1,25 @@ +#ifndef ISL_DEPRECATED_MAP_INT_H +#define ISL_DEPRECATED_MAP_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int isl_basic_map_plain_is_fixed(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, isl_int *val); + +__isl_give isl_map *isl_map_fix(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int value); +int isl_map_plain_is_fixed(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int *val); + +__isl_give isl_map *isl_map_fixed_power(__isl_take isl_map *map, isl_int exp); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/mat_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/mat_int.h @@ -0,0 +1,19 @@ +#ifndef ISL_DEPRECATED_MAT_INT_H +#define ISL_DEPRECATED_MAT_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int isl_mat_get_element(__isl_keep isl_mat *mat, int row, int col, isl_int *v); +__isl_give isl_mat *isl_mat_set_element(__isl_take isl_mat *mat, + int row, int col, isl_int v); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/point_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/point_int.h @@ -0,0 +1,20 @@ +#ifndef ISL_DEPRECATED_POINT_INT_H +#define ISL_DEPRECATED_POINT_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int isl_point_get_coordinate(__isl_keep isl_point *pnt, + enum isl_dim_type type, int pos, isl_int *v); +__isl_give isl_point *isl_point_set_coordinate(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, isl_int v); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/polynomial_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/polynomial_int.h @@ -0,0 +1,32 @@ +#ifndef ISL_DEPRECATED_POLYNOMIAL_INT_H +#define ISL_DEPRECATED_POLYNOMIAL_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_qpolynomial *isl_qpolynomial_rat_cst_on_domain( + __isl_take isl_space *space, const isl_int n, const isl_int d); +int isl_qpolynomial_is_cst(__isl_keep isl_qpolynomial *qp, + isl_int *n, isl_int *d); +__isl_give isl_qpolynomial *isl_qpolynomial_scale( + __isl_take isl_qpolynomial *qp, isl_int v); + +void isl_term_get_num(__isl_keep isl_term *term, isl_int *n); +void isl_term_get_den(__isl_keep isl_term *term, isl_int *d); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale( + __isl_take isl_qpolynomial_fold *fold, isl_int v); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fix_dim( + __isl_take isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type, unsigned n, isl_int v); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/set_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/set_int.h @@ -0,0 +1,27 @@ +#ifndef ISL_DEPRECATED_SET_INT_H +#define ISL_DEPRECATED_SET_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_basic_set *isl_basic_set_fix(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, isl_int value); +__isl_give isl_set *isl_set_lower_bound(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value); +__isl_give isl_set *isl_set_upper_bound(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value); +__isl_give isl_set *isl_set_fix(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value); + +int isl_set_plain_is_fixed(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int *val); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/union_map_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/union_map_int.h @@ -0,0 +1,18 @@ +#ifndef ISL_DEPRECATED_UNION_MAP_INT_H +#define ISL_DEPRECATED_UNION_MAP_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_union_map *isl_union_map_fixed_power( + __isl_take isl_union_map *umap, isl_int exp); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/val_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/val_int.h @@ -0,0 +1,18 @@ +#ifndef ISL_DEPRECATED_VAL_INT_H +#define ISL_DEPRECATED_VAL_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_val *isl_val_int_from_isl_int(isl_ctx *ctx, isl_int n); +int isl_val_get_num_isl_int(__isl_keep isl_val *v, isl_int *n); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/deprecated/vec_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/deprecated/vec_int.h @@ -0,0 +1,22 @@ +#ifndef ISL_DEPRECATED_VEC_INT_H +#define ISL_DEPRECATED_VEC_INT_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int isl_vec_get_element(__isl_keep isl_vec *vec, int pos, isl_int *v); +__isl_give isl_vec *isl_vec_set_element(__isl_take isl_vec *vec, + int pos, isl_int v); + +__isl_give isl_vec *isl_vec_set(__isl_take isl_vec *vec, isl_int v); +__isl_give isl_vec *isl_vec_fdiv_r(__isl_take isl_vec *vec, isl_int m); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/flow.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/flow.h @@ -0,0 +1,146 @@ +#ifndef ISL_FLOW_H +#define ISL_FLOW_H + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Let n (>= 0) be the number of iterators shared by first and second. + * If first precedes second textually return 2 * n + 1, + * otherwise return 2 * n. + */ +typedef int (*isl_access_level_before)(void *first, void *second); + +struct isl_restriction; +typedef struct isl_restriction isl_restriction; + +__isl_null isl_restriction *isl_restriction_free( + __isl_take isl_restriction *restr); +__isl_give isl_restriction *isl_restriction_empty( + __isl_take isl_map *source_map); +__isl_give isl_restriction *isl_restriction_none( + __isl_take isl_map *source_map); +__isl_give isl_restriction *isl_restriction_input( + __isl_take isl_set *source_restr, __isl_take isl_set *sink_restr); +__isl_give isl_restriction *isl_restriction_output( + __isl_take isl_set *source_restr); + +isl_ctx *isl_restriction_get_ctx(__isl_keep isl_restriction *restr); + +typedef __isl_give isl_restriction *(*isl_access_restrict)( + __isl_keep isl_map *source_map, __isl_keep isl_set *sink, + void *source_user, void *user); + +struct isl_access_info; +typedef struct isl_access_info isl_access_info; +struct isl_flow; +typedef struct isl_flow isl_flow; + +__isl_give isl_access_info *isl_access_info_alloc(__isl_take isl_map *sink, + void *sink_user, isl_access_level_before fn, int max_source); +__isl_give isl_access_info *isl_access_info_set_restrict( + __isl_take isl_access_info *acc, isl_access_restrict fn, void *user); +__isl_give isl_access_info *isl_access_info_add_source( + __isl_take isl_access_info *acc, __isl_take isl_map *source, + int must, void *source_user); +__isl_null isl_access_info *isl_access_info_free( + __isl_take isl_access_info *acc); + +isl_ctx *isl_access_info_get_ctx(__isl_keep isl_access_info *acc); + +__isl_give isl_flow *isl_access_info_compute_flow(__isl_take isl_access_info *acc); +isl_stat isl_flow_foreach(__isl_keep isl_flow *deps, + isl_stat (*fn)(__isl_take isl_map *dep, int must, void *dep_user, + void *user), + void *user); +__isl_give isl_map *isl_flow_get_no_source(__isl_keep isl_flow *deps, int must); +void isl_flow_free(__isl_take isl_flow *deps); + +isl_ctx *isl_flow_get_ctx(__isl_keep isl_flow *deps); + +struct __isl_export isl_union_access_info; +typedef struct isl_union_access_info isl_union_access_info; +struct __isl_export isl_union_flow; +typedef struct isl_union_flow isl_union_flow; + +__isl_constructor +__isl_give isl_union_access_info *isl_union_access_info_from_sink( + __isl_take isl_union_map *sink); +__isl_export +__isl_give isl_union_access_info *isl_union_access_info_set_must_source( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *must_source); +__isl_export +__isl_give isl_union_access_info *isl_union_access_info_set_may_source( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *may_source); +__isl_export +__isl_give isl_union_access_info *isl_union_access_info_set_schedule( + __isl_take isl_union_access_info *access, + __isl_take isl_schedule *schedule); +__isl_export +__isl_give isl_union_access_info *isl_union_access_info_set_schedule_map( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *schedule_map); +__isl_give isl_union_access_info *isl_union_access_info_copy( + __isl_keep isl_union_access_info *access); +__isl_null isl_union_access_info *isl_union_access_info_free( + __isl_take isl_union_access_info *access); + +isl_ctx *isl_union_access_info_get_ctx( + __isl_keep isl_union_access_info *access); + +__isl_give isl_printer *isl_printer_print_union_access_info( + __isl_take isl_printer *p, __isl_keep isl_union_access_info *access); +__isl_give char *isl_union_access_info_to_str( + __isl_keep isl_union_access_info *access); + +__isl_export +__isl_give isl_union_flow *isl_union_access_info_compute_flow( + __isl_take isl_union_access_info *access); + +isl_ctx *isl_union_flow_get_ctx(__isl_keep isl_union_flow *flow); +__isl_export +__isl_give isl_union_map *isl_union_flow_get_must_dependence( + __isl_keep isl_union_flow *flow); +__isl_export +__isl_give isl_union_map *isl_union_flow_get_may_dependence( + __isl_keep isl_union_flow *flow); +__isl_export +__isl_give isl_union_map *isl_union_flow_get_full_must_dependence( + __isl_keep isl_union_flow *flow); +__isl_export +__isl_give isl_union_map *isl_union_flow_get_full_may_dependence( + __isl_keep isl_union_flow *flow); +__isl_export +__isl_give isl_union_map *isl_union_flow_get_must_no_source( + __isl_keep isl_union_flow *flow); +__isl_export +__isl_give isl_union_map *isl_union_flow_get_may_no_source( + __isl_keep isl_union_flow *flow); +__isl_null isl_union_flow *isl_union_flow_free(__isl_take isl_union_flow *flow); + +__isl_give isl_printer *isl_printer_print_union_flow( + __isl_take isl_printer *p, __isl_keep isl_union_flow *flow); +__isl_give char *isl_union_flow_to_str(__isl_keep isl_union_flow *flow); + +int isl_union_map_compute_flow(__isl_take isl_union_map *sink, + __isl_take isl_union_map *must_source, + __isl_take isl_union_map *may_source, + __isl_take isl_union_map *schedule, + __isl_give isl_union_map **must_dep, __isl_give isl_union_map **may_dep, + __isl_give isl_union_map **must_no_source, + __isl_give isl_union_map **may_no_source); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/hash.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/hash.h @@ -0,0 +1,77 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_HASH_H +#define ISL_HASH_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define isl_hash_init() (2166136261u) +#define isl_hash_byte(h,b) do { \ + h *= 16777619; \ + h ^= b; \ + } while(0) +#define isl_hash_hash(h,h2) \ + do { \ + isl_hash_byte(h, (h2) & 0xFF); \ + isl_hash_byte(h, ((h2) >> 8) & 0xFF); \ + isl_hash_byte(h, ((h2) >> 16) & 0xFF); \ + isl_hash_byte(h, ((h2) >> 24) & 0xFF); \ + } while(0) +#define isl_hash_bits(h,bits) \ + ((bits) == 32) ? (h) : \ + ((bits) >= 16) ? \ + ((h) >> (bits)) ^ ((h) & (((uint32_t)1 << (bits)) - 1)) : \ + (((h) >> (bits)) ^ (h)) & (((uint32_t)1 << (bits)) - 1) + +uint32_t isl_hash_string(uint32_t hash, const char *s); +uint32_t isl_hash_mem(uint32_t hash, const void *p, size_t len); + +#define isl_hash_builtin(h,l) isl_hash_mem(h, &l, sizeof(l)) + +struct isl_hash_table_entry +{ + uint32_t hash; + void *data; +}; + +struct isl_hash_table { + int bits; + int n; + struct isl_hash_table_entry *entries; +}; + +struct isl_hash_table *isl_hash_table_alloc(struct isl_ctx *ctx, int min_size); +void isl_hash_table_free(struct isl_ctx *ctx, struct isl_hash_table *table); + +int isl_hash_table_init(struct isl_ctx *ctx, struct isl_hash_table *table, + int min_size); +void isl_hash_table_clear(struct isl_hash_table *table); +struct isl_hash_table_entry *isl_hash_table_find(struct isl_ctx *ctx, + struct isl_hash_table *table, + uint32_t key_hash, + int (*eq)(const void *entry, const void *val), + const void *val, int reserve); +isl_stat isl_hash_table_foreach(isl_ctx *ctx, struct isl_hash_table *table, + isl_stat (*fn)(void **entry, void *user), void *user); +void isl_hash_table_remove(struct isl_ctx *ctx, + struct isl_hash_table *table, + struct isl_hash_table_entry *entry); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/hmap.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/hmap.h @@ -0,0 +1,55 @@ +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define ISL_xCAT(A,B) A ## B +#define ISL_CAT(A,B) ISL_xCAT(A,B) +#define ISL_xFN(TYPE,NAME) TYPE ## _ ## NAME +#define ISL_FN(TYPE,NAME) ISL_xFN(TYPE,NAME) + +struct ISL_HMAP; +typedef struct ISL_HMAP ISL_HMAP; + +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,alloc)(isl_ctx *ctx, int min_size); +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,copy)(__isl_keep ISL_HMAP *hmap); +__isl_null ISL_HMAP *ISL_FN(ISL_HMAP,free)(__isl_take ISL_HMAP *hmap); + +isl_ctx *ISL_FN(ISL_HMAP,get_ctx)(__isl_keep ISL_HMAP *hmap); + +__isl_give ISL_MAYBE(ISL_VAL) ISL_FN(ISL_HMAP,try_get)( + __isl_keep ISL_HMAP *hmap, __isl_keep ISL_KEY *key); +isl_bool ISL_FN(ISL_HMAP,has)(__isl_keep ISL_HMAP *hmap, + __isl_keep ISL_KEY *key); +__isl_give ISL_VAL *ISL_FN(ISL_HMAP,get)(__isl_keep ISL_HMAP *hmap, + __isl_take ISL_KEY *key); +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,set)(__isl_take ISL_HMAP *hmap, + __isl_take ISL_KEY *key, __isl_take ISL_VAL *val); +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,drop)(__isl_take ISL_HMAP *hmap, + __isl_take ISL_KEY *key); + +isl_stat ISL_FN(ISL_HMAP,foreach)(__isl_keep ISL_HMAP *hmap, + isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val, + void *user), + void *user); + +__isl_give isl_printer *ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)( + __isl_take isl_printer *p, __isl_keep ISL_HMAP *hmap); +void ISL_FN(ISL_HMAP,dump)(__isl_keep ISL_HMAP *hmap); + +#undef ISL_xCAT +#undef ISL_CAT +#undef ISL_KEY +#undef ISL_VAL +#undef ISL_xFN +#undef ISL_FN +#undef ISL_xHMAP +#undef ISL_yHMAP +#undef ISL_HMAP + +#if defined(__cplusplus) +} +#endif Index: lib/Analysis/isl/include/isl/hmap_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/hmap_templ.c @@ -0,0 +1,417 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include + +#define ISL_xCAT(A,B) A ## B +#define ISL_CAT(A,B) ISL_xCAT(A,B) +#define ISL_xFN(TYPE,NAME) TYPE ## _ ## NAME +#define ISL_FN(TYPE,NAME) ISL_xFN(TYPE,NAME) +#define ISL_xS(TYPE1,TYPE2,NAME) struct isl_ ## TYPE1 ## _ ## TYPE2 ## _ ## NAME +#define ISL_yS(TYPE1,TYPE2,NAME) ISL_xS(TYPE1,TYPE2,NAME) +#define ISL_S(NAME) ISL_yS(ISL_KEY,ISL_VAL,NAME) + +struct ISL_HMAP { + int ref; + isl_ctx *ctx; + struct isl_hash_table table; +}; + +ISL_S(pair) { + ISL_KEY *key; + ISL_VAL *val; +}; + +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,alloc)(isl_ctx *ctx, int min_size) +{ + ISL_HMAP *hmap; + + hmap = isl_calloc_type(ctx, ISL_HMAP); + if (!hmap) + return NULL; + + hmap->ctx = ctx; + isl_ctx_ref(ctx); + hmap->ref = 1; + + if (isl_hash_table_init(ctx, &hmap->table, min_size) < 0) + return ISL_FN(ISL_HMAP,free)(hmap); + + return hmap; +} + +static isl_stat free_pair(void **entry, void *user) +{ + ISL_S(pair) *pair = *entry; + ISL_FN(ISL_KEY,free)(pair->key); + ISL_FN(ISL_VAL,free)(pair->val); + free(pair); + *entry = NULL; + return isl_stat_ok; +} + +__isl_null ISL_HMAP *ISL_FN(ISL_HMAP,free)(__isl_take ISL_HMAP *hmap) +{ + if (!hmap) + return NULL; + if (--hmap->ref > 0) + return NULL; + isl_hash_table_foreach(hmap->ctx, &hmap->table, &free_pair, NULL); + isl_hash_table_clear(&hmap->table); + isl_ctx_deref(hmap->ctx); + free(hmap); + return NULL; +} + +isl_ctx *ISL_FN(ISL_HMAP,get_ctx)(__isl_keep ISL_HMAP *hmap) +{ + return hmap ? hmap->ctx : NULL; +} + +/* Add a mapping from "key" to "val" to the associative array + * pointed to by user. + */ +static isl_stat add_key_val(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val, + void *user) +{ + ISL_HMAP **hmap = (ISL_HMAP **) user; + + *hmap = ISL_FN(ISL_HMAP,set)(*hmap, key, val); + + if (!*hmap) + return isl_stat_error; + + return isl_stat_ok; +} + +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,dup)(__isl_keep ISL_HMAP *hmap) +{ + ISL_HMAP *dup; + + if (!hmap) + return NULL; + + dup = ISL_FN(ISL_HMAP,alloc)(hmap->ctx, hmap->table.n); + if (ISL_FN(ISL_HMAP,foreach)(hmap, &add_key_val, &dup) < 0) + return ISL_FN(ISL_HMAP,free)(dup); + + return dup; +} + +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,cow)(__isl_take ISL_HMAP *hmap) +{ + if (!hmap) + return NULL; + + if (hmap->ref == 1) + return hmap; + hmap->ref--; + return ISL_FN(ISL_HMAP,dup)(hmap); +} + +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,copy)(__isl_keep ISL_HMAP *hmap) +{ + if (!hmap) + return NULL; + + hmap->ref++; + return hmap; +} + +static int has_key(const void *entry, const void *c_key) +{ + const ISL_S(pair) *pair = entry; + ISL_KEY *key = (ISL_KEY *) c_key; + + return ISL_KEY_IS_EQUAL(pair->key, key); +} + +/* If "hmap" contains a value associated to "key", then return + * (isl_bool_true, copy of value). + * Otherwise, return + * (isl_bool_false, NULL). + * If an error occurs, then return + * (isl_bool_error, NULL). + */ +__isl_give ISL_MAYBE(ISL_VAL) ISL_FN(ISL_HMAP,try_get)( + __isl_keep ISL_HMAP *hmap, __isl_keep ISL_KEY *key) +{ + struct isl_hash_table_entry *entry; + ISL_S(pair) *pair; + uint32_t hash; + ISL_MAYBE(ISL_VAL) res = { isl_bool_false, NULL }; + + if (!hmap || !key) + goto error; + + hash = ISL_FN(ISL_KEY,get_hash)(key); + entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash, + &has_key, key, 0); + + if (!entry) + return res; + + pair = entry->data; + + res.valid = isl_bool_true; + res.value = ISL_FN(ISL_VAL,copy)(pair->val); + if (!res.value) + res.valid = isl_bool_error; + return res; +error: + res.valid = isl_bool_error; + res.value = NULL; + return res; +} + +/* If "hmap" contains a value associated to "key", then return + * isl_bool_true. Otherwise, return isl_bool_false. + * Return isl_bool_error on error. + */ +isl_bool ISL_FN(ISL_HMAP,has)(__isl_keep ISL_HMAP *hmap, + __isl_keep ISL_KEY *key) +{ + ISL_MAYBE(ISL_VAL) res; + + res = ISL_FN(ISL_HMAP,try_get)(hmap, key); + ISL_FN(ISL_VAL,free)(res.value); + + return res.valid; +} + +/* If "hmap" contains a value associated to "key", then return + * a copy of that value. Otherwise, return NULL. + * Return NULL on error. + */ +__isl_give ISL_VAL *ISL_FN(ISL_HMAP,get)(__isl_keep ISL_HMAP *hmap, + __isl_take ISL_KEY *key) +{ + ISL_VAL *res; + + res = ISL_FN(ISL_HMAP,try_get)(hmap, key).value; + ISL_FN(ISL_KEY,free)(key); + return res; +} + +/* Remove the mapping between "key" and its associated value (if any) + * from "hmap". + * + * If "key" is not mapped to anything, then we leave "hmap" untouched" + */ +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,drop)(__isl_take ISL_HMAP *hmap, + __isl_take ISL_KEY *key) +{ + struct isl_hash_table_entry *entry; + ISL_S(pair) *pair; + uint32_t hash; + + if (!hmap || !key) + goto error; + + hash = ISL_FN(ISL_KEY,get_hash)(key); + entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash, + &has_key, key, 0); + if (!entry) { + ISL_FN(ISL_KEY,free)(key); + return hmap; + } + + hmap = ISL_FN(ISL_HMAP,cow)(hmap); + if (!hmap) + goto error; + entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash, + &has_key, key, 0); + ISL_FN(ISL_KEY,free)(key); + + if (!entry) + isl_die(hmap->ctx, isl_error_internal, + "missing entry" , goto error); + + pair = entry->data; + isl_hash_table_remove(hmap->ctx, &hmap->table, entry); + ISL_FN(ISL_KEY,free)(pair->key); + ISL_FN(ISL_VAL,free)(pair->val); + free(pair); + + return hmap; +error: + ISL_FN(ISL_KEY,free)(key); + ISL_FN(ISL_HMAP,free)(hmap); + return NULL; +} + +/* Add a mapping from "key" to "val" to "hmap". + * If "key" was already mapped to something else, then that mapping + * is replaced. + * If key happened to be mapped to "val" already, then we leave + * "hmap" untouched. + */ +__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,set)(__isl_take ISL_HMAP *hmap, + __isl_take ISL_KEY *key, __isl_take ISL_VAL *val) +{ + struct isl_hash_table_entry *entry; + ISL_S(pair) *pair; + uint32_t hash; + + if (!hmap || !key || !val) + goto error; + + hash = ISL_FN(ISL_KEY,get_hash)(key); + entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash, + &has_key, key, 0); + if (entry) { + int equal; + pair = entry->data; + equal = ISL_VAL_IS_EQUAL(pair->val, val); + if (equal < 0) + goto error; + if (equal) { + ISL_FN(ISL_KEY,free)(key); + ISL_FN(ISL_VAL,free)(val); + return hmap; + } + } + + hmap = ISL_FN(ISL_HMAP,cow)(hmap); + if (!hmap) + goto error; + + entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash, + &has_key, key, 1); + + if (!entry) + goto error; + + if (entry->data) { + pair = entry->data; + ISL_FN(ISL_VAL,free)(pair->val); + pair->val = val; + ISL_FN(ISL_KEY,free)(key); + return hmap; + } + + pair = isl_alloc_type(hmap->ctx, ISL_S(pair)); + if (!pair) + goto error; + + entry->data = pair; + pair->key = key; + pair->val = val; + return hmap; +error: + ISL_FN(ISL_KEY,free)(key); + ISL_FN(ISL_VAL,free)(val); + return ISL_FN(ISL_HMAP,free)(hmap); +} + +/* Internal data structure for isl_map_to_basic_set_foreach. + * + * fn is the function that should be called on each entry. + * user is the user-specified final argument to fn. + */ +ISL_S(foreach_data) { + isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val, + void *user); + void *user; +}; + +/* Call data->fn on a copy of the key and value in *entry. + */ +static isl_stat call_on_copy(void **entry, void *user) +{ + ISL_S(pair) *pair = *entry; + ISL_S(foreach_data) *data = (ISL_S(foreach_data) *) user; + + return data->fn(ISL_FN(ISL_KEY,copy)(pair->key), + ISL_FN(ISL_VAL,copy)(pair->val), data->user); +} + +/* Call "fn" on each pair of key and value in "hmap". + */ +isl_stat ISL_FN(ISL_HMAP,foreach)(__isl_keep ISL_HMAP *hmap, + isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val, + void *user), + void *user) +{ + ISL_S(foreach_data) data = { fn, user }; + + if (!hmap) + return isl_stat_error; + + return isl_hash_table_foreach(hmap->ctx, &hmap->table, + &call_on_copy, &data); +} + +/* Internal data structure for print_pair. + * + * p is the printer on which the associative array is being printed. + * first is set if the current key-value pair is the first to be printed. + */ +ISL_S(print_data) { + isl_printer *p; + int first; +}; + +/* Print the given key-value pair to data->p. + */ +static isl_stat print_pair(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val, + void *user) +{ + ISL_S(print_data) *data = user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, ", "); + data->p = ISL_KEY_PRINT(data->p, key); + data->p = isl_printer_print_str(data->p, ": "); + data->p = ISL_VAL_PRINT(data->p, val); + data->first = 0; + + ISL_FN(ISL_KEY,free)(key); + ISL_FN(ISL_VAL,free)(val); + return isl_stat_ok; +} + +/* Print the associative array to "p". + */ +__isl_give isl_printer *ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)( + __isl_take isl_printer *p, __isl_keep ISL_HMAP *hmap) +{ + ISL_S(print_data) data; + + if (!p || !hmap) + return isl_printer_free(p); + + p = isl_printer_print_str(p, "{"); + data.p = p; + data.first = 1; + if (ISL_FN(ISL_HMAP,foreach)(hmap, &print_pair, &data) < 0) + data.p = isl_printer_free(data.p); + p = data.p; + p = isl_printer_print_str(p, "}"); + + return p; +} + +void ISL_FN(ISL_HMAP,dump)(__isl_keep ISL_HMAP *hmap) +{ + isl_printer *printer; + + if (!hmap) + return; + + printer = isl_printer_to_file(ISL_FN(ISL_HMAP,get_ctx)(hmap), stderr); + printer = ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)(printer, hmap); + printer = isl_printer_end_line(printer); + + isl_printer_free(printer); +} Index: lib/Analysis/isl/include/isl/id.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/id.h @@ -0,0 +1,40 @@ +#ifndef ISL_ID_H +#define ISL_ID_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_id; +typedef struct isl_id isl_id; + +ISL_DECLARE_LIST(id) + +isl_ctx *isl_id_get_ctx(__isl_keep isl_id *id); +uint32_t isl_id_get_hash(__isl_keep isl_id *id); + +__isl_give isl_id *isl_id_alloc(isl_ctx *ctx, + __isl_keep const char *name, void *user); +__isl_give isl_id *isl_id_copy(isl_id *id); +__isl_null isl_id *isl_id_free(__isl_take isl_id *id); + +void *isl_id_get_user(__isl_keep isl_id *id); +__isl_keep const char *isl_id_get_name(__isl_keep isl_id *id); + +__isl_give isl_id *isl_id_set_free_user(__isl_take isl_id *id, + void (*free_user)(void *user)); + +__isl_give isl_printer *isl_printer_print_id(__isl_take isl_printer *p, + __isl_keep isl_id *id); +void isl_id_dump(__isl_keep isl_id *id); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/id_to_ast_expr.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/id_to_ast_expr.h @@ -0,0 +1,18 @@ +#ifndef ISL_ID_TO_AST_EXPR_H +#define ISL_ID_TO_AST_EXPR_H + +#include +#include +#include + +#define ISL_KEY isl_id +#define ISL_VAL isl_ast_expr +#define ISL_HMAP_SUFFIX id_to_ast_expr +#define ISL_HMAP isl_id_to_ast_expr +#include +#undef ISL_KEY +#undef ISL_VAL +#undef ISL_HMAP_SUFFIX +#undef ISL_HMAP + +#endif Index: lib/Analysis/isl/include/isl/id_to_id.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/id_to_id.h @@ -0,0 +1,17 @@ +#ifndef ISL_ID_TO_ID_H +#define ISL_ID_TO_ID_H + +#include +#include + +#define ISL_KEY isl_id +#define ISL_VAL isl_id +#define ISL_HMAP_SUFFIX id_to_id +#define ISL_HMAP isl_id_to_id +#include +#undef ISL_KEY +#undef ISL_VAL +#undef ISL_HMAP_SUFFIX +#undef ISL_HMAP + +#endif Index: lib/Analysis/isl/include/isl/id_to_pw_aff.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/id_to_pw_aff.h @@ -0,0 +1,18 @@ +#ifndef ISL_ID_TO_PW_AFF_H +#define ISL_ID_TO_PW_AFF_H + +#include +#include +#include + +#define ISL_KEY isl_id +#define ISL_VAL isl_pw_aff +#define ISL_HMAP_SUFFIX id_to_pw_aff +#define ISL_HMAP isl_id_to_pw_aff +#include +#undef ISL_KEY +#undef ISL_VAL +#undef ISL_HMAP_SUFFIX +#undef ISL_HMAP + +#endif Index: lib/Analysis/isl/include/isl/ilp.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/ilp.h @@ -0,0 +1,38 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_ILP_H +#define ISL_ILP_H + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_val *isl_basic_set_max_val(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj); +__isl_export +__isl_give isl_val *isl_set_min_val(__isl_keep isl_set *set, + __isl_keep isl_aff *obj); +__isl_export +__isl_give isl_val *isl_set_max_val(__isl_keep isl_set *set, + __isl_keep isl_aff *obj); +__isl_give isl_multi_val *isl_union_set_min_multi_union_pw_aff( + __isl_keep isl_union_set *set, __isl_keep isl_multi_union_pw_aff *obj); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/list.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/list.h @@ -0,0 +1,76 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_LIST_H +#define ISL_LIST_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define ISL_DECLARE_LIST_TYPE(EL) \ +struct isl_##EL; \ +struct isl_##EL##_list; \ +typedef struct isl_##EL##_list isl_##EL##_list; +#define ISL_DECLARE_LIST_FN(EL) \ +isl_ctx *isl_##EL##_list_get_ctx(__isl_keep isl_##EL##_list *list); \ +__isl_give isl_##EL##_list *isl_##EL##_list_from_##EL( \ + __isl_take struct isl_##EL *el); \ +__isl_give isl_##EL##_list *isl_##EL##_list_alloc(isl_ctx *ctx, int n); \ +__isl_give isl_##EL##_list *isl_##EL##_list_copy( \ + __isl_keep isl_##EL##_list *list); \ +__isl_null isl_##EL##_list *isl_##EL##_list_free( \ + __isl_take isl_##EL##_list *list); \ +__isl_give isl_##EL##_list *isl_##EL##_list_add( \ + __isl_take isl_##EL##_list *list, \ + __isl_take struct isl_##EL *el); \ +__isl_give isl_##EL##_list *isl_##EL##_list_insert( \ + __isl_take isl_##EL##_list *list, unsigned pos, \ + __isl_take struct isl_##EL *el); \ +__isl_give isl_##EL##_list *isl_##EL##_list_drop( \ + __isl_take isl_##EL##_list *list, unsigned first, unsigned n); \ +__isl_give isl_##EL##_list *isl_##EL##_list_concat( \ + __isl_take isl_##EL##_list *list1, \ + __isl_take isl_##EL##_list *list2); \ +int isl_##EL##_list_n_##EL(__isl_keep isl_##EL##_list *list); \ +__isl_give struct isl_##EL *isl_##EL##_list_get_##EL( \ + __isl_keep isl_##EL##_list *list, int index); \ +__isl_give struct isl_##EL##_list *isl_##EL##_list_set_##EL( \ + __isl_take struct isl_##EL##_list *list, int index, \ + __isl_take struct isl_##EL *el); \ +isl_stat isl_##EL##_list_foreach(__isl_keep isl_##EL##_list *list, \ + isl_stat (*fn)(__isl_take struct isl_##EL *el, void *user), \ + void *user); \ +__isl_give isl_##EL##_list *isl_##EL##_list_sort( \ + __isl_take isl_##EL##_list *list, \ + int (*cmp)(__isl_keep struct isl_##EL *a, \ + __isl_keep struct isl_##EL *b, \ + void *user), void *user); \ +isl_stat isl_##EL##_list_foreach_scc(__isl_keep isl_##EL##_list *list, \ + isl_bool (*follows)(__isl_keep struct isl_##EL *a, \ + __isl_keep struct isl_##EL *b, void *user), \ + void *follows_user, \ + isl_stat (*fn)(__isl_take isl_##EL##_list *scc, void *user), \ + void *fn_user); \ +__isl_give isl_printer *isl_printer_print_##EL##_list( \ + __isl_take isl_printer *p, __isl_keep isl_##EL##_list *list); \ +void isl_##EL##_list_dump(__isl_keep isl_##EL##_list *list); + +#define ISL_DECLARE_LIST(EL) \ + ISL_DECLARE_LIST_TYPE(EL) \ + ISL_DECLARE_LIST_FN(EL) + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/local_space.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/local_space.h @@ -0,0 +1,95 @@ +#ifndef ISL_LOCAL_SPACE_H +#define ISL_LOCAL_SPACE_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_local_space; +typedef struct isl_local_space isl_local_space; + +isl_ctx *isl_local_space_get_ctx(__isl_keep isl_local_space *ls); + +__isl_give isl_local_space *isl_local_space_from_space(__isl_take isl_space *dim); + +__isl_give isl_local_space *isl_local_space_copy( + __isl_keep isl_local_space *ls); +__isl_null isl_local_space *isl_local_space_free( + __isl_take isl_local_space *ls); + +isl_bool isl_local_space_is_params(__isl_keep isl_local_space *ls); +isl_bool isl_local_space_is_set(__isl_keep isl_local_space *ls); + +__isl_give isl_local_space *isl_local_space_set_tuple_id( + __isl_take isl_local_space *ls, + enum isl_dim_type type, __isl_take isl_id *id); + +int isl_local_space_dim(__isl_keep isl_local_space *ls, + enum isl_dim_type type); +isl_bool isl_local_space_has_dim_name(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); +const char *isl_local_space_get_dim_name(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); +__isl_give isl_local_space *isl_local_space_set_dim_name( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, const char *s); +isl_bool isl_local_space_has_dim_id(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); +__isl_give isl_id *isl_local_space_get_dim_id(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos); +__isl_give isl_local_space *isl_local_space_set_dim_id( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); +__isl_give isl_space *isl_local_space_get_space(__isl_keep isl_local_space *ls); +__isl_give isl_aff *isl_local_space_get_div(__isl_keep isl_local_space *ls, + int pos); + +int isl_local_space_find_dim_by_name(__isl_keep isl_local_space *ls, + enum isl_dim_type type, const char *name); + +__isl_give isl_local_space *isl_local_space_domain( + __isl_take isl_local_space *ls); +__isl_give isl_local_space *isl_local_space_range( + __isl_take isl_local_space *ls); +__isl_give isl_local_space *isl_local_space_from_domain( + __isl_take isl_local_space *ls); +__isl_give isl_local_space *isl_local_space_add_dims( + __isl_take isl_local_space *ls, enum isl_dim_type type, unsigned n); +__isl_give isl_local_space *isl_local_space_drop_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_local_space *isl_local_space_insert_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_local_space *isl_local_space_intersect( + __isl_take isl_local_space *ls1, __isl_take isl_local_space *ls2); + +__isl_give isl_local_space *isl_local_space_wrap( + __isl_take isl_local_space *ls); + +isl_bool isl_local_space_is_equal(__isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2); + +__isl_give isl_basic_map *isl_local_space_lifting( + __isl_take isl_local_space *ls); + +__isl_give isl_local_space *isl_local_space_flatten_domain( + __isl_take isl_local_space *ls); +__isl_give isl_local_space *isl_local_space_flatten_range( + __isl_take isl_local_space *ls); + +__isl_give isl_printer *isl_printer_print_local_space(__isl_take isl_printer *p, + __isl_keep isl_local_space *ls); +void isl_local_space_dump(__isl_keep isl_local_space *ls); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/lp.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/lp.h @@ -0,0 +1,37 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_LP_H +#define ISL_LP_H + +#include +#include +#include + +enum isl_lp_result { + isl_lp_error = -1, + isl_lp_ok = 0, + isl_lp_unbounded, + isl_lp_empty +}; + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_val *isl_basic_set_min_lp_val(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj); +__isl_give isl_val *isl_basic_set_max_lp_val(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/map.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/map.h @@ -0,0 +1,683 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_MAP_H +#define ISL_MAP_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* General notes: + * + * All structures are reference counted to allow reuse without duplication. + * A *_copy operation will increase the reference count, while a *_free + * operation will decrease the reference count and only actually release + * the structures when the reference count drops to zero. + * + * Functions that return an isa structure will in general _destroy_ + * all argument isa structures (the obvious execption begin the _copy + * functions). A pointer passed to such a function may therefore + * never be used after the function call. If you want to keep a + * reference to the old structure(s), use the appropriate _copy function. + */ + +unsigned isl_basic_map_n_in(const struct isl_basic_map *bmap); +unsigned isl_basic_map_n_out(const struct isl_basic_map *bmap); +unsigned isl_basic_map_n_param(const struct isl_basic_map *bmap); +unsigned isl_basic_map_n_div(const struct isl_basic_map *bmap); +unsigned isl_basic_map_total_dim(const struct isl_basic_map *bmap); +unsigned isl_basic_map_dim(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type); + +unsigned isl_map_n_in(const struct isl_map *map); +unsigned isl_map_n_out(const struct isl_map *map); +unsigned isl_map_n_param(const struct isl_map *map); +unsigned isl_map_dim(__isl_keep isl_map *map, enum isl_dim_type type); + +isl_ctx *isl_basic_map_get_ctx(__isl_keep isl_basic_map *bmap); +isl_ctx *isl_map_get_ctx(__isl_keep isl_map *map); +__isl_give isl_space *isl_basic_map_get_space(__isl_keep isl_basic_map *bmap); +__isl_give isl_space *isl_map_get_space(__isl_keep isl_map *map); + +__isl_give isl_aff *isl_basic_map_get_div(__isl_keep isl_basic_map *bmap, + int pos); + +__isl_give isl_local_space *isl_basic_map_get_local_space( + __isl_keep isl_basic_map *bmap); + +__isl_give isl_basic_map *isl_basic_map_set_tuple_name( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, const char *s); +const char *isl_basic_map_get_tuple_name(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type); +isl_bool isl_map_has_tuple_name(__isl_keep isl_map *map, + enum isl_dim_type type); +const char *isl_map_get_tuple_name(__isl_keep isl_map *map, + enum isl_dim_type type); +__isl_give isl_map *isl_map_set_tuple_name(__isl_take isl_map *map, + enum isl_dim_type type, const char *s); +const char *isl_basic_map_get_dim_name(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos); +isl_bool isl_map_has_dim_name(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); +const char *isl_map_get_dim_name(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); +__isl_give isl_basic_map *isl_basic_map_set_dim_name( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, const char *s); +__isl_give isl_map *isl_map_set_dim_name(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, const char *s); + +__isl_give isl_basic_map *isl_basic_map_set_tuple_id( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, __isl_take isl_id *id); +__isl_give isl_map *isl_map_set_dim_id(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); +isl_bool isl_basic_map_has_dim_id(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos); +isl_bool isl_map_has_dim_id(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); +__isl_give isl_id *isl_map_get_dim_id(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); +__isl_give isl_map *isl_map_set_tuple_id(__isl_take isl_map *map, + enum isl_dim_type type, __isl_take isl_id *id); +__isl_give isl_map *isl_map_reset_tuple_id(__isl_take isl_map *map, + enum isl_dim_type type); +isl_bool isl_map_has_tuple_id(__isl_keep isl_map *map, enum isl_dim_type type); +__isl_give isl_id *isl_map_get_tuple_id(__isl_keep isl_map *map, + enum isl_dim_type type); +__isl_give isl_map *isl_map_reset_user(__isl_take isl_map *map); + +int isl_basic_map_find_dim_by_name(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, const char *name); +int isl_map_find_dim_by_id(__isl_keep isl_map *map, enum isl_dim_type type, + __isl_keep isl_id *id); +int isl_map_find_dim_by_name(__isl_keep isl_map *map, enum isl_dim_type type, + const char *name); + +int isl_basic_map_is_rational(__isl_keep isl_basic_map *bmap); + +__isl_give isl_basic_map *isl_basic_map_identity(__isl_take isl_space *dim); +__isl_null isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_copy(__isl_keep isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_equal( + __isl_take isl_space *dim, unsigned n_equal); +__isl_give isl_basic_map *isl_basic_map_less_at(__isl_take isl_space *dim, + unsigned pos); +__isl_give isl_basic_map *isl_basic_map_more_at(__isl_take isl_space *dim, + unsigned pos); +__isl_give isl_basic_map *isl_basic_map_empty(__isl_take isl_space *dim); +__isl_give isl_basic_map *isl_basic_map_universe(__isl_take isl_space *dim); +__isl_give isl_basic_map *isl_basic_map_nat_universe(__isl_take isl_space *dim); +__isl_give isl_basic_map *isl_basic_map_remove_redundancies( + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_remove_redundancies(__isl_take isl_map *map); +__isl_give isl_basic_map *isl_map_simple_hull(__isl_take isl_map *map); +__isl_export +__isl_give isl_basic_map *isl_map_unshifted_simple_hull( + __isl_take isl_map *map); +__isl_give isl_basic_map *isl_map_plain_unshifted_simple_hull( + __isl_take isl_map *map); +__isl_give isl_basic_map *isl_map_unshifted_simple_hull_from_map_list( + __isl_take isl_map *map, __isl_take isl_map_list *list); + +__isl_export +__isl_give isl_basic_map *isl_basic_map_intersect_domain( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_basic_map *isl_basic_map_intersect_range( + __isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_basic_map *isl_basic_map_intersect( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); +__isl_give isl_basic_map *isl_basic_map_list_intersect( + __isl_take isl_basic_map_list *list); +__isl_export +__isl_give isl_map *isl_basic_map_union( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); +__isl_export +__isl_give isl_basic_map *isl_basic_map_apply_domain( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); +__isl_export +__isl_give isl_basic_map *isl_basic_map_apply_range( + __isl_take isl_basic_map *bmap1, + __isl_take isl_basic_map *bmap2); +__isl_export +__isl_give isl_basic_map *isl_basic_map_affine_hull( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_preimage_domain_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma); +__isl_give isl_basic_map *isl_basic_map_preimage_range_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma); +__isl_export +__isl_give isl_basic_map *isl_basic_map_reverse(__isl_take isl_basic_map *bmap); +__isl_give isl_basic_set *isl_basic_map_domain(__isl_take isl_basic_map *bmap); +__isl_give isl_basic_set *isl_basic_map_range(__isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_domain_map( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_range_map( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_remove_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_map *isl_basic_map_eliminate( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_map *isl_basic_map_from_basic_set( + __isl_take isl_basic_set *bset, __isl_take isl_space *dim); +__isl_export +__isl_give isl_basic_map *isl_basic_map_sample(__isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_basic_map *isl_basic_map_detect_equalities( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_read_from_file(isl_ctx *ctx, + FILE *input); +__isl_constructor +__isl_give isl_basic_map *isl_basic_map_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give isl_map *isl_map_read_from_file(isl_ctx *ctx, FILE *input); +__isl_constructor +__isl_give isl_map *isl_map_read_from_str(isl_ctx *ctx, const char *str); +void isl_basic_map_dump(__isl_keep isl_basic_map *bmap); +void isl_map_dump(__isl_keep isl_map *map); +__isl_give isl_printer *isl_printer_print_basic_map( + __isl_take isl_printer *printer, __isl_keep isl_basic_map *bmap); +__isl_give char *isl_map_to_str(__isl_keep isl_map *map); +__isl_give isl_printer *isl_printer_print_map(__isl_take isl_printer *printer, + __isl_keep isl_map *map); +__isl_give isl_basic_map *isl_basic_map_fix_si(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_basic_map *isl_basic_map_fix_val(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v); +__isl_give isl_basic_map *isl_basic_map_lower_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_basic_map *isl_basic_map_upper_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value); + +struct isl_basic_map *isl_basic_map_sum( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2); +struct isl_basic_map *isl_basic_map_neg(struct isl_basic_map *bmap); + +__isl_give isl_map *isl_map_sum(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_neg(__isl_take isl_map *map); +__isl_give isl_map *isl_map_floordiv_val(__isl_take isl_map *map, + __isl_take isl_val *d); + +__isl_export +isl_bool isl_basic_map_is_equal(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); +isl_bool isl_basic_map_is_disjoint(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); + +__isl_give isl_map *isl_basic_map_partial_lexmax( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_map *isl_basic_map_partial_lexmin( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_map *isl_map_partial_lexmax( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty); +__isl_give isl_map *isl_map_partial_lexmin( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty); +__isl_export +__isl_give isl_map *isl_basic_map_lexmin(__isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_map *isl_basic_map_lexmax(__isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_map *isl_map_lexmin(__isl_take isl_map *map); +__isl_export +__isl_give isl_map *isl_map_lexmax(__isl_take isl_map *map); +__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexmin_pw_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexmax_pw_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_pw_multi_aff *isl_basic_map_lexmin_pw_multi_aff( + __isl_take isl_basic_map *bmap); +__isl_give isl_pw_multi_aff *isl_map_lexmin_pw_multi_aff( + __isl_take isl_map *map); +__isl_give isl_pw_multi_aff *isl_map_lexmax_pw_multi_aff( + __isl_take isl_map *map); + +void isl_basic_map_print_internal(__isl_keep isl_basic_map *bmap, + FILE *out, int indent); + +struct isl_basic_map *isl_map_copy_basic_map(struct isl_map *map); +__isl_give isl_map *isl_map_drop_basic_map(__isl_take isl_map *map, + __isl_keep isl_basic_map *bmap); + +__isl_give isl_val *isl_basic_map_plain_get_val_if_fixed( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos); + +int isl_basic_map_image_is_bounded(__isl_keep isl_basic_map *bmap); +isl_bool isl_basic_map_plain_is_universe(__isl_keep isl_basic_map *bmap); +isl_bool isl_basic_map_is_universe(__isl_keep isl_basic_map *bmap); +isl_bool isl_basic_map_plain_is_empty(__isl_keep isl_basic_map *bmap); +__isl_export +isl_bool isl_basic_map_is_empty(__isl_keep isl_basic_map *bmap); +__isl_export +isl_bool isl_basic_map_is_subset(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); +isl_bool isl_basic_map_is_strict_subset(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); + +__isl_give isl_map *isl_map_universe(__isl_take isl_space *dim); +__isl_give isl_map *isl_map_nat_universe(__isl_take isl_space *dim); +__isl_give isl_map *isl_map_empty(__isl_take isl_space *dim); +__isl_give isl_map *isl_map_identity(__isl_take isl_space *dim); +__isl_give isl_map *isl_map_lex_lt_first(__isl_take isl_space *dim, unsigned n); +__isl_give isl_map *isl_map_lex_le_first(__isl_take isl_space *dim, unsigned n); +__isl_give isl_map *isl_map_lex_lt(__isl_take isl_space *set_dim); +__isl_give isl_map *isl_map_lex_le(__isl_take isl_space *set_dim); +__isl_give isl_map *isl_map_lex_gt_first(__isl_take isl_space *dim, unsigned n); +__isl_give isl_map *isl_map_lex_ge_first(__isl_take isl_space *dim, unsigned n); +__isl_give isl_map *isl_map_lex_gt(__isl_take isl_space *set_dim); +__isl_give isl_map *isl_map_lex_ge(__isl_take isl_space *set_dim); +__isl_null isl_map *isl_map_free(__isl_take isl_map *map); +__isl_give isl_map *isl_map_copy(__isl_keep isl_map *map); +__isl_export +__isl_give isl_map *isl_map_reverse(__isl_take isl_map *map); +__isl_export +__isl_give isl_map *isl_map_union( + __isl_take isl_map *map1, + __isl_take isl_map *map2); +struct isl_map *isl_map_union_disjoint( + struct isl_map *map1, struct isl_map *map2); +__isl_export +__isl_give isl_map *isl_map_intersect_domain( + __isl_take isl_map *map, + __isl_take isl_set *set); +__isl_export +__isl_give isl_map *isl_map_intersect_range( + __isl_take isl_map *map, + __isl_take isl_set *set); +__isl_export +__isl_give isl_map *isl_map_apply_domain( + __isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_export +__isl_give isl_map *isl_map_apply_range( + __isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_preimage_domain_multi_aff(__isl_take isl_map *map, + __isl_take isl_multi_aff *ma); +__isl_give isl_map *isl_map_preimage_range_multi_aff(__isl_take isl_map *map, + __isl_take isl_multi_aff *ma); +__isl_give isl_map *isl_map_preimage_domain_pw_multi_aff( + __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_map *isl_map_preimage_range_pw_multi_aff( + __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_map *isl_map_preimage_domain_multi_pw_aff( + __isl_take isl_map *map, __isl_take isl_multi_pw_aff *mpa); +__isl_give isl_basic_map *isl_basic_map_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2); +__isl_give isl_map *isl_map_product(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_basic_map *isl_basic_map_domain_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2); +__isl_give isl_basic_map *isl_basic_map_range_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2); +__isl_give isl_map *isl_map_domain_product(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_range_product(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_basic_map *isl_basic_map_flat_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2); +__isl_give isl_map *isl_map_flat_product(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_basic_map *isl_basic_map_flat_range_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2); +__isl_give isl_map *isl_map_flat_domain_product(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_flat_range_product(__isl_take isl_map *map1, + __isl_take isl_map *map2); +isl_bool isl_map_domain_is_wrapping(__isl_keep isl_map *map); +isl_bool isl_map_range_is_wrapping(__isl_keep isl_map *map); +__isl_give isl_map *isl_map_factor_domain(__isl_take isl_map *map); +__isl_give isl_map *isl_map_factor_range(__isl_take isl_map *map); +__isl_give isl_map *isl_map_domain_factor_domain(__isl_take isl_map *map); +__isl_give isl_map *isl_map_domain_factor_range(__isl_take isl_map *map); +__isl_give isl_map *isl_map_range_factor_domain(__isl_take isl_map *map); +__isl_give isl_map *isl_map_range_factor_range(__isl_take isl_map *map); +__isl_export +__isl_give isl_map *isl_map_intersect(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_export +__isl_give isl_map *isl_map_intersect_params(__isl_take isl_map *map, + __isl_take isl_set *params); +__isl_export +__isl_give isl_map *isl_map_subtract( + __isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_subtract_domain(__isl_take isl_map *map, + __isl_take isl_set *dom); +__isl_give isl_map *isl_map_subtract_range(__isl_take isl_map *map, + __isl_take isl_set *dom); +__isl_export +__isl_give isl_map *isl_map_complement(__isl_take isl_map *map); +struct isl_map *isl_map_fix_input_si(struct isl_map *map, + unsigned input, int value); +__isl_give isl_map *isl_map_fix_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_map *isl_map_fix_val(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v); +__isl_give isl_map *isl_map_lower_bound_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_map *isl_map_upper_bound_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value); +__isl_export +__isl_give isl_basic_set *isl_basic_map_deltas(__isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_set *isl_map_deltas(__isl_take isl_map *map); +__isl_give isl_basic_map *isl_basic_map_deltas_map( + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_deltas_map(__isl_take isl_map *map); +__isl_export +__isl_give isl_map *isl_map_detect_equalities(__isl_take isl_map *map); +__isl_export +__isl_give isl_basic_map *isl_map_affine_hull(__isl_take isl_map *map); +__isl_give isl_basic_map *isl_map_convex_hull(__isl_take isl_map *map); +__isl_export +__isl_give isl_basic_map *isl_map_polyhedral_hull(__isl_take isl_map *map); +__isl_give isl_basic_map *isl_basic_map_add_dims(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned n); +__isl_give isl_map *isl_map_add_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned n); +__isl_give isl_basic_map *isl_basic_map_insert_dims( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, + unsigned pos, unsigned n); +__isl_give isl_map *isl_map_insert_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, unsigned n); +__isl_give isl_basic_map *isl_basic_map_move_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_map *isl_map_move_dims(__isl_take isl_map *map, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_basic_map *isl_basic_map_project_out( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_map *isl_map_project_out(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_map *isl_basic_map_remove_divs( + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_remove_unknown_divs(__isl_take isl_map *map); +__isl_give isl_map *isl_map_remove_divs(__isl_take isl_map *map); +__isl_give isl_map *isl_map_eliminate(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_map *isl_map_remove_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_map *isl_basic_map_remove_divs_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_map *isl_map_remove_divs_involving_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +struct isl_map *isl_map_remove_inputs(struct isl_map *map, + unsigned first, unsigned n); + +__isl_give isl_basic_map *isl_basic_map_equate(__isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_basic_map *isl_basic_map_order_ge(__isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_map *isl_map_order_ge(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_map *isl_map_order_le(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_map *isl_map_equate(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_map *isl_map_oppose(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_map *isl_map_order_lt(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_basic_map *isl_basic_map_order_gt(__isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); +__isl_give isl_map *isl_map_order_gt(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); + +__isl_export +__isl_give isl_map *isl_set_identity(__isl_take isl_set *set); + +__isl_export +isl_bool isl_basic_set_is_wrapping(__isl_keep isl_basic_set *bset); +__isl_export +isl_bool isl_set_is_wrapping(__isl_keep isl_set *set); +__isl_give isl_basic_set *isl_basic_map_wrap(__isl_take isl_basic_map *bmap); +__isl_give isl_set *isl_map_wrap(__isl_take isl_map *map); +__isl_give isl_basic_map *isl_basic_set_unwrap(__isl_take isl_basic_set *bset); +__isl_give isl_map *isl_set_unwrap(__isl_take isl_set *set); +__isl_export +__isl_give isl_basic_map *isl_basic_map_flatten(__isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_map *isl_map_flatten(__isl_take isl_map *map); +__isl_export +__isl_give isl_basic_map *isl_basic_map_flatten_domain( + __isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_basic_map *isl_basic_map_flatten_range( + __isl_take isl_basic_map *bmap); +__isl_export +__isl_give isl_map *isl_map_flatten_domain(__isl_take isl_map *map); +__isl_export +__isl_give isl_map *isl_map_flatten_range(__isl_take isl_map *map); +__isl_export +__isl_give isl_basic_set *isl_basic_set_flatten(__isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_set *isl_set_flatten(__isl_take isl_set *set); +__isl_give isl_map *isl_set_flatten_map(__isl_take isl_set *set); +__isl_give isl_set *isl_map_params(__isl_take isl_map *map); +__isl_give isl_set *isl_map_domain(__isl_take isl_map *bmap); +__isl_give isl_set *isl_map_range(__isl_take isl_map *map); +__isl_give isl_map *isl_map_domain_map(__isl_take isl_map *map); +__isl_give isl_map *isl_map_range_map(__isl_take isl_map *map); +__isl_give isl_map *isl_set_wrapped_domain_map(__isl_take isl_set *set); +__isl_constructor +__isl_give isl_map *isl_map_from_basic_map(__isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_from_domain(__isl_take isl_set *set); +__isl_give isl_basic_map *isl_basic_map_from_domain( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_map *isl_basic_map_from_range( + __isl_take isl_basic_set *bset); +__isl_give isl_map *isl_map_from_range(__isl_take isl_set *set); +__isl_give isl_basic_map *isl_basic_map_from_domain_and_range( + __isl_take isl_basic_set *domain, __isl_take isl_basic_set *range); +__isl_give isl_map *isl_map_from_domain_and_range(__isl_take isl_set *domain, + __isl_take isl_set *range); +__isl_give isl_map *isl_map_from_set(__isl_take isl_set *set, + __isl_take isl_space *dim); +__isl_export +__isl_give isl_basic_map *isl_map_sample(__isl_take isl_map *map); + +isl_bool isl_map_plain_is_empty(__isl_keep isl_map *map); +isl_bool isl_map_plain_is_universe(__isl_keep isl_map *map); +__isl_export +isl_bool isl_map_is_empty(__isl_keep isl_map *map); +__isl_export +isl_bool isl_map_is_subset(__isl_keep isl_map *map1, __isl_keep isl_map *map2); +__isl_export +isl_bool isl_map_is_strict_subset(__isl_keep isl_map *map1, + __isl_keep isl_map *map2); +__isl_export +isl_bool isl_map_is_equal(__isl_keep isl_map *map1, __isl_keep isl_map *map2); +__isl_export +isl_bool isl_map_is_disjoint(__isl_keep isl_map *map1, + __isl_keep isl_map *map2); +isl_bool isl_basic_map_is_single_valued(__isl_keep isl_basic_map *bmap); +isl_bool isl_map_plain_is_single_valued(__isl_keep isl_map *map); +__isl_export +isl_bool isl_map_is_single_valued(__isl_keep isl_map *map); +isl_bool isl_map_plain_is_injective(__isl_keep isl_map *map); +__isl_export +isl_bool isl_map_is_injective(__isl_keep isl_map *map); +__isl_export +isl_bool isl_map_is_bijective(__isl_keep isl_map *map); +isl_bool isl_map_is_identity(__isl_keep isl_map *map); +int isl_map_is_translation(__isl_keep isl_map *map); +int isl_map_has_equal_space(__isl_keep isl_map *map1, __isl_keep isl_map *map2); + +isl_bool isl_basic_map_can_zip(__isl_keep isl_basic_map *bmap); +isl_bool isl_map_can_zip(__isl_keep isl_map *map); +__isl_give isl_basic_map *isl_basic_map_zip(__isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_zip(__isl_take isl_map *map); + +isl_bool isl_basic_map_can_curry(__isl_keep isl_basic_map *bmap); +isl_bool isl_map_can_curry(__isl_keep isl_map *map); +__isl_give isl_basic_map *isl_basic_map_curry(__isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_curry(__isl_take isl_map *map); + +isl_bool isl_map_can_range_curry(__isl_keep isl_map *map); +__isl_give isl_map *isl_map_range_curry(__isl_take isl_map *map); + +isl_bool isl_basic_map_can_uncurry(__isl_keep isl_basic_map *bmap); +isl_bool isl_map_can_uncurry(__isl_keep isl_map *map); +__isl_give isl_basic_map *isl_basic_map_uncurry(__isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_uncurry(__isl_take isl_map *map); + +__isl_give isl_map *isl_map_make_disjoint(__isl_take isl_map *map); +__isl_give isl_map *isl_basic_map_compute_divs(__isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_compute_divs(__isl_take isl_map *map); +__isl_give isl_map *isl_map_align_divs(__isl_take isl_map *map); + +__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_map *isl_basic_map_drop_constraints_not_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_map *isl_map_drop_constraints_involving_dims( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_map *isl_map_drop_constraints_not_involving_dims( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); + +isl_bool isl_basic_map_involves_dims(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +isl_bool isl_map_involves_dims(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); + +void isl_map_print_internal(__isl_keep isl_map *map, FILE *out, int indent); + +__isl_give isl_val *isl_map_plain_get_val_if_fixed(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos); + +__isl_give isl_basic_map *isl_basic_map_gist_domain( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *context); +__isl_export +__isl_give isl_basic_map *isl_basic_map_gist(__isl_take isl_basic_map *bmap, + __isl_take isl_basic_map *context); +__isl_export +__isl_give isl_map *isl_map_gist(__isl_take isl_map *map, + __isl_take isl_map *context); +__isl_export +__isl_give isl_map *isl_map_gist_domain(__isl_take isl_map *map, + __isl_take isl_set *context); +__isl_give isl_map *isl_map_gist_range(__isl_take isl_map *map, + __isl_take isl_set *context); +__isl_give isl_map *isl_map_gist_params(__isl_take isl_map *map, + __isl_take isl_set *context); +__isl_give isl_map *isl_map_gist_basic_map(__isl_take isl_map *map, + __isl_take isl_basic_map *context); + +__isl_export +__isl_give isl_map *isl_map_coalesce(__isl_take isl_map *map); + +isl_bool isl_map_plain_is_equal(__isl_keep isl_map *map1, + __isl_keep isl_map *map2); + +uint32_t isl_map_get_hash(__isl_keep isl_map *map); + +int isl_map_n_basic_map(__isl_keep isl_map *map); +__isl_export +isl_stat isl_map_foreach_basic_map(__isl_keep isl_map *map, + isl_stat (*fn)(__isl_take isl_basic_map *bmap, void *user), void *user); + +__isl_give isl_map *isl_set_lifting(__isl_take isl_set *set); + +__isl_give isl_map *isl_map_fixed_power_val(__isl_take isl_map *map, + __isl_take isl_val *exp); +__isl_give isl_map *isl_map_power(__isl_take isl_map *map, int *exact); +__isl_give isl_map *isl_map_reaching_path_lengths(__isl_take isl_map *map, + int *exact); +__isl_give isl_map *isl_map_transitive_closure(__isl_take isl_map *map, + int *exact); + +__isl_give isl_map *isl_map_lex_le_map(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_lex_lt_map(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_lex_ge_map(__isl_take isl_map *map1, + __isl_take isl_map *map2); +__isl_give isl_map *isl_map_lex_gt_map(__isl_take isl_map *map1, + __isl_take isl_map *map2); + +__isl_give isl_basic_map *isl_basic_map_align_params( + __isl_take isl_basic_map *bmap, __isl_take isl_space *model); +__isl_give isl_map *isl_map_align_params(__isl_take isl_map *map, + __isl_take isl_space *model); + +__isl_give isl_mat *isl_basic_map_equalities_matrix( + __isl_keep isl_basic_map *bmap, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5); +__isl_give isl_mat *isl_basic_map_inequalities_matrix( + __isl_keep isl_basic_map *bmap, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5); +__isl_give isl_basic_map *isl_basic_map_from_constraint_matrices( + __isl_take isl_space *dim, + __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5); + +__isl_give isl_basic_map *isl_basic_map_from_aff(__isl_take isl_aff *aff); +__isl_give isl_basic_map *isl_basic_map_from_multi_aff( + __isl_take isl_multi_aff *maff); +__isl_give isl_basic_map *isl_basic_map_from_aff_list( + __isl_take isl_space *domain_dim, __isl_take isl_aff_list *list); + +__isl_give isl_map *isl_map_from_aff(__isl_take isl_aff *aff); +__isl_give isl_map *isl_map_from_multi_aff(__isl_take isl_multi_aff *maff); + +__isl_give isl_pw_aff *isl_map_dim_max(__isl_take isl_map *map, int pos); + +ISL_DECLARE_LIST_FN(basic_map) +ISL_DECLARE_LIST_FN(map) + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/map_to_basic_set.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/map_to_basic_set.h @@ -0,0 +1,18 @@ +#ifndef ISL_MAP_TO_BASIC_SET_H +#define ISL_MAP_TO_BASIC_SET_H + +#include +#include +#include + +#define ISL_KEY isl_map +#define ISL_VAL isl_basic_set +#define ISL_HMAP_SUFFIX map_to_basic_set +#define ISL_HMAP isl_map_to_basic_set +#include +#undef ISL_KEY +#undef ISL_VAL +#undef ISL_HMAP_SUFFIX +#undef ISL_HMAP + +#endif Index: lib/Analysis/isl/include/isl/map_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/map_type.h @@ -0,0 +1,37 @@ +#ifndef ISL_MAP_TYPE_H +#define ISL_MAP_TYPE_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct __isl_subclass(isl_map) isl_basic_map; +typedef struct isl_basic_map isl_basic_map; +ISL_DECLARE_LIST_TYPE(basic_map) +struct __isl_subclass(isl_union_map) isl_map; +typedef struct isl_map isl_map; +ISL_DECLARE_LIST_TYPE(map) + +#ifndef isl_basic_set +struct __isl_subclass(isl_set) isl_basic_set; +typedef struct isl_basic_set isl_basic_set; +ISL_DECLARE_LIST_TYPE(basic_set) +#endif + +#ifndef isl_set +struct __isl_subclass(isl_union_set) isl_set; +typedef struct isl_set isl_set; +ISL_DECLARE_LIST_TYPE(set) +#endif + +ISL_DECLARE_LIST_FN(basic_set) +ISL_DECLARE_LIST_FN(set) + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/mat.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/mat.h @@ -0,0 +1,111 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_MAT_H +#define ISL_MAT_H + +#include + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_mat; +typedef struct isl_mat isl_mat; + +isl_ctx *isl_mat_get_ctx(__isl_keep isl_mat *mat); + +__isl_give isl_mat *isl_mat_alloc(isl_ctx *ctx, + unsigned n_row, unsigned n_col); +struct isl_mat *isl_mat_dup(struct isl_mat *mat); +struct isl_mat *isl_mat_extend(struct isl_mat *mat, + unsigned n_row, unsigned n_col); +struct isl_mat *isl_mat_identity(struct isl_ctx *ctx, unsigned n_row); +__isl_give isl_mat *isl_mat_copy(__isl_keep isl_mat *mat); +struct isl_mat *isl_mat_cow(struct isl_mat *mat); +__isl_null isl_mat *isl_mat_free(__isl_take isl_mat *mat); + +int isl_mat_rows(__isl_keep isl_mat *mat); +int isl_mat_cols(__isl_keep isl_mat *mat); +__isl_give isl_val *isl_mat_get_element_val(__isl_keep isl_mat *mat, + int row, int col); +__isl_give isl_mat *isl_mat_set_element_si(__isl_take isl_mat *mat, + int row, int col, int v); +__isl_give isl_mat *isl_mat_set_element_val(__isl_take isl_mat *mat, + int row, int col, __isl_take isl_val *v); + +struct isl_mat *isl_mat_swap_cols(struct isl_mat *mat, unsigned i, unsigned j); +struct isl_mat *isl_mat_swap_rows(struct isl_mat *mat, unsigned i, unsigned j); + +struct isl_vec *isl_mat_vec_product(struct isl_mat *mat, struct isl_vec *vec); +struct isl_vec *isl_vec_mat_product(struct isl_vec *vec, struct isl_mat *mat); +__isl_give isl_vec *isl_mat_vec_inverse_product(__isl_take isl_mat *mat, + __isl_take isl_vec *vec); +struct isl_mat *isl_mat_aff_direct_sum(struct isl_mat *left, + struct isl_mat *right); +__isl_give isl_mat *isl_mat_diagonal(__isl_take isl_mat *mat1, + __isl_take isl_mat *mat2); +struct isl_mat *isl_mat_left_hermite(struct isl_mat *M, + int neg, struct isl_mat **U, struct isl_mat **Q); +struct isl_mat *isl_mat_lin_to_aff(struct isl_mat *mat); +struct isl_mat *isl_mat_inverse_product(struct isl_mat *left, + struct isl_mat *right); +__isl_give isl_mat *isl_mat_product(__isl_take isl_mat *left, + __isl_take isl_mat *right); +struct isl_mat *isl_mat_transpose(struct isl_mat *mat); +__isl_give isl_mat *isl_mat_right_inverse(__isl_take isl_mat *mat); +__isl_give isl_mat *isl_mat_right_kernel(__isl_take isl_mat *mat); + +__isl_give isl_mat *isl_mat_normalize(__isl_take isl_mat *mat); +__isl_give isl_mat *isl_mat_normalize_row(__isl_take isl_mat *mat, int row); + +struct isl_mat *isl_mat_drop_cols(struct isl_mat *mat, + unsigned col, unsigned n); +struct isl_mat *isl_mat_drop_rows(struct isl_mat *mat, + unsigned row, unsigned n); +__isl_give isl_mat *isl_mat_insert_cols(__isl_take isl_mat *mat, + unsigned col, unsigned n); +__isl_give isl_mat *isl_mat_insert_rows(__isl_take isl_mat *mat, + unsigned row, unsigned n); +__isl_give isl_mat *isl_mat_move_cols(__isl_take isl_mat *mat, + unsigned dst_col, unsigned src_col, unsigned n); +__isl_give isl_mat *isl_mat_add_rows(__isl_take isl_mat *mat, unsigned n); +__isl_give isl_mat *isl_mat_insert_zero_cols(__isl_take isl_mat *mat, + unsigned first, unsigned n); +__isl_give isl_mat *isl_mat_add_zero_cols(__isl_take isl_mat *mat, unsigned n); +__isl_give isl_mat *isl_mat_insert_zero_rows(__isl_take isl_mat *mat, + unsigned row, unsigned n); +__isl_give isl_mat *isl_mat_add_zero_rows(__isl_take isl_mat *mat, unsigned n); + +void isl_mat_col_add(__isl_keep isl_mat *mat, int dst_col, int src_col); + +struct isl_mat *isl_mat_unimodular_complete(struct isl_mat *M, int row); + +__isl_give isl_mat *isl_mat_from_row_vec(__isl_take isl_vec *vec); +__isl_give isl_mat *isl_mat_concat(__isl_take isl_mat *top, + __isl_take isl_mat *bot); +__isl_give isl_mat *isl_mat_vec_concat(__isl_take isl_mat *top, + __isl_take isl_vec *bot); + +int isl_mat_is_equal(__isl_keep isl_mat *mat1, __isl_keep isl_mat *mat2); + +int isl_mat_initial_non_zero_cols(__isl_keep isl_mat *mat); + +void isl_mat_print_internal(__isl_keep isl_mat *mat, FILE *out, int indent); +void isl_mat_dump(__isl_keep isl_mat *mat); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/maybe.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/maybe.h @@ -0,0 +1,7 @@ +#ifndef ISL_MAYBE_H +#define ISL_MAYBE_H + +#define ISL_xMAYBE(TYPE) isl_maybe_ ## TYPE +#define ISL_MAYBE(TYPE) ISL_xMAYBE(TYPE) + +#endif Index: lib/Analysis/isl/include/isl/maybe_ast_expr.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/maybe_ast_expr.h @@ -0,0 +1,8 @@ +#ifndef ISL_MAYBE_AST_EXPR_H +#define ISL_MAYBE_AST_EXPR_H + +#define ISL_TYPE isl_ast_expr +#include +#undef ISL_TYPE + +#endif Index: lib/Analysis/isl/include/isl/maybe_basic_set.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/maybe_basic_set.h @@ -0,0 +1,8 @@ +#ifndef ISL_MAYBE_BASIC_SET_H +#define ISL_MAYBE_BASIC_SET_H + +#define ISL_TYPE isl_basic_set +#include +#undef ISL_TYPE + +#endif Index: lib/Analysis/isl/include/isl/maybe_id.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/maybe_id.h @@ -0,0 +1,8 @@ +#ifndef ISL_MAYBE_ID_H +#define ISL_MAYBE_ID_H + +#define ISL_TYPE isl_id +#include +#undef ISL_TYPE + +#endif Index: lib/Analysis/isl/include/isl/maybe_pw_aff.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/maybe_pw_aff.h @@ -0,0 +1,8 @@ +#ifndef ISL_MAYBE_PW_AFF_H +#define ISL_MAYBE_PW_AFF_H + +#define ISL_TYPE isl_pw_aff +#include +#undef ISL_TYPE + +#endif Index: lib/Analysis/isl/include/isl/maybe_templ.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/maybe_templ.h @@ -0,0 +1,12 @@ +#include +#include + +/* A structure that possibly contains a pointer to an object of type ISL_TYPE. + * The pointer in "value" is only valid if "valid" is isl_bool_true. + * Otherwise, "value" is set to NULL. + */ +struct ISL_MAYBE(ISL_TYPE) { + isl_bool valid; + ISL_TYPE *value; +}; +typedef struct ISL_MAYBE(ISL_TYPE) ISL_MAYBE(ISL_TYPE); Index: lib/Analysis/isl/include/isl/multi.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/multi.h @@ -0,0 +1,146 @@ +#ifndef ISL_MULTI_H +#define ISL_MULTI_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define ISL_DECLARE_MULTI(BASE) \ +unsigned isl_multi_##BASE##_dim(__isl_keep isl_multi_##BASE *multi, \ + enum isl_dim_type type); \ +isl_ctx *isl_multi_##BASE##_get_ctx( \ + __isl_keep isl_multi_##BASE *multi); \ +__isl_give isl_space *isl_multi_##BASE##_get_space( \ + __isl_keep isl_multi_##BASE *multi); \ +__isl_give isl_space *isl_multi_##BASE##_get_domain_space( \ + __isl_keep isl_multi_##BASE *multi); \ +int isl_multi_##BASE##_find_dim_by_name( \ + __isl_keep isl_multi_##BASE *multi, \ + enum isl_dim_type type, const char *name); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_from_##BASE##_list( \ + __isl_take isl_space *space, __isl_take isl_##BASE##_list *list); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_zero( \ + __isl_take isl_space *space); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_copy( \ + __isl_keep isl_multi_##BASE *multi); \ +__isl_null isl_multi_##BASE *isl_multi_##BASE##_free( \ + __isl_take isl_multi_##BASE *multi); \ +isl_bool isl_multi_##BASE##_plain_is_equal( \ + __isl_keep isl_multi_##BASE *multi1, \ + __isl_keep isl_multi_##BASE *multi2); \ +int isl_multi_##BASE##_find_dim_by_id( \ + __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type, \ + __isl_keep isl_id *id); \ +__isl_give isl_id *isl_multi_##BASE##_get_dim_id( \ + __isl_take isl_multi_##BASE *multi, \ + enum isl_dim_type type, unsigned pos); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_dim_name( \ + __isl_take isl_multi_##BASE *multi, \ + enum isl_dim_type type, unsigned pos, const char *s); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_dim_id( \ + __isl_take isl_multi_##BASE *multi, \ + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); \ +const char *isl_multi_##BASE##_get_tuple_name( \ + __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type); \ +isl_bool isl_multi_##BASE##_has_tuple_id( \ + __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type); \ +__isl_give isl_id *isl_multi_##BASE##_get_tuple_id( \ + __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_tuple_name( \ + __isl_take isl_multi_##BASE *multi, \ + enum isl_dim_type type, const char *s); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_tuple_id( \ + __isl_take isl_multi_##BASE *multi, \ + enum isl_dim_type type, __isl_take isl_id *id); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_reset_tuple_id( \ + __isl_take isl_multi_##BASE *multi, enum isl_dim_type type); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_reset_user( \ + __isl_take isl_multi_##BASE *multi); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_drop_dims( \ + __isl_take isl_multi_##BASE *multi, enum isl_dim_type type, \ + unsigned first, unsigned n); \ +__isl_give isl_##BASE *isl_multi_##BASE##_get_##BASE( \ + __isl_keep isl_multi_##BASE *multi, int pos); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_##BASE( \ + __isl_take isl_multi_##BASE *multi, int pos, \ + __isl_take isl_##BASE *el); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_splice( \ + __isl_take isl_multi_##BASE *multi1, unsigned pos, \ + __isl_take isl_multi_##BASE *multi2); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_flatten_range( \ + __isl_take isl_multi_##BASE *multi); \ +__isl_export \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_flat_range_product( \ + __isl_take isl_multi_##BASE *multi1, \ + __isl_take isl_multi_##BASE *multi2); \ +__isl_export \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_product( \ + __isl_take isl_multi_##BASE *multi1, \ + __isl_take isl_multi_##BASE *multi2); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_factor_range( \ + __isl_take isl_multi_##BASE *multi); \ +isl_bool isl_multi_##BASE##_range_is_wrapping( \ + __isl_keep isl_multi_##BASE *multi); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_factor_domain( \ + __isl_take isl_multi_##BASE *multi); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_factor_range( \ + __isl_take isl_multi_##BASE *multi); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_val( \ + __isl_take isl_multi_##BASE *multi, __isl_take isl_val *v); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_down_val( \ + __isl_take isl_multi_##BASE *multi, __isl_take isl_val *v); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_multi_val( \ + __isl_take isl_multi_##BASE *multi, \ + __isl_take isl_multi_val *mv); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_down_multi_val( \ + __isl_take isl_multi_##BASE *multi, \ + __isl_take isl_multi_val *mv); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_mod_multi_val( \ + __isl_take isl_multi_##BASE *multi, \ + __isl_take isl_multi_val *mv); \ +__isl_export \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_add( \ + __isl_take isl_multi_##BASE *multi1, \ + __isl_take isl_multi_##BASE *multi2); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_sub( \ + __isl_take isl_multi_##BASE *multi1, \ + __isl_take isl_multi_##BASE *multi2); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_align_params( \ + __isl_take isl_multi_##BASE *multi, \ + __isl_take isl_space *model); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_from_range( \ + __isl_take isl_multi_##BASE *multi); + +#define ISL_DECLARE_MULTI_NEG(BASE) \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_neg( \ + __isl_take isl_multi_##BASE *multi); + +#define ISL_DECLARE_MULTI_DIMS(BASE) \ +isl_bool isl_multi_##BASE##_involves_dims( \ + __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type, \ + unsigned first, unsigned n); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_insert_dims( \ + __isl_take isl_multi_##BASE *multi, enum isl_dim_type type, \ + unsigned first, unsigned n); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_add_dims( \ + __isl_take isl_multi_##BASE *multi, enum isl_dim_type type, \ + unsigned n); + +#define ISL_DECLARE_MULTI_WITH_DOMAIN(BASE) \ +__isl_export \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_product( \ + __isl_take isl_multi_##BASE *multi1, \ + __isl_take isl_multi_##BASE *multi2); \ +__isl_give isl_multi_##BASE *isl_multi_##BASE##_splice( \ + __isl_take isl_multi_##BASE *multi1, unsigned in_pos, \ + unsigned out_pos, __isl_take isl_multi_##BASE *multi2); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/obj.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/obj.h @@ -0,0 +1,57 @@ +#ifndef ISL_OBJ_H +#define ISL_OBJ_H + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_obj_vtable { + void *(*copy)(void *v1); + void *(*add)(void *v1, void *v2); + __isl_give isl_printer *(*print)(__isl_take isl_printer *p, void *v); + void (*free)(void *v); +}; +typedef struct isl_obj_vtable *isl_obj_type; +extern struct isl_obj_vtable isl_obj_none_vtable; +#define isl_obj_none (&isl_obj_none_vtable) +extern struct isl_obj_vtable isl_obj_int_vtable; +#define isl_obj_int (&isl_obj_int_vtable) +extern struct isl_obj_vtable isl_obj_val_vtable; +#define isl_obj_val (&isl_obj_val_vtable) +extern struct isl_obj_vtable isl_obj_set_vtable; +#define isl_obj_set (&isl_obj_set_vtable) +extern struct isl_obj_vtable isl_obj_union_set_vtable; +#define isl_obj_union_set (&isl_obj_union_set_vtable) +extern struct isl_obj_vtable isl_obj_map_vtable; +#define isl_obj_map (&isl_obj_map_vtable) +extern struct isl_obj_vtable isl_obj_union_map_vtable; +#define isl_obj_union_map (&isl_obj_union_map_vtable) +extern struct isl_obj_vtable isl_obj_pw_multi_aff_vtable; +#define isl_obj_pw_multi_aff (&isl_obj_pw_multi_aff_vtable) +extern struct isl_obj_vtable isl_obj_pw_qpolynomial_vtable; +#define isl_obj_pw_qpolynomial (&isl_obj_pw_qpolynomial_vtable) +extern struct isl_obj_vtable isl_obj_union_pw_qpolynomial_vtable; +#define isl_obj_union_pw_qpolynomial (&isl_obj_union_pw_qpolynomial_vtable) +extern struct isl_obj_vtable isl_obj_pw_qpolynomial_fold_vtable; +#define isl_obj_pw_qpolynomial_fold (&isl_obj_pw_qpolynomial_fold_vtable) +extern struct isl_obj_vtable isl_obj_union_pw_qpolynomial_fold_vtable; +#define isl_obj_union_pw_qpolynomial_fold (&isl_obj_union_pw_qpolynomial_fold_vtable) +extern struct isl_obj_vtable isl_obj_schedule_vtable; +#define isl_obj_schedule (&isl_obj_schedule_vtable) +struct isl_obj { + isl_obj_type type; + void *v; +}; + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/options.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/options.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_OPTIONS_H +#define ISL_OPTIONS_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_options; + +ISL_ARG_DECL(isl_options, struct isl_options, isl_options_args) + +#define ISL_BOUND_BERNSTEIN 0 +#define ISL_BOUND_RANGE 1 +isl_stat isl_options_set_bound(isl_ctx *ctx, int val); +int isl_options_get_bound(isl_ctx *ctx); + +#define ISL_ON_ERROR_WARN 0 +#define ISL_ON_ERROR_CONTINUE 1 +#define ISL_ON_ERROR_ABORT 2 +isl_stat isl_options_set_on_error(isl_ctx *ctx, int val); +int isl_options_get_on_error(isl_ctx *ctx); + +isl_stat isl_options_set_gbr_only_first(isl_ctx *ctx, int val); +int isl_options_get_gbr_only_first(isl_ctx *ctx); + +#define ISL_SCHEDULE_ALGORITHM_ISL 0 +#define ISL_SCHEDULE_ALGORITHM_FEAUTRIER 1 +isl_stat isl_options_set_schedule_algorithm(isl_ctx *ctx, int val); +int isl_options_get_schedule_algorithm(isl_ctx *ctx); + +isl_stat isl_options_set_pip_symmetry(isl_ctx *ctx, int val); +int isl_options_get_pip_symmetry(isl_ctx *ctx); + +isl_stat isl_options_set_coalesce_bounded_wrapping(isl_ctx *ctx, int val); +int isl_options_get_coalesce_bounded_wrapping(isl_ctx *ctx); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/point.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/point.h @@ -0,0 +1,43 @@ +#ifndef ISL_POINT_H +#define ISL_POINT_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct __isl_subclass(isl_basic_set) isl_point; +typedef struct isl_point isl_point; + +isl_ctx *isl_point_get_ctx(__isl_keep isl_point *pnt); +__isl_give isl_space *isl_point_get_space(__isl_keep isl_point *pnt); + +__isl_give isl_point *isl_point_zero(__isl_take isl_space *dim); +__isl_give isl_point *isl_point_copy(__isl_keep isl_point *pnt); +void isl_point_free(__isl_take isl_point *pnt); + +__isl_give isl_val *isl_point_get_coordinate_val(__isl_keep isl_point *pnt, + enum isl_dim_type type, int pos); +__isl_give isl_point *isl_point_set_coordinate_val(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, __isl_take isl_val *v); + +__isl_give isl_point *isl_point_add_ui(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, unsigned val); +__isl_give isl_point *isl_point_sub_ui(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, unsigned val); + +__isl_give isl_point *isl_point_void(__isl_take isl_space *dim); +isl_bool isl_point_is_void(__isl_keep isl_point *pnt); + +__isl_give isl_printer *isl_printer_print_point( + __isl_take isl_printer *printer, __isl_keep isl_point *pnt); +void isl_point_dump(__isl_keep isl_point *pnt); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/polynomial.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/polynomial.h @@ -0,0 +1,671 @@ +#ifndef ISL_POLYNOMIAL_H +#define ISL_POLYNOMIAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +isl_ctx *isl_qpolynomial_get_ctx(__isl_keep isl_qpolynomial *qp); +__isl_give isl_space *isl_qpolynomial_get_domain_space( + __isl_keep isl_qpolynomial *qp); +__isl_give isl_space *isl_qpolynomial_get_space(__isl_keep isl_qpolynomial *qp); +unsigned isl_qpolynomial_dim(__isl_keep isl_qpolynomial *qp, + enum isl_dim_type type); +isl_bool isl_qpolynomial_involves_dims(__isl_keep isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_val *isl_qpolynomial_get_constant_val( + __isl_keep isl_qpolynomial *qp); + +__isl_give isl_qpolynomial *isl_qpolynomial_set_dim_name( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned pos, const char *s); + +__isl_give isl_qpolynomial *isl_qpolynomial_zero_on_domain(__isl_take isl_space *dim); +__isl_give isl_qpolynomial *isl_qpolynomial_one_on_domain(__isl_take isl_space *dim); +__isl_give isl_qpolynomial *isl_qpolynomial_infty_on_domain(__isl_take isl_space *dim); +__isl_give isl_qpolynomial *isl_qpolynomial_neginfty_on_domain(__isl_take isl_space *dim); +__isl_give isl_qpolynomial *isl_qpolynomial_nan_on_domain(__isl_take isl_space *dim); +__isl_give isl_qpolynomial *isl_qpolynomial_val_on_domain( + __isl_take isl_space *space, __isl_take isl_val *val); +__isl_give isl_qpolynomial *isl_qpolynomial_var_on_domain(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos); +__isl_give isl_qpolynomial *isl_qpolynomial_copy(__isl_keep isl_qpolynomial *qp); +__isl_null isl_qpolynomial *isl_qpolynomial_free( + __isl_take isl_qpolynomial *qp); + +isl_bool isl_qpolynomial_plain_is_equal(__isl_keep isl_qpolynomial *qp1, + __isl_keep isl_qpolynomial *qp2); +isl_bool isl_qpolynomial_is_zero(__isl_keep isl_qpolynomial *qp); +isl_bool isl_qpolynomial_is_nan(__isl_keep isl_qpolynomial *qp); +isl_bool isl_qpolynomial_is_infty(__isl_keep isl_qpolynomial *qp); +isl_bool isl_qpolynomial_is_neginfty(__isl_keep isl_qpolynomial *qp); +int isl_qpolynomial_sgn(__isl_keep isl_qpolynomial *qp); + +__isl_give isl_qpolynomial *isl_qpolynomial_neg(__isl_take isl_qpolynomial *qp); +__isl_give isl_qpolynomial *isl_qpolynomial_add(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); +__isl_give isl_qpolynomial *isl_qpolynomial_sub(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); +__isl_give isl_qpolynomial *isl_qpolynomial_mul(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); +__isl_give isl_qpolynomial *isl_qpolynomial_pow(__isl_take isl_qpolynomial *qp, + unsigned power); +__isl_give isl_qpolynomial *isl_qpolynomial_scale_val( + __isl_take isl_qpolynomial *qp, __isl_take isl_val *v); +__isl_give isl_qpolynomial *isl_qpolynomial_scale_down_val( + __isl_take isl_qpolynomial *qp, __isl_take isl_val *v); + +__isl_give isl_qpolynomial *isl_qpolynomial_insert_dims( + __isl_take isl_qpolynomial *qp, enum isl_dim_type type, + unsigned first, unsigned n); +__isl_give isl_qpolynomial *isl_qpolynomial_add_dims( + __isl_take isl_qpolynomial *qp, enum isl_dim_type type, unsigned n); +__isl_give isl_qpolynomial *isl_qpolynomial_move_dims( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_qpolynomial *isl_qpolynomial_project_domain_on_params( + __isl_take isl_qpolynomial *qp); +__isl_give isl_qpolynomial *isl_qpolynomial_drop_dims( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_qpolynomial *isl_qpolynomial_substitute( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n, + __isl_keep isl_qpolynomial **subs); + +int isl_qpolynomial_as_polynomial_on_domain(__isl_keep isl_qpolynomial *qp, + __isl_keep isl_basic_set *bset, + int (*fn)(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, void *user), void *user); + +__isl_give isl_qpolynomial *isl_qpolynomial_homogenize( + __isl_take isl_qpolynomial *poly); + +__isl_give isl_qpolynomial *isl_qpolynomial_align_params( + __isl_take isl_qpolynomial *qp, __isl_take isl_space *model); + +isl_ctx *isl_term_get_ctx(__isl_keep isl_term *term); + +__isl_give isl_term *isl_term_copy(__isl_keep isl_term *term); +void isl_term_free(__isl_take isl_term *term); + +unsigned isl_term_dim(__isl_keep isl_term *term, enum isl_dim_type type); +__isl_give isl_val *isl_term_get_coefficient_val(__isl_keep isl_term *term); +int isl_term_get_exp(__isl_keep isl_term *term, + enum isl_dim_type type, unsigned pos); +__isl_give isl_aff *isl_term_get_div(__isl_keep isl_term *term, unsigned pos); + +isl_stat isl_qpolynomial_foreach_term(__isl_keep isl_qpolynomial *qp, + isl_stat (*fn)(__isl_take isl_term *term, void *user), void *user); + +__isl_give isl_val *isl_qpolynomial_eval(__isl_take isl_qpolynomial *qp, + __isl_take isl_point *pnt); + +__isl_give isl_qpolynomial *isl_qpolynomial_gist_params( + __isl_take isl_qpolynomial *qp, __isl_take isl_set *context); +__isl_give isl_qpolynomial *isl_qpolynomial_gist( + __isl_take isl_qpolynomial *qp, __isl_take isl_set *context); + +__isl_give isl_qpolynomial *isl_qpolynomial_from_constraint( + __isl_take isl_constraint *c, enum isl_dim_type type, unsigned pos); +__isl_give isl_qpolynomial *isl_qpolynomial_from_term(__isl_take isl_term *term); +__isl_give isl_qpolynomial *isl_qpolynomial_from_aff(__isl_take isl_aff *aff); +__isl_give isl_basic_map *isl_basic_map_from_qpolynomial( + __isl_take isl_qpolynomial *qp); + +__isl_give isl_printer *isl_printer_print_qpolynomial( + __isl_take isl_printer *p, __isl_keep isl_qpolynomial *qp); +void isl_qpolynomial_print(__isl_keep isl_qpolynomial *qp, FILE *out, + unsigned output_format); +void isl_qpolynomial_dump(__isl_keep isl_qpolynomial *qp); + +isl_ctx *isl_pw_qpolynomial_get_ctx(__isl_keep isl_pw_qpolynomial *pwqp); + +isl_bool isl_pw_qpolynomial_plain_is_equal(__isl_keep isl_pw_qpolynomial *pwqp1, + __isl_keep isl_pw_qpolynomial *pwqp2); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_zero(__isl_take isl_space *dim); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_alloc(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_qpolynomial( + __isl_take isl_qpolynomial *qp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_copy( + __isl_keep isl_pw_qpolynomial *pwqp); +__isl_null isl_pw_qpolynomial *isl_pw_qpolynomial_free( + __isl_take isl_pw_qpolynomial *pwqp); + +isl_bool isl_pw_qpolynomial_is_zero(__isl_keep isl_pw_qpolynomial *pwqp); + +__isl_give isl_space *isl_pw_qpolynomial_get_domain_space( + __isl_keep isl_pw_qpolynomial *pwqp); +__isl_give isl_space *isl_pw_qpolynomial_get_space( + __isl_keep isl_pw_qpolynomial *pwqp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_reset_domain_space( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_space *dim); +unsigned isl_pw_qpolynomial_dim(__isl_keep isl_pw_qpolynomial *pwqp, + enum isl_dim_type type); +isl_bool isl_pw_qpolynomial_involves_dims(__isl_keep isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned first, unsigned n); +int isl_pw_qpolynomial_has_equal_space(__isl_keep isl_pw_qpolynomial *pwqp1, + __isl_keep isl_pw_qpolynomial *pwqp2); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_set_dim_name( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned pos, const char *s); + +int isl_pw_qpolynomial_find_dim_by_name(__isl_keep isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, const char *name); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_reset_user( + __isl_take isl_pw_qpolynomial *pwqp); + +__isl_give isl_set *isl_pw_qpolynomial_domain(__isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_intersect_domain( + __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_intersect_params( + __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_subtract_domain( + __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_project_domain_on_params( + __isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_drop_dims( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_split_dims( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_sub( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_disjoint( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_neg( + __isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_scale_val( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_val *v); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_scale_down_val( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_val *v); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_pow( + __isl_take isl_pw_qpolynomial *pwqp, unsigned exponent); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_insert_dims( + __isl_take isl_pw_qpolynomial *pwqp, enum isl_dim_type type, + unsigned first, unsigned n); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_dims( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned n); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_move_dims( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_fix_val( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned n, __isl_take isl_val *v); + +__isl_give isl_val *isl_pw_qpolynomial_eval( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_point *pnt); + +__isl_give isl_val *isl_pw_qpolynomial_max(__isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_val *isl_pw_qpolynomial_min(__isl_take isl_pw_qpolynomial *pwqp); + +isl_stat isl_pw_qpolynomial_foreach_piece(__isl_keep isl_pw_qpolynomial *pwqp, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_qpolynomial *qp, + void *user), void *user); +isl_stat isl_pw_qpolynomial_foreach_lifted_piece( + __isl_keep isl_pw_qpolynomial *pwqp, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_qpolynomial *qp, + void *user), void *user); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_pw_aff( + __isl_take isl_pw_aff *pwaff); + +__isl_constructor +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_file(isl_ctx *ctx, + FILE *input); +__isl_give isl_printer *isl_printer_print_pw_qpolynomial( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp); +void isl_pw_qpolynomial_print(__isl_keep isl_pw_qpolynomial *pwqp, FILE *out, + unsigned output_format); +void isl_pw_qpolynomial_dump(__isl_keep isl_pw_qpolynomial *pwqp); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_coalesce( + __isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_gist( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_set *context); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_gist_params( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_set *context); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_split_periods( + __isl_take isl_pw_qpolynomial *pwqp, int max_periods); + +__isl_give isl_pw_qpolynomial *isl_basic_set_multiplicative_call( + __isl_take isl_basic_set *bset, + __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset)); + +isl_ctx *isl_qpolynomial_fold_get_ctx(__isl_keep isl_qpolynomial_fold *fold); +enum isl_fold isl_qpolynomial_fold_get_type(__isl_keep isl_qpolynomial_fold *fold); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_empty(enum isl_fold type, + __isl_take isl_space *dim); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_alloc( + enum isl_fold type, __isl_take isl_qpolynomial *qp); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_copy( + __isl_keep isl_qpolynomial_fold *fold); +void isl_qpolynomial_fold_free(__isl_take isl_qpolynomial_fold *fold); + +int isl_qpolynomial_fold_is_empty(__isl_keep isl_qpolynomial_fold *fold); +isl_bool isl_qpolynomial_fold_is_nan(__isl_keep isl_qpolynomial_fold *fold); +int isl_qpolynomial_fold_plain_is_equal(__isl_keep isl_qpolynomial_fold *fold1, + __isl_keep isl_qpolynomial_fold *fold2); + +__isl_give isl_space *isl_qpolynomial_fold_get_domain_space( + __isl_keep isl_qpolynomial_fold *fold); +__isl_give isl_space *isl_qpolynomial_fold_get_space( + __isl_keep isl_qpolynomial_fold *fold); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold( + __isl_take isl_qpolynomial_fold *fold1, + __isl_take isl_qpolynomial_fold *fold2); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_val( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_down_val( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_move_dims( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type type, unsigned first, unsigned n, + __isl_keep isl_qpolynomial **subs); + +__isl_give isl_val *isl_qpolynomial_fold_eval( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_point *pnt); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist_params( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context); + +isl_stat isl_qpolynomial_fold_foreach_qpolynomial( + __isl_keep isl_qpolynomial_fold *fold, + isl_stat (*fn)(__isl_take isl_qpolynomial *qp, void *user), void *user); + +__isl_give isl_printer *isl_printer_print_qpolynomial_fold( + __isl_take isl_printer *p, __isl_keep isl_qpolynomial_fold *fold); +void isl_qpolynomial_fold_print(__isl_keep isl_qpolynomial_fold *fold, FILE *out, + unsigned output_format); +void isl_qpolynomial_fold_dump(__isl_keep isl_qpolynomial_fold *fold); + +isl_ctx *isl_pw_qpolynomial_fold_get_ctx(__isl_keep isl_pw_qpolynomial_fold *pwf); + +isl_bool isl_pw_qpolynomial_fold_plain_is_equal( + __isl_keep isl_pw_qpolynomial_fold *pwf1, + __isl_keep isl_pw_qpolynomial_fold *pwf2); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_from_pw_qpolynomial( + enum isl_fold type, __isl_take isl_pw_qpolynomial *pwqp); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_alloc( + enum isl_fold type, + __isl_take isl_set *set, __isl_take isl_qpolynomial_fold *fold); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_copy( + __isl_keep isl_pw_qpolynomial_fold *pwf); +__isl_null isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_free( + __isl_take isl_pw_qpolynomial_fold *pwf); + +isl_bool isl_pw_qpolynomial_fold_is_zero( + __isl_keep isl_pw_qpolynomial_fold *pwf); + +__isl_give isl_space *isl_pw_qpolynomial_fold_get_domain_space( + __isl_keep isl_pw_qpolynomial_fold *pwf); +__isl_give isl_space *isl_pw_qpolynomial_fold_get_space( + __isl_keep isl_pw_qpolynomial_fold *pwf); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_reset_space( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_space *dim); +unsigned isl_pw_qpolynomial_fold_dim(__isl_keep isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type); +int isl_pw_qpolynomial_fold_has_equal_space( + __isl_keep isl_pw_qpolynomial_fold *pwf1, + __isl_keep isl_pw_qpolynomial_fold *pwf2); + +size_t isl_pw_qpolynomial_fold_size(__isl_keep isl_pw_qpolynomial_fold *pwf); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_zero( + __isl_take isl_space *dim, enum isl_fold type); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_set_dim_name( + __isl_take isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type, unsigned pos, const char *s); + +int isl_pw_qpolynomial_fold_find_dim_by_name( + __isl_keep isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type, const char *name); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_reset_user( + __isl_take isl_pw_qpolynomial_fold *pwf); + +__isl_give isl_set *isl_pw_qpolynomial_fold_domain( + __isl_take isl_pw_qpolynomial_fold *pwf); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_intersect_domain( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_intersect_params( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_subtract_domain( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add( + __isl_take isl_pw_qpolynomial_fold *pwf1, + __isl_take isl_pw_qpolynomial_fold *pwf2); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fold( + __isl_take isl_pw_qpolynomial_fold *pwf1, + __isl_take isl_pw_qpolynomial_fold *pwf2); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add_disjoint( + __isl_take isl_pw_qpolynomial_fold *pwf1, + __isl_take isl_pw_qpolynomial_fold *pwf2); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_scale_val( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_val *v); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_scale_down_val( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_val *v); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_project_domain_on_params( + __isl_take isl_pw_qpolynomial_fold *pwf); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_drop_dims( + __isl_take isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_move_dims( + __isl_take isl_pw_qpolynomial_fold *pwf, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); + +__isl_give isl_val *isl_pw_qpolynomial_fold_eval( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_point *pnt); + +isl_stat isl_pw_qpolynomial_fold_foreach_piece( + __isl_keep isl_pw_qpolynomial_fold *pwf, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_qpolynomial_fold *fold, void *user), void *user); +isl_stat isl_pw_qpolynomial_fold_foreach_lifted_piece( + __isl_keep isl_pw_qpolynomial_fold *pwf, + isl_stat (*fn)(__isl_take isl_set *set, + __isl_take isl_qpolynomial_fold *fold, void *user), void *user); + +__isl_give isl_printer *isl_printer_print_pw_qpolynomial_fold( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf); +void isl_pw_qpolynomial_fold_print(__isl_keep isl_pw_qpolynomial_fold *pwf, + FILE *out, unsigned output_format); +void isl_pw_qpolynomial_fold_dump(__isl_keep isl_pw_qpolynomial_fold *pwf); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_coalesce( + __isl_take isl_pw_qpolynomial_fold *pwf); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_gist( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *context); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_gist_params( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *context); + +__isl_give isl_val *isl_pw_qpolynomial_fold_max( + __isl_take isl_pw_qpolynomial_fold *pwf); +__isl_give isl_val *isl_pw_qpolynomial_fold_min( + __isl_take isl_pw_qpolynomial_fold *pwf); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_bound( + __isl_take isl_pw_qpolynomial *pwqp, enum isl_fold type, int *tight); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_bound( + __isl_take isl_pw_qpolynomial_fold *pwf, int *tight); +__isl_give isl_pw_qpolynomial_fold *isl_set_apply_pw_qpolynomial_fold( + __isl_take isl_set *set, __isl_take isl_pw_qpolynomial_fold *pwf, + int *tight); +__isl_give isl_pw_qpolynomial_fold *isl_map_apply_pw_qpolynomial_fold( + __isl_take isl_map *map, __isl_take isl_pw_qpolynomial_fold *pwf, + int *tight); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_to_polynomial( + __isl_take isl_pw_qpolynomial *pwqp, int sign); + +isl_ctx *isl_union_pw_qpolynomial_get_ctx( + __isl_keep isl_union_pw_qpolynomial *upwqp); + +unsigned isl_union_pw_qpolynomial_dim( + __isl_keep isl_union_pw_qpolynomial *upwqp, enum isl_dim_type type); + +isl_bool isl_union_pw_qpolynomial_plain_is_equal( + __isl_keep isl_union_pw_qpolynomial *upwqp1, + __isl_keep isl_union_pw_qpolynomial *upwqp2); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_from_pw_qpolynomial(__isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_zero( + __isl_take isl_space *dim); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_add_pw_qpolynomial( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_pw_qpolynomial *pwqp); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_copy( + __isl_keep isl_union_pw_qpolynomial *upwqp); +__isl_null isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_free( + __isl_take isl_union_pw_qpolynomial *upwqp); + +__isl_constructor +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_read_from_str( + isl_ctx *ctx, const char *str); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_neg( + __isl_take isl_union_pw_qpolynomial *upwqp); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_add( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_sub( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_scale_val( + __isl_take isl_union_pw_qpolynomial *upwqp, __isl_take isl_val *v); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_scale_down_val( + __isl_take isl_union_pw_qpolynomial *upwqp, __isl_take isl_val *v); + +__isl_give isl_union_set *isl_union_pw_qpolynomial_domain( + __isl_take isl_union_pw_qpolynomial *upwqp); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_intersect_domain( + __isl_take isl_union_pw_qpolynomial *upwpq, + __isl_take isl_union_set *uset); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_intersect_params( + __isl_take isl_union_pw_qpolynomial *upwpq, + __isl_take isl_set *set); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_subtract_domain( + __isl_take isl_union_pw_qpolynomial *upwpq, + __isl_take isl_union_set *uset); + +__isl_give isl_space *isl_union_pw_qpolynomial_get_space( + __isl_keep isl_union_pw_qpolynomial *upwqp); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_set_dim_name( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type, unsigned pos, const char *s); + +int isl_union_pw_qpolynomial_find_dim_by_name( + __isl_keep isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type, const char *name); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_drop_dims( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_reset_user( + __isl_take isl_union_pw_qpolynomial *upwqp); + +__isl_give isl_val *isl_union_pw_qpolynomial_eval( + __isl_take isl_union_pw_qpolynomial *upwqp, __isl_take isl_point *pnt); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_coalesce( + __isl_take isl_union_pw_qpolynomial *upwqp); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_gist( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_union_set *context); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_gist_params( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_set *context); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_align_params( + __isl_take isl_union_pw_qpolynomial *upwqp, + __isl_take isl_space *model); + +int isl_union_pw_qpolynomial_n_pw_qpolynomial( + __isl_keep isl_union_pw_qpolynomial *upwqp); +isl_stat isl_union_pw_qpolynomial_foreach_pw_qpolynomial( + __isl_keep isl_union_pw_qpolynomial *upwqp, + isl_stat (*fn)(__isl_take isl_pw_qpolynomial *pwqp, void *user), + void *user); +__isl_give isl_pw_qpolynomial *isl_union_pw_qpolynomial_extract_pw_qpolynomial( + __isl_keep isl_union_pw_qpolynomial *upwqp, __isl_take isl_space *dim); + +__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial( + __isl_take isl_printer *p, __isl_keep isl_union_pw_qpolynomial *upwqp); + +isl_ctx *isl_union_pw_qpolynomial_fold_get_ctx( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + +unsigned isl_union_pw_qpolynomial_fold_dim( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, enum isl_dim_type type); + +isl_bool isl_union_pw_qpolynomial_fold_plain_is_equal( + __isl_keep isl_union_pw_qpolynomial_fold *upwf1, + __isl_keep isl_union_pw_qpolynomial_fold *upwf2); + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_from_pw_qpolynomial_fold(__isl_take isl_pw_qpolynomial_fold *pwf); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_zero( + __isl_take isl_space *dim, enum isl_fold type); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold( + __isl_take isl_union_pw_qpolynomial_fold *upwqp, + __isl_take isl_pw_qpolynomial_fold *pwqp); +__isl_null isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_free( + __isl_take isl_union_pw_qpolynomial_fold *upwf); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_copy( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold( + __isl_take isl_union_pw_qpolynomial_fold *upwf1, + __isl_take isl_union_pw_qpolynomial_fold *upwf2); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_add_union_pw_qpolynomial( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_pw_qpolynomial *upwqp); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_scale_val( + __isl_take isl_union_pw_qpolynomial_fold *upwf, __isl_take isl_val *v); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_scale_down_val( + __isl_take isl_union_pw_qpolynomial_fold *upwf, __isl_take isl_val *v); + +__isl_give isl_union_set *isl_union_pw_qpolynomial_fold_domain( + __isl_take isl_union_pw_qpolynomial_fold *upwf); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_intersect_domain( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_set *uset); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_intersect_params( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_set *set); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_subtract_domain( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_set *uset); + +enum isl_fold isl_union_pw_qpolynomial_fold_get_type( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); +__isl_give isl_space *isl_union_pw_qpolynomial_fold_get_space( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_set_dim_name( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type, unsigned pos, const char *s); + +int isl_union_pw_qpolynomial_fold_find_dim_by_name( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type, const char *name); + +__isl_give isl_union_pw_qpolynomial_fold * + isl_union_pw_qpolynomial_fold_drop_dims( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_reset_user( + __isl_take isl_union_pw_qpolynomial_fold *upwf); + +__isl_give isl_val *isl_union_pw_qpolynomial_fold_eval( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_point *pnt); + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_coalesce( + __isl_take isl_union_pw_qpolynomial_fold *upwf); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_gist( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_set *context); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_gist_params( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_set *context); + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_align_params( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_space *model); + +int isl_union_pw_qpolynomial_fold_n_pw_qpolynomial_fold( + __isl_keep isl_union_pw_qpolynomial_fold *upwf); +isl_stat isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, + isl_stat (*fn)(__isl_take isl_pw_qpolynomial_fold *pwf, + void *user), void *user); +__isl_give isl_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_extract_pw_qpolynomial_fold( + __isl_keep isl_union_pw_qpolynomial_fold *upwf, __isl_take isl_space *dim); + +__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial_fold( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_qpolynomial_fold *upwf); + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_bound( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_fold type, int *tight); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_set_apply_union_pw_qpolynomial_fold( + __isl_take isl_union_set *uset, + __isl_take isl_union_pw_qpolynomial_fold *upwf, int *tight); +__isl_give isl_union_pw_qpolynomial_fold *isl_union_map_apply_union_pw_qpolynomial_fold( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_qpolynomial_fold *upwf, int *tight); + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_to_polynomial( + __isl_take isl_union_pw_qpolynomial *upwqp, int sign); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/polynomial_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/polynomial_type.h @@ -0,0 +1,31 @@ +#ifndef ISL_POLYNOMIAL_TYPE_H +#define ISL_POLYNOMIAL_TYPE_H + +struct isl_qpolynomial; +typedef struct isl_qpolynomial isl_qpolynomial; + +struct isl_term; +typedef struct isl_term isl_term; + +struct __isl_export isl_pw_qpolynomial; +typedef struct isl_pw_qpolynomial isl_pw_qpolynomial; + +enum isl_fold { + isl_fold_min, + isl_fold_max, + isl_fold_list +}; + +struct isl_qpolynomial_fold; +typedef struct isl_qpolynomial_fold isl_qpolynomial_fold; + +struct isl_pw_qpolynomial_fold; +typedef struct isl_pw_qpolynomial_fold isl_pw_qpolynomial_fold; + +struct __isl_export isl_union_pw_qpolynomial; +typedef struct isl_union_pw_qpolynomial isl_union_pw_qpolynomial; + +struct isl_union_pw_qpolynomial_fold; +typedef struct isl_union_pw_qpolynomial_fold isl_union_pw_qpolynomial_fold; + +#endif Index: lib/Analysis/isl/include/isl/printer.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/printer.h @@ -0,0 +1,84 @@ +#ifndef ISL_PRINTER_H +#define ISL_PRINTER_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_printer *isl_printer_to_file(isl_ctx *ctx, FILE *file); +__isl_give isl_printer *isl_printer_to_str(isl_ctx *ctx); +__isl_null isl_printer *isl_printer_free(__isl_take isl_printer *printer); + +isl_ctx *isl_printer_get_ctx(__isl_keep isl_printer *printer); +FILE *isl_printer_get_file(__isl_keep isl_printer *printer); + +__isl_give char *isl_printer_get_str(__isl_keep isl_printer *printer); + +__isl_give isl_printer *isl_printer_set_indent(__isl_take isl_printer *p, + int indent); +__isl_give isl_printer *isl_printer_indent(__isl_take isl_printer *p, + int indent); + +#define ISL_FORMAT_ISL 0 +#define ISL_FORMAT_POLYLIB 1 +#define ISL_FORMAT_POLYLIB_CONSTRAINTS 2 +#define ISL_FORMAT_OMEGA 3 +#define ISL_FORMAT_C 4 +#define ISL_FORMAT_LATEX 5 +#define ISL_FORMAT_EXT_POLYLIB 6 +__isl_give isl_printer *isl_printer_set_output_format(__isl_take isl_printer *p, + int output_format); +int isl_printer_get_output_format(__isl_keep isl_printer *p); + +#define ISL_YAML_STYLE_BLOCK 0 +#define ISL_YAML_STYLE_FLOW 1 +__isl_give isl_printer *isl_printer_set_yaml_style(__isl_take isl_printer *p, + int yaml_style); +int isl_printer_get_yaml_style(__isl_keep isl_printer *p); + +__isl_give isl_printer *isl_printer_set_indent_prefix(__isl_take isl_printer *p, + const char *prefix); +__isl_give isl_printer *isl_printer_set_prefix(__isl_take isl_printer *p, + const char *prefix); +__isl_give isl_printer *isl_printer_set_suffix(__isl_take isl_printer *p, + const char *suffix); +__isl_give isl_printer *isl_printer_set_isl_int_width(__isl_take isl_printer *p, + int width); + +isl_bool isl_printer_has_note(__isl_keep isl_printer *p, + __isl_keep isl_id *id); +__isl_give isl_id *isl_printer_get_note(__isl_keep isl_printer *p, + __isl_take isl_id *id); +__isl_give isl_printer *isl_printer_set_note(__isl_take isl_printer *p, + __isl_take isl_id *id, __isl_take isl_id *note); + +__isl_give isl_printer *isl_printer_start_line(__isl_take isl_printer *p); +__isl_give isl_printer *isl_printer_end_line(__isl_take isl_printer *p); +__isl_give isl_printer *isl_printer_print_double(__isl_take isl_printer *p, + double d); +__isl_give isl_printer *isl_printer_print_int(__isl_take isl_printer *p, int i); +__isl_give isl_printer *isl_printer_print_str(__isl_take isl_printer *p, + const char *s); + +__isl_give isl_printer *isl_printer_yaml_start_mapping( + __isl_take isl_printer *p); +__isl_give isl_printer *isl_printer_yaml_end_mapping( + __isl_take isl_printer *p); +__isl_give isl_printer *isl_printer_yaml_start_sequence( + __isl_take isl_printer *p); +__isl_give isl_printer *isl_printer_yaml_end_sequence( + __isl_take isl_printer *p); +__isl_give isl_printer *isl_printer_yaml_next(__isl_take isl_printer *p); + +__isl_give isl_printer *isl_printer_flush(__isl_take isl_printer *p); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/printer_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/printer_type.h @@ -0,0 +1,15 @@ +#ifndef ISL_PRINTER_TYPE_H +#define ISL_PRINTER_TYPE_H + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_printer; +typedef struct isl_printer isl_printer; + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/schedule.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/schedule.h @@ -0,0 +1,177 @@ +#ifndef ISL_SCHEDULE_H +#define ISL_SCHEDULE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_schedule_constraints; +typedef struct isl_schedule_constraints isl_schedule_constraints; + +isl_stat isl_options_set_schedule_max_coefficient(isl_ctx *ctx, int val); +int isl_options_get_schedule_max_coefficient(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_max_constant_term(isl_ctx *ctx, int val); +int isl_options_get_schedule_max_constant_term(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_maximize_band_depth(isl_ctx *ctx, int val); +int isl_options_get_schedule_maximize_band_depth(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_maximize_coincidence(isl_ctx *ctx, int val); +int isl_options_get_schedule_maximize_coincidence(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_outer_coincidence(isl_ctx *ctx, int val); +int isl_options_get_schedule_outer_coincidence(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_split_scaled(isl_ctx *ctx, int val); +int isl_options_get_schedule_split_scaled(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_treat_coalescing(isl_ctx *ctx, int val); +int isl_options_get_schedule_treat_coalescing(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_separate_components(isl_ctx *ctx, int val); +int isl_options_get_schedule_separate_components(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_serialize_sccs(isl_ctx *ctx, int val); +int isl_options_get_schedule_serialize_sccs(isl_ctx *ctx); + +isl_stat isl_options_set_schedule_whole_component(isl_ctx *ctx, int val); +int isl_options_get_schedule_whole_component(isl_ctx *ctx); + +__isl_give isl_schedule_constraints *isl_schedule_constraints_copy( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_schedule_constraints *isl_schedule_constraints_on_domain( + __isl_take isl_union_set *domain); +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_context( + __isl_take isl_schedule_constraints *sc, __isl_take isl_set *context); +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_validity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *validity); +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_coincidence( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *coincidence); +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_proximity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *proximity); +__isl_give isl_schedule_constraints * +isl_schedule_constraints_set_conditional_validity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *condition, + __isl_take isl_union_map *validity); +__isl_null isl_schedule_constraints *isl_schedule_constraints_free( + __isl_take isl_schedule_constraints *sc); + +isl_ctx *isl_schedule_constraints_get_ctx( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_union_set *isl_schedule_constraints_get_domain( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_union_map *isl_schedule_constraints_get_validity( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_union_map *isl_schedule_constraints_get_coincidence( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_union_map *isl_schedule_constraints_get_proximity( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_union_map *isl_schedule_constraints_get_conditional_validity( + __isl_keep isl_schedule_constraints *sc); +__isl_give isl_union_map * +isl_schedule_constraints_get_conditional_validity_condition( + __isl_keep isl_schedule_constraints *sc); + +__isl_give isl_schedule_constraints *isl_schedule_constraints_apply( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *umap); + +void isl_schedule_constraints_dump(__isl_keep isl_schedule_constraints *sc); + +__isl_give isl_schedule *isl_schedule_constraints_compute_schedule( + __isl_take isl_schedule_constraints *sc); + +__isl_give isl_schedule *isl_union_set_compute_schedule( + __isl_take isl_union_set *domain, + __isl_take isl_union_map *validity, + __isl_take isl_union_map *proximity); + +__isl_give isl_schedule *isl_schedule_empty(__isl_take isl_space *space); +__isl_give isl_schedule *isl_schedule_from_domain( + __isl_take isl_union_set *domain); +__isl_give isl_schedule *isl_schedule_copy(__isl_keep isl_schedule *sched); +__isl_null isl_schedule *isl_schedule_free(__isl_take isl_schedule *sched); +__isl_export +__isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched); + +isl_ctx *isl_schedule_get_ctx(__isl_keep isl_schedule *sched); +isl_bool isl_schedule_plain_is_equal(__isl_keep isl_schedule *schedule1, + __isl_keep isl_schedule *schedule2); + +__isl_export +__isl_give isl_schedule_node *isl_schedule_get_root( + __isl_keep isl_schedule *schedule); +__isl_give isl_union_set *isl_schedule_get_domain( + __isl_keep isl_schedule *schedule); + +isl_stat isl_schedule_foreach_schedule_node_top_down( + __isl_keep isl_schedule *sched, + isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user), + void *user); +__isl_give isl_schedule *isl_schedule_map_schedule_node_bottom_up( + __isl_take isl_schedule *schedule, + __isl_give isl_schedule_node *(*fn)( + __isl_take isl_schedule_node *node, void *user), void *user); + +__isl_give isl_schedule *isl_schedule_insert_context( + __isl_take isl_schedule *schedule, __isl_take isl_set *context); +__isl_give isl_schedule *isl_schedule_insert_partial_schedule( + __isl_take isl_schedule *schedule, + __isl_take isl_multi_union_pw_aff *partial); +__isl_give isl_schedule *isl_schedule_insert_guard( + __isl_take isl_schedule *schedule, __isl_take isl_set *guard); +__isl_give isl_schedule *isl_schedule_sequence( + __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2); +__isl_give isl_schedule *isl_schedule_set( + __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2); +__isl_give isl_schedule *isl_schedule_intersect_domain( + __isl_take isl_schedule *schedule, __isl_take isl_union_set *domain); +__isl_give isl_schedule *isl_schedule_gist_domain_params( + __isl_take isl_schedule *schedule, __isl_take isl_set *context); + +__isl_give isl_schedule *isl_schedule_reset_user( + __isl_take isl_schedule *schedule); +__isl_give isl_schedule *isl_schedule_align_params( + __isl_take isl_schedule *schedule, __isl_take isl_space *space); +__isl_overload +__isl_give isl_schedule *isl_schedule_pullback_union_pw_multi_aff( + __isl_take isl_schedule *schedule, + __isl_take isl_union_pw_multi_aff *upma); +__isl_give isl_schedule *isl_schedule_expand(__isl_take isl_schedule *schedule, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_schedule *expansion); + +__isl_give isl_band_list *isl_schedule_get_band_forest( + __isl_keep isl_schedule *schedule); + +__isl_give isl_schedule *isl_schedule_read_from_file(isl_ctx *ctx, FILE *input); +__isl_constructor +__isl_give isl_schedule *isl_schedule_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give isl_printer *isl_printer_print_schedule(__isl_take isl_printer *p, + __isl_keep isl_schedule *schedule); +void isl_schedule_dump(__isl_keep isl_schedule *schedule); +__isl_give char *isl_schedule_to_str(__isl_keep isl_schedule *schedule); + +int isl_schedule_foreach_band(__isl_keep isl_schedule *sched, + int (*fn)(__isl_keep isl_band *band, void *user), void *user); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/schedule_node.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/schedule_node.h @@ -0,0 +1,239 @@ +#ifndef ISL_SCHEDULE_NODE_H +#define ISL_SCHEDULE_NODE_H + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_schedule_node *isl_schedule_node_from_domain( + __isl_take isl_union_set *domain); +__isl_give isl_schedule_node *isl_schedule_node_from_extension( + __isl_take isl_union_map *extension); +__isl_give isl_schedule_node *isl_schedule_node_copy( + __isl_keep isl_schedule_node *node); +__isl_null isl_schedule_node *isl_schedule_node_free( + __isl_take isl_schedule_node *node); + +isl_bool isl_schedule_node_is_equal(__isl_keep isl_schedule_node *node1, + __isl_keep isl_schedule_node *node2); + +isl_ctx *isl_schedule_node_get_ctx(__isl_keep isl_schedule_node *node); +enum isl_schedule_node_type isl_schedule_node_get_type( + __isl_keep isl_schedule_node *node); +enum isl_schedule_node_type isl_schedule_node_get_parent_type( + __isl_keep isl_schedule_node *node); +__isl_export +__isl_give isl_schedule *isl_schedule_node_get_schedule( + __isl_keep isl_schedule_node *node); + +isl_stat isl_schedule_node_foreach_descendant_top_down( + __isl_keep isl_schedule_node *node, + isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user), + void *user); +isl_stat isl_schedule_node_foreach_ancestor_top_down( + __isl_keep isl_schedule_node *node, + isl_stat (*fn)(__isl_keep isl_schedule_node *node, void *user), + void *user); +__isl_give isl_schedule_node *isl_schedule_node_map_descendant_bottom_up( + __isl_take isl_schedule_node *node, + __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node, + void *user), void *user); + +int isl_schedule_node_get_tree_depth(__isl_keep isl_schedule_node *node); +isl_bool isl_schedule_node_has_parent(__isl_keep isl_schedule_node *node); +isl_bool isl_schedule_node_has_children(__isl_keep isl_schedule_node *node); +isl_bool isl_schedule_node_has_previous_sibling( + __isl_keep isl_schedule_node *node); +isl_bool isl_schedule_node_has_next_sibling(__isl_keep isl_schedule_node *node); +int isl_schedule_node_n_children(__isl_keep isl_schedule_node *node); +int isl_schedule_node_get_child_position(__isl_keep isl_schedule_node *node); +int isl_schedule_node_get_ancestor_child_position( + __isl_keep isl_schedule_node *node, + __isl_keep isl_schedule_node *ancestor); +__isl_give isl_schedule_node *isl_schedule_node_get_child( + __isl_keep isl_schedule_node *node, int pos); +__isl_give isl_schedule_node *isl_schedule_node_get_shared_ancestor( + __isl_keep isl_schedule_node *node1, + __isl_keep isl_schedule_node *node2); + +__isl_give isl_schedule_node *isl_schedule_node_root( + __isl_take isl_schedule_node *node); +__isl_export +__isl_give isl_schedule_node *isl_schedule_node_parent( + __isl_take isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_ancestor( + __isl_take isl_schedule_node *node, int generation); +__isl_export +__isl_give isl_schedule_node *isl_schedule_node_child( + __isl_take isl_schedule_node *node, int pos); +__isl_give isl_schedule_node *isl_schedule_node_first_child( + __isl_take isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_previous_sibling( + __isl_take isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_next_sibling( + __isl_take isl_schedule_node *node); + +isl_bool isl_schedule_node_is_subtree_anchored( + __isl_keep isl_schedule_node *node); + +__isl_give isl_schedule_node *isl_schedule_node_group( + __isl_take isl_schedule_node *node, __isl_take isl_id *group_id); + +__isl_give isl_schedule_node *isl_schedule_node_sequence_splice_child( + __isl_take isl_schedule_node *node, int pos); + +__isl_give isl_space *isl_schedule_node_band_get_space( + __isl_keep isl_schedule_node *node); +__isl_give isl_multi_union_pw_aff *isl_schedule_node_band_get_partial_schedule( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_map *isl_schedule_node_band_get_partial_schedule_union_map( + __isl_keep isl_schedule_node *node); +enum isl_ast_loop_type isl_schedule_node_band_member_get_ast_loop_type( + __isl_keep isl_schedule_node *node, int pos); +__isl_give isl_schedule_node *isl_schedule_node_band_member_set_ast_loop_type( + __isl_take isl_schedule_node *node, int pos, + enum isl_ast_loop_type type); +enum isl_ast_loop_type isl_schedule_node_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_node *node, int pos); +__isl_give isl_schedule_node * +isl_schedule_node_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_node *node, int pos, + enum isl_ast_loop_type type); +__isl_give isl_union_set *isl_schedule_node_band_get_ast_build_options( + __isl_keep isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_band_set_ast_build_options( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *options); +__isl_give isl_set *isl_schedule_node_band_get_ast_isolate_option( + __isl_keep isl_schedule_node *node); +unsigned isl_schedule_node_band_n_member(__isl_keep isl_schedule_node *node); +__isl_export +isl_bool isl_schedule_node_band_member_get_coincident( + __isl_keep isl_schedule_node *node, int pos); +__isl_export +__isl_give isl_schedule_node *isl_schedule_node_band_member_set_coincident( + __isl_take isl_schedule_node *node, int pos, int coincident); +isl_bool isl_schedule_node_band_get_permutable( + __isl_keep isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_band_set_permutable( + __isl_take isl_schedule_node *node, int permutable); + +isl_stat isl_options_set_tile_scale_tile_loops(isl_ctx *ctx, int val); +int isl_options_get_tile_scale_tile_loops(isl_ctx *ctx); +isl_stat isl_options_set_tile_shift_point_loops(isl_ctx *ctx, int val); +int isl_options_get_tile_shift_point_loops(isl_ctx *ctx); + +__isl_give isl_schedule_node *isl_schedule_node_band_scale( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_node *isl_schedule_node_band_scale_down( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_node *isl_schedule_node_band_mod( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_node *isl_schedule_node_band_shift( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_union_pw_aff *shift); +__isl_give isl_schedule_node *isl_schedule_node_band_tile( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes); +__isl_give isl_schedule_node *isl_schedule_node_band_sink( + __isl_take isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_band_split( + __isl_take isl_schedule_node *node, int pos); + +__isl_give isl_set *isl_schedule_node_context_get_context( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_set *isl_schedule_node_domain_get_domain( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_map *isl_schedule_node_expansion_get_expansion( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_map *isl_schedule_node_extension_get_extension( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_set *isl_schedule_node_filter_get_filter( + __isl_keep isl_schedule_node *node); +__isl_give isl_set *isl_schedule_node_guard_get_guard( + __isl_keep isl_schedule_node *node); +__isl_give isl_id *isl_schedule_node_mark_get_id( + __isl_keep isl_schedule_node *node); + +int isl_schedule_node_get_schedule_depth(__isl_keep isl_schedule_node *node); +__isl_give isl_union_set *isl_schedule_node_get_domain( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_set *isl_schedule_node_get_universe_domain( + __isl_keep isl_schedule_node *node); +__isl_export +__isl_give isl_multi_union_pw_aff * +isl_schedule_node_get_prefix_schedule_multi_union_pw_aff( + __isl_keep isl_schedule_node *node); +__isl_export +__isl_give isl_union_pw_multi_aff * +isl_schedule_node_get_prefix_schedule_union_pw_multi_aff( + __isl_keep isl_schedule_node *node); +__isl_export +__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_union_map( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_map *isl_schedule_node_get_subtree_expansion( + __isl_keep isl_schedule_node *node); +__isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction( + __isl_keep isl_schedule_node *node); + +__isl_give isl_schedule_node *isl_schedule_node_insert_context( + __isl_take isl_schedule_node *node, __isl_take isl_set *context); +__isl_give isl_schedule_node *isl_schedule_node_insert_partial_schedule( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_union_pw_aff *schedule); +__isl_give isl_schedule_node *isl_schedule_node_insert_filter( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter); +__isl_give isl_schedule_node *isl_schedule_node_insert_guard( + __isl_take isl_schedule_node *node, __isl_take isl_set *context); +__isl_give isl_schedule_node *isl_schedule_node_insert_mark( + __isl_take isl_schedule_node *node, __isl_take isl_id *mark); +__isl_give isl_schedule_node *isl_schedule_node_insert_sequence( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set_list *filters); +__isl_give isl_schedule_node *isl_schedule_node_insert_set( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set_list *filters); + +__isl_give isl_schedule_node *isl_schedule_node_cut( + __isl_take isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_delete( + __isl_take isl_schedule_node *node); + +__isl_give isl_schedule_node *isl_schedule_node_order_before( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter); +__isl_give isl_schedule_node *isl_schedule_node_order_after( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter); + +__isl_give isl_schedule_node *isl_schedule_node_graft_before( + __isl_take isl_schedule_node *node, + __isl_take isl_schedule_node *graft); +__isl_give isl_schedule_node *isl_schedule_node_graft_after( + __isl_take isl_schedule_node *node, + __isl_take isl_schedule_node *graft); + +__isl_give isl_schedule_node *isl_schedule_node_reset_user( + __isl_take isl_schedule_node *node); +__isl_give isl_schedule_node *isl_schedule_node_align_params( + __isl_take isl_schedule_node *node, __isl_take isl_space *space); + +__isl_give isl_printer *isl_printer_print_schedule_node( + __isl_take isl_printer *p, __isl_keep isl_schedule_node *node); +void isl_schedule_node_dump(__isl_keep isl_schedule_node *node); +__isl_give char *isl_schedule_node_to_str(__isl_keep isl_schedule_node *node); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/schedule_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/schedule_type.h @@ -0,0 +1,33 @@ +#ifndef ISL_SCHEDULE_TYPE_H +#define ISL_SCHEDULE_TYPE_H + +#if defined(__cplusplus) +extern "C" { +#endif + +enum isl_schedule_node_type { + isl_schedule_node_error = -1, + isl_schedule_node_band, + isl_schedule_node_context, + isl_schedule_node_domain, + isl_schedule_node_expansion, + isl_schedule_node_extension, + isl_schedule_node_filter, + isl_schedule_node_leaf, + isl_schedule_node_guard, + isl_schedule_node_mark, + isl_schedule_node_sequence, + isl_schedule_node_set +}; + +struct __isl_export isl_schedule_node; +typedef struct isl_schedule_node isl_schedule_node; + +struct __isl_export isl_schedule; +typedef struct isl_schedule isl_schedule; + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/set.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/set.h @@ -0,0 +1,511 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_SET_H +#define ISL_SET_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +unsigned isl_basic_set_n_dim(__isl_keep isl_basic_set *bset); +unsigned isl_basic_set_n_param(__isl_keep isl_basic_set *bset); +unsigned isl_basic_set_total_dim(const struct isl_basic_set *bset); +unsigned isl_basic_set_dim(__isl_keep isl_basic_set *bset, + enum isl_dim_type type); + +unsigned isl_set_n_dim(__isl_keep isl_set *set); +unsigned isl_set_n_param(__isl_keep isl_set *set); +unsigned isl_set_dim(__isl_keep isl_set *set, enum isl_dim_type type); + +isl_ctx *isl_basic_set_get_ctx(__isl_keep isl_basic_set *bset); +isl_ctx *isl_set_get_ctx(__isl_keep isl_set *set); +__isl_give isl_space *isl_basic_set_get_space(__isl_keep isl_basic_set *bset); +__isl_give isl_space *isl_set_get_space(__isl_keep isl_set *set); +__isl_give isl_set *isl_set_reset_space(__isl_take isl_set *set, + __isl_take isl_space *dim); + +__isl_give isl_aff *isl_basic_set_get_div(__isl_keep isl_basic_set *bset, + int pos); + +__isl_give isl_local_space *isl_basic_set_get_local_space( + __isl_keep isl_basic_set *bset); + +const char *isl_basic_set_get_tuple_name(__isl_keep isl_basic_set *bset); +isl_bool isl_set_has_tuple_name(__isl_keep isl_set *set); +const char *isl_set_get_tuple_name(__isl_keep isl_set *set); +__isl_give isl_basic_set *isl_basic_set_set_tuple_name( + __isl_take isl_basic_set *set, const char *s); +__isl_give isl_set *isl_set_set_tuple_name(__isl_take isl_set *set, + const char *s); +const char *isl_basic_set_get_dim_name(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos); +__isl_give isl_basic_set *isl_basic_set_set_dim_name( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, const char *s); +isl_bool isl_set_has_dim_name(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +const char *isl_set_get_dim_name(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +__isl_give isl_set *isl_set_set_dim_name(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, const char *s); + +__isl_give isl_id *isl_basic_set_get_dim_id(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos); +__isl_give isl_basic_set *isl_basic_set_set_tuple_id( + __isl_take isl_basic_set *bset, __isl_take isl_id *id); +__isl_give isl_set *isl_set_set_dim_id(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); +isl_bool isl_set_has_dim_id(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +__isl_give isl_id *isl_set_get_dim_id(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +__isl_give isl_set *isl_set_set_tuple_id(__isl_take isl_set *set, + __isl_take isl_id *id); +__isl_give isl_set *isl_set_reset_tuple_id(__isl_take isl_set *set); +isl_bool isl_set_has_tuple_id(__isl_keep isl_set *set); +__isl_give isl_id *isl_set_get_tuple_id(__isl_keep isl_set *set); +__isl_give isl_set *isl_set_reset_user(__isl_take isl_set *set); + +int isl_set_find_dim_by_id(__isl_keep isl_set *set, enum isl_dim_type type, + __isl_keep isl_id *id); +int isl_set_find_dim_by_name(__isl_keep isl_set *set, enum isl_dim_type type, + const char *name); + +int isl_basic_set_is_rational(__isl_keep isl_basic_set *bset); + +__isl_null isl_basic_set *isl_basic_set_free(__isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_copy(__isl_keep isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_empty(__isl_take isl_space *dim); +__isl_give isl_basic_set *isl_basic_set_universe(__isl_take isl_space *dim); +__isl_give isl_basic_set *isl_basic_set_nat_universe(__isl_take isl_space *dim); +__isl_give isl_basic_set *isl_basic_set_positive_orthant( + __isl_take isl_space *space); +void isl_basic_set_print_internal(__isl_keep isl_basic_set *bset, + FILE *out, int indent); +__isl_export +__isl_give isl_basic_set *isl_basic_set_intersect( + __isl_take isl_basic_set *bset1, + __isl_take isl_basic_set *bset2); +__isl_export +__isl_give isl_basic_set *isl_basic_set_intersect_params( + __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2); +__isl_export +__isl_give isl_basic_set *isl_basic_set_apply( + __isl_take isl_basic_set *bset, + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_set *isl_basic_set_preimage_multi_aff( + __isl_take isl_basic_set *bset, __isl_take isl_multi_aff *ma); +__isl_export +__isl_give isl_basic_set *isl_basic_set_affine_hull( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_remove_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_export +__isl_give isl_basic_set *isl_basic_set_sample(__isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_basic_set *isl_basic_set_detect_equalities( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_remove_redundancies( + __isl_take isl_basic_set *bset); +__isl_give isl_set *isl_set_remove_redundancies(__isl_take isl_set *set); +__isl_give isl_basic_set *isl_basic_set_list_intersect( + __isl_take struct isl_basic_set_list *list); +__isl_give isl_basic_set *isl_basic_set_list_product( + __isl_take struct isl_basic_set_list *list); + +__isl_give isl_set *isl_set_list_union(__isl_take isl_set_list *list); + +__isl_give isl_basic_set *isl_basic_set_read_from_file(isl_ctx *ctx, + FILE *input); +__isl_constructor +__isl_give isl_basic_set *isl_basic_set_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give isl_set *isl_set_read_from_file(isl_ctx *ctx, FILE *input); +__isl_constructor +__isl_give isl_set *isl_set_read_from_str(isl_ctx *ctx, const char *str); +void isl_basic_set_dump(__isl_keep isl_basic_set *bset); +void isl_set_dump(__isl_keep isl_set *set); +__isl_give isl_printer *isl_printer_print_basic_set( + __isl_take isl_printer *printer, __isl_keep isl_basic_set *bset); +__isl_give isl_printer *isl_printer_print_set(__isl_take isl_printer *printer, + __isl_keep isl_set *map); +__isl_give isl_basic_set *isl_basic_set_fix_si(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_basic_set *isl_basic_set_fix_val(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v); +__isl_give isl_set *isl_set_fix_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_set *isl_set_lower_bound_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_set *isl_set_lower_bound_val(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *value); +__isl_give isl_set *isl_set_upper_bound_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value); +__isl_give isl_set *isl_set_upper_bound_val(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *value); + +__isl_give isl_set *isl_set_equate(__isl_take isl_set *set, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2); + +__isl_export +isl_bool isl_basic_set_is_equal(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); +isl_bool isl_basic_set_is_disjoint(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); + +__isl_give isl_set *isl_basic_set_partial_lexmin( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_set *isl_basic_set_partial_lexmax( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_set *isl_set_partial_lexmin( + __isl_take isl_set *set, __isl_take isl_set *dom, + __isl_give isl_set **empty); +__isl_give isl_set *isl_set_partial_lexmax( + __isl_take isl_set *set, __isl_take isl_set *dom, + __isl_give isl_set **empty); +__isl_export +__isl_give isl_set *isl_basic_set_lexmin(__isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_set *isl_basic_set_lexmax(__isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_set *isl_set_lexmin(__isl_take isl_set *set); +__isl_export +__isl_give isl_set *isl_set_lexmax(__isl_take isl_set *set); +__isl_give isl_pw_multi_aff *isl_basic_set_partial_lexmin_pw_multi_aff( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_pw_multi_aff *isl_basic_set_partial_lexmax_pw_multi_aff( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty); +__isl_give isl_pw_multi_aff *isl_set_lexmin_pw_multi_aff( + __isl_take isl_set *set); +__isl_give isl_pw_multi_aff *isl_set_lexmax_pw_multi_aff( + __isl_take isl_set *set); + +__isl_export +__isl_give isl_set *isl_basic_set_union( + __isl_take isl_basic_set *bset1, + __isl_take isl_basic_set *bset2); + +int isl_basic_set_compare_at(struct isl_basic_set *bset1, + struct isl_basic_set *bset2, int pos); +int isl_set_follows_at(__isl_keep isl_set *set1, + __isl_keep isl_set *set2, int pos); + +__isl_give isl_basic_set *isl_basic_set_params(__isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_from_params( + __isl_take isl_basic_set *bset); +__isl_give isl_set *isl_set_params(__isl_take isl_set *set); +__isl_give isl_set *isl_set_from_params(__isl_take isl_set *set); + +int isl_basic_set_dims_get_sign(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, unsigned n, int *signs); + +isl_bool isl_basic_set_plain_is_universe(__isl_keep isl_basic_set *bset); +isl_bool isl_basic_set_is_universe(__isl_keep isl_basic_set *bset); +isl_bool isl_basic_set_plain_is_empty(__isl_keep isl_basic_set *bset); +__isl_export +isl_bool isl_basic_set_is_empty(__isl_keep isl_basic_set *bset); +int isl_basic_set_is_bounded(__isl_keep isl_basic_set *bset); +__isl_export +isl_bool isl_basic_set_is_subset(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); +isl_bool isl_basic_set_plain_is_equal(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2); + +__isl_give isl_set *isl_set_empty(__isl_take isl_space *dim); +__isl_give isl_set *isl_set_universe(__isl_take isl_space *dim); +__isl_give isl_set *isl_set_nat_universe(__isl_take isl_space *dim); +__isl_give isl_set *isl_set_copy(__isl_keep isl_set *set); +__isl_null isl_set *isl_set_free(__isl_take isl_set *set); +__isl_constructor +__isl_give isl_set *isl_set_from_basic_set(__isl_take isl_basic_set *bset); +__isl_export +__isl_give isl_basic_set *isl_set_sample(__isl_take isl_set *set); +__isl_give isl_point *isl_basic_set_sample_point(__isl_take isl_basic_set *bset); +__isl_give isl_point *isl_set_sample_point(__isl_take isl_set *set); +__isl_export +__isl_give isl_set *isl_set_detect_equalities(__isl_take isl_set *set); +__isl_export +__isl_give isl_basic_set *isl_set_affine_hull(__isl_take isl_set *set); +__isl_give isl_basic_set *isl_set_convex_hull(__isl_take isl_set *set); +__isl_export +__isl_give isl_basic_set *isl_set_polyhedral_hull(__isl_take isl_set *set); +__isl_give isl_basic_set *isl_set_simple_hull(__isl_take isl_set *set); +__isl_export +__isl_give isl_basic_set *isl_set_unshifted_simple_hull( + __isl_take isl_set *set); +__isl_give isl_basic_set *isl_set_plain_unshifted_simple_hull( + __isl_take isl_set *set); +__isl_give isl_basic_set *isl_set_unshifted_simple_hull_from_set_list( + __isl_take isl_set *set, __isl_take isl_set_list *list); +struct isl_basic_set *isl_set_bounded_simple_hull(struct isl_set *set); +__isl_give isl_set *isl_set_recession_cone(__isl_take isl_set *set); + +struct isl_set *isl_set_union_disjoint( + struct isl_set *set1, struct isl_set *set2); +__isl_export +__isl_give isl_set *isl_set_union( + __isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_give isl_set *isl_set_product(__isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_give isl_basic_set *isl_basic_set_flat_product( + __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2); +__isl_give isl_set *isl_set_flat_product(__isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_export +__isl_give isl_set *isl_set_intersect( + __isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_export +__isl_give isl_set *isl_set_intersect_params(__isl_take isl_set *set, + __isl_take isl_set *params); +__isl_export +__isl_give isl_set *isl_set_subtract( + __isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_export +__isl_give isl_set *isl_set_complement(__isl_take isl_set *set); +__isl_export +__isl_give isl_set *isl_set_apply( + __isl_take isl_set *set, + __isl_take isl_map *map); +__isl_give isl_set *isl_set_preimage_multi_aff(__isl_take isl_set *set, + __isl_take isl_multi_aff *ma); +__isl_give isl_set *isl_set_preimage_pw_multi_aff(__isl_take isl_set *set, + __isl_take isl_pw_multi_aff *pma); +__isl_give isl_set *isl_set_preimage_multi_pw_aff(__isl_take isl_set *set, + __isl_take isl_multi_pw_aff *mpa); +__isl_give isl_set *isl_set_fix_val(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v); +struct isl_set *isl_set_fix_dim_si(struct isl_set *set, + unsigned dim, int value); +__isl_give isl_basic_set *isl_basic_set_insert_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, unsigned n); +__isl_give isl_set *isl_set_insert_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, unsigned n); +__isl_give isl_basic_set *isl_basic_set_add_dims(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned n); +ISL_DEPRECATED +__isl_give isl_basic_set *isl_basic_set_add(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned n); +__isl_give isl_set *isl_set_add_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned n); +__isl_give isl_basic_set *isl_basic_set_move_dims(__isl_take isl_basic_set *bset, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_set *isl_set_move_dims(__isl_take isl_set *set, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_basic_set *isl_basic_set_project_out( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_set *isl_set_project_out(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_map *isl_set_project_onto_map(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_set *isl_basic_set_remove_divs( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_eliminate( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_set *isl_set_eliminate(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); +struct isl_set *isl_set_eliminate_dims(struct isl_set *set, + unsigned first, unsigned n); +__isl_give isl_set *isl_set_remove_dims(__isl_take isl_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_set *isl_basic_set_remove_divs_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_set *isl_set_remove_divs_involving_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_set *isl_basic_set_remove_unknown_divs( + __isl_take isl_basic_set *bset); +__isl_give isl_set *isl_set_remove_unknown_divs(__isl_take isl_set *set); +__isl_give isl_set *isl_set_remove_divs(__isl_take isl_set *set); +__isl_give isl_set *isl_set_split_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_set *isl_basic_set_drop_constraints_not_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_set *isl_set_drop_constraints_involving_dims( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_set *isl_set_drop_constraints_not_involving_dims( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); + +isl_bool isl_basic_set_involves_dims(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +isl_bool isl_set_involves_dims(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); + +void isl_set_print_internal(__isl_keep isl_set *set, FILE *out, int indent); +isl_bool isl_set_plain_is_empty(__isl_keep isl_set *set); +isl_bool isl_set_plain_is_universe(__isl_keep isl_set *set); +isl_bool isl_set_is_params(__isl_keep isl_set *set); +__isl_export +isl_bool isl_set_is_empty(__isl_keep isl_set *set); +int isl_set_is_bounded(__isl_keep isl_set *set); +__isl_export +isl_bool isl_set_is_subset(__isl_keep isl_set *set1, __isl_keep isl_set *set2); +__isl_export +isl_bool isl_set_is_strict_subset(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); +__isl_export +isl_bool isl_set_is_equal(__isl_keep isl_set *set1, __isl_keep isl_set *set2); +__isl_export +isl_bool isl_set_is_disjoint(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); +isl_bool isl_set_is_singleton(__isl_keep isl_set *set); +int isl_set_is_box(__isl_keep isl_set *set); +int isl_set_has_equal_space(__isl_keep isl_set *set1, __isl_keep isl_set *set2); + +__isl_give isl_set *isl_set_sum(__isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_give isl_basic_set *isl_basic_set_neg(__isl_take isl_basic_set *bset); +__isl_give isl_set *isl_set_neg(__isl_take isl_set *set); + +__isl_give isl_set *isl_set_make_disjoint(__isl_take isl_set *set); +struct isl_set *isl_basic_set_compute_divs(struct isl_basic_set *bset); +__isl_give isl_set *isl_set_compute_divs(__isl_take isl_set *set); +__isl_give isl_set *isl_set_align_divs(__isl_take isl_set *set); + +struct isl_basic_set *isl_set_copy_basic_set(struct isl_set *set); +struct isl_set *isl_set_drop_basic_set(struct isl_set *set, + struct isl_basic_set *bset); + +__isl_give isl_val *isl_set_plain_get_val_if_fixed(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +int isl_set_dim_is_bounded(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +isl_bool isl_set_dim_has_lower_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +isl_bool isl_set_dim_has_upper_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +isl_bool isl_set_dim_has_any_lower_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); +isl_bool isl_set_dim_has_any_upper_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos); + +__isl_export +__isl_give isl_basic_set *isl_basic_set_gist(__isl_take isl_basic_set *bset, + __isl_take isl_basic_set *context); +__isl_give isl_set *isl_set_gist_basic_set(__isl_take isl_set *set, + __isl_take isl_basic_set *context); +__isl_export +__isl_give isl_set *isl_set_gist(__isl_take isl_set *set, + __isl_take isl_set *context); +__isl_give isl_set *isl_set_gist_params(__isl_take isl_set *set, + __isl_take isl_set *context); +isl_stat isl_set_dim_residue_class_val(__isl_keep isl_set *set, + int pos, __isl_give isl_val **modulo, __isl_give isl_val **residue); + +__isl_export +__isl_give isl_set *isl_set_coalesce(__isl_take isl_set *set); + +int isl_set_plain_cmp(__isl_keep isl_set *set1, __isl_keep isl_set *set2); +isl_bool isl_set_plain_is_equal(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); +isl_bool isl_set_plain_is_disjoint(__isl_keep isl_set *set1, + __isl_keep isl_set *set2); + +uint32_t isl_set_get_hash(struct isl_set *set); + +int isl_set_dim_is_unique(struct isl_set *set, unsigned dim); + +int isl_set_n_basic_set(__isl_keep isl_set *set); +__isl_export +isl_stat isl_set_foreach_basic_set(__isl_keep isl_set *set, + isl_stat (*fn)(__isl_take isl_basic_set *bset, void *user), void *user); +__isl_give isl_basic_set_list *isl_set_get_basic_set_list( + __isl_keep isl_set *set); + +isl_stat isl_set_foreach_point(__isl_keep isl_set *set, + isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user); +__isl_give isl_val *isl_set_count_val(__isl_keep isl_set *set); + +__isl_constructor +__isl_give isl_basic_set *isl_basic_set_from_point(__isl_take isl_point *pnt); +__isl_constructor +__isl_give isl_set *isl_set_from_point(__isl_take isl_point *pnt); +__isl_give isl_basic_set *isl_basic_set_box_from_points( + __isl_take isl_point *pnt1, __isl_take isl_point *pnt2); +__isl_give isl_set *isl_set_box_from_points(__isl_take isl_point *pnt1, + __isl_take isl_point *pnt2); + +__isl_give isl_basic_set *isl_basic_set_lift(__isl_take isl_basic_set *bset); +__isl_give isl_set *isl_set_lift(__isl_take isl_set *set); + +__isl_give isl_map *isl_set_lex_le_set(__isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_give isl_map *isl_set_lex_lt_set(__isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_give isl_map *isl_set_lex_ge_set(__isl_take isl_set *set1, + __isl_take isl_set *set2); +__isl_give isl_map *isl_set_lex_gt_set(__isl_take isl_set *set1, + __isl_take isl_set *set2); + +int isl_set_size(__isl_keep isl_set *set); + +__isl_give isl_basic_set *isl_basic_set_align_params( + __isl_take isl_basic_set *bset, __isl_take isl_space *model); +__isl_give isl_set *isl_set_align_params(__isl_take isl_set *set, + __isl_take isl_space *model); + +__isl_give isl_mat *isl_basic_set_equalities_matrix( + __isl_keep isl_basic_set *bset, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4); +__isl_give isl_mat *isl_basic_set_inequalities_matrix( + __isl_keep isl_basic_set *bset, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4); +__isl_give isl_basic_set *isl_basic_set_from_constraint_matrices( + __isl_take isl_space *dim, + __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4); + +__isl_give isl_mat *isl_basic_set_reduced_basis(__isl_keep isl_basic_set *bset); + +__isl_give isl_basic_set *isl_basic_set_coefficients( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_set_coefficients(__isl_take isl_set *set); +__isl_give isl_basic_set *isl_basic_set_solutions( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_set_solutions(__isl_take isl_set *set); + +__isl_give isl_pw_aff *isl_set_dim_max(__isl_take isl_set *set, int pos); +__isl_give isl_pw_aff *isl_set_dim_min(__isl_take isl_set *set, int pos); + +__isl_give char *isl_set_to_str(__isl_keep isl_set *set); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/set_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/set_type.h @@ -0,0 +1,6 @@ +#ifndef ISL_SET_TYPE_H +#define ISL_SET_TYPE_H + +#include + +#endif Index: lib/Analysis/isl/include/isl/space.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/space.h @@ -0,0 +1,177 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_SPACE_H +#define ISL_SPACE_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_space; +typedef struct isl_space isl_space; + +enum isl_dim_type { + isl_dim_cst, + isl_dim_param, + isl_dim_in, + isl_dim_out, + isl_dim_set = isl_dim_out, + isl_dim_div, + isl_dim_all +}; + +isl_ctx *isl_space_get_ctx(__isl_keep isl_space *dim); +__isl_give isl_space *isl_space_alloc(isl_ctx *ctx, + unsigned nparam, unsigned n_in, unsigned n_out); +__isl_give isl_space *isl_space_set_alloc(isl_ctx *ctx, + unsigned nparam, unsigned dim); +__isl_give isl_space *isl_space_params_alloc(isl_ctx *ctx, unsigned nparam); +__isl_give isl_space *isl_space_copy(__isl_keep isl_space *dim); +__isl_null isl_space *isl_space_free(__isl_take isl_space *space); + +isl_bool isl_space_is_params(__isl_keep isl_space *space); +isl_bool isl_space_is_set(__isl_keep isl_space *space); +isl_bool isl_space_is_map(__isl_keep isl_space *space); + +__isl_give isl_space *isl_space_set_tuple_name(__isl_take isl_space *dim, + enum isl_dim_type type, const char *s); +isl_bool isl_space_has_tuple_name(__isl_keep isl_space *space, + enum isl_dim_type type); +const char *isl_space_get_tuple_name(__isl_keep isl_space *dim, + enum isl_dim_type type); +__isl_give isl_space *isl_space_set_tuple_id(__isl_take isl_space *dim, + enum isl_dim_type type, __isl_take isl_id *id); +__isl_give isl_space *isl_space_reset_tuple_id(__isl_take isl_space *dim, + enum isl_dim_type type); +isl_bool isl_space_has_tuple_id(__isl_keep isl_space *dim, + enum isl_dim_type type); +__isl_give isl_id *isl_space_get_tuple_id(__isl_keep isl_space *dim, + enum isl_dim_type type); +__isl_give isl_space *isl_space_reset_user(__isl_take isl_space *space); + +__isl_give isl_space *isl_space_set_dim_id(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id); +isl_bool isl_space_has_dim_id(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos); +__isl_give isl_id *isl_space_get_dim_id(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos); + +int isl_space_find_dim_by_id(__isl_keep isl_space *dim, enum isl_dim_type type, + __isl_keep isl_id *id); +int isl_space_find_dim_by_name(__isl_keep isl_space *space, + enum isl_dim_type type, const char *name); + +isl_bool isl_space_has_dim_name(__isl_keep isl_space *space, + enum isl_dim_type type, unsigned pos); +__isl_give isl_space *isl_space_set_dim_name(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos, + __isl_keep const char *name); +__isl_keep const char *isl_space_get_dim_name(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos); + +__isl_give isl_space *isl_space_extend(__isl_take isl_space *dim, + unsigned nparam, unsigned n_in, unsigned n_out); +__isl_give isl_space *isl_space_add_dims(__isl_take isl_space *dim, enum isl_dim_type type, + unsigned n); +__isl_give isl_space *isl_space_move_dims(__isl_take isl_space *dim, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); +__isl_give isl_space *isl_space_insert_dims(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos, unsigned n); +__isl_give isl_space *isl_space_join(__isl_take isl_space *left, + __isl_take isl_space *right); +__isl_give isl_space *isl_space_product(__isl_take isl_space *left, + __isl_take isl_space *right); +__isl_give isl_space *isl_space_domain_product(__isl_take isl_space *left, + __isl_take isl_space *right); +__isl_give isl_space *isl_space_range_product(__isl_take isl_space *left, + __isl_take isl_space *right); +__isl_give isl_space *isl_space_factor_domain(__isl_take isl_space *space); +__isl_give isl_space *isl_space_factor_range(__isl_take isl_space *space); +__isl_give isl_space *isl_space_domain_factor_domain( + __isl_take isl_space *space); +__isl_give isl_space *isl_space_domain_factor_range( + __isl_take isl_space *space); +__isl_give isl_space *isl_space_range_factor_domain( + __isl_take isl_space *space); +__isl_give isl_space *isl_space_range_factor_range( + __isl_take isl_space *space); +__isl_give isl_space *isl_space_map_from_set(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_map_from_domain_and_range( + __isl_take isl_space *domain, __isl_take isl_space *range); +__isl_give isl_space *isl_space_reverse(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_drop_dims(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned first, unsigned num); +__isl_give isl_space *isl_space_drop_inputs(__isl_take isl_space *dim, + unsigned first, unsigned n); +__isl_give isl_space *isl_space_drop_outputs(__isl_take isl_space *dim, + unsigned first, unsigned n); +__isl_give isl_space *isl_space_domain(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_from_domain(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_range(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_from_range(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_domain_map(__isl_take isl_space *space); +__isl_give isl_space *isl_space_range_map(__isl_take isl_space *space); +__isl_give isl_space *isl_space_params(__isl_take isl_space *space); +__isl_give isl_space *isl_space_set_from_params(__isl_take isl_space *space); + +__isl_give isl_space *isl_space_align_params(__isl_take isl_space *dim1, + __isl_take isl_space *dim2); + +isl_bool isl_space_is_wrapping(__isl_keep isl_space *dim); +isl_bool isl_space_domain_is_wrapping(__isl_keep isl_space *space); +isl_bool isl_space_range_is_wrapping(__isl_keep isl_space *space); +__isl_give isl_space *isl_space_wrap(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_unwrap(__isl_take isl_space *dim); + +isl_bool isl_space_can_zip(__isl_keep isl_space *dim); +__isl_give isl_space *isl_space_zip(__isl_take isl_space *dim); + +isl_bool isl_space_can_curry(__isl_keep isl_space *space); +__isl_give isl_space *isl_space_curry(__isl_take isl_space *space); + +isl_bool isl_space_can_range_curry(__isl_keep isl_space *space); +__isl_give isl_space *isl_space_range_curry(__isl_take isl_space *space); + +isl_bool isl_space_can_uncurry(__isl_keep isl_space *space); +__isl_give isl_space *isl_space_uncurry(__isl_take isl_space *space); + +isl_bool isl_space_is_domain(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); +isl_bool isl_space_is_range(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); +isl_bool isl_space_is_equal(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); +isl_bool isl_space_tuple_is_equal(__isl_keep isl_space *space1, + enum isl_dim_type type1, __isl_keep isl_space *space2, + enum isl_dim_type type2); +int isl_space_match(__isl_keep isl_space *dim1, enum isl_dim_type dim1_type, + __isl_keep isl_space *dim2, enum isl_dim_type dim2_type); +ISL_DEPRECATED +int isl_space_tuple_match(__isl_keep isl_space *space1, enum isl_dim_type type1, + __isl_keep isl_space *space2, enum isl_dim_type type2); +int isl_space_compatible(__isl_keep isl_space *dim1, + __isl_keep isl_space *dim2); +unsigned isl_space_dim(__isl_keep isl_space *dim, enum isl_dim_type type); + +__isl_give char *isl_space_to_str(__isl_keep isl_space *space); +__isl_give isl_printer *isl_printer_print_space(__isl_take isl_printer *p, + __isl_keep isl_space *dim); +void isl_space_dump(__isl_keep isl_space *dim); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/stdint.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/stdint.h @@ -0,0 +1 @@ +#include Index: lib/Analysis/isl/include/isl/stream.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/stream.h @@ -0,0 +1,96 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_STREAM_H +#define ISL_STREAM_H + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +enum isl_token_type { ISL_TOKEN_ERROR = -1, + ISL_TOKEN_UNKNOWN = 256, ISL_TOKEN_VALUE, + ISL_TOKEN_IDENT, ISL_TOKEN_GE, + ISL_TOKEN_LE, ISL_TOKEN_GT, ISL_TOKEN_LT, + ISL_TOKEN_NE, ISL_TOKEN_EQ_EQ, + ISL_TOKEN_LEX_GE, ISL_TOKEN_LEX_LE, + ISL_TOKEN_LEX_GT, ISL_TOKEN_LEX_LT, + ISL_TOKEN_TO, ISL_TOKEN_AND, + ISL_TOKEN_OR, ISL_TOKEN_EXISTS, ISL_TOKEN_NOT, + ISL_TOKEN_DEF, ISL_TOKEN_INFTY, ISL_TOKEN_NAN, + ISL_TOKEN_MIN, ISL_TOKEN_MAX, ISL_TOKEN_RAT, + ISL_TOKEN_TRUE, ISL_TOKEN_FALSE, + ISL_TOKEN_CEILD, ISL_TOKEN_FLOORD, ISL_TOKEN_MOD, + ISL_TOKEN_STRING, + ISL_TOKEN_MAP, ISL_TOKEN_AFF, + ISL_TOKEN_CEIL, ISL_TOKEN_FLOOR, + ISL_TOKEN_IMPLIES, + ISL_TOKEN_LAST }; + +struct isl_token; + +__isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok); +__isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok); +int isl_token_get_type(struct isl_token *tok); +void isl_token_free(struct isl_token *tok); + +struct isl_stream; +typedef struct isl_stream isl_stream; + +__isl_give isl_stream *isl_stream_new_file(isl_ctx *ctx, FILE *file); +__isl_give isl_stream *isl_stream_new_str(isl_ctx *ctx, const char *str); +void isl_stream_free(__isl_take isl_stream *s); + +isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s); + +void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok, + char *msg); + +struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s); +struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s); +int isl_stream_next_token_is(__isl_keep isl_stream *s, int type); +void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok); +void isl_stream_flush_tokens(__isl_keep isl_stream *s); +int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type); +char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s); +int isl_stream_eat(__isl_keep isl_stream *s, int type); +int isl_stream_is_empty(__isl_keep isl_stream *s); +int isl_stream_skip_line(__isl_keep isl_stream *s); + +enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s, + const char *name); + +struct isl_obj isl_stream_read_obj(__isl_keep isl_stream *s); +__isl_give isl_val *isl_stream_read_val(__isl_keep isl_stream *s); +__isl_give isl_multi_aff *isl_stream_read_multi_aff(__isl_keep isl_stream *s); +__isl_give isl_map *isl_stream_read_map(__isl_keep isl_stream *s); +__isl_give isl_set *isl_stream_read_set(__isl_keep isl_stream *s); +__isl_give isl_pw_qpolynomial *isl_stream_read_pw_qpolynomial( + __isl_keep isl_stream *s); +__isl_give isl_union_map *isl_stream_read_union_map(__isl_keep isl_stream *s); +__isl_give isl_schedule *isl_stream_read_schedule(isl_stream *s); + +int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s); +int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s); +int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s); +int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s); +int isl_stream_yaml_next(__isl_keep isl_stream *s); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/union_map.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/union_map.h @@ -0,0 +1,295 @@ +#ifndef ISL_UNION_MAP_H +#define ISL_UNION_MAP_H + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +unsigned isl_union_map_dim(__isl_keep isl_union_map *umap, + enum isl_dim_type type); +isl_bool isl_union_map_involves_dims(__isl_keep isl_union_map *umap, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_id *isl_union_map_get_dim_id(__isl_keep isl_union_map *umap, + enum isl_dim_type type, unsigned pos); + +__isl_constructor +__isl_give isl_union_map *isl_union_map_from_basic_map( + __isl_take isl_basic_map *bmap); +__isl_constructor +__isl_give isl_union_map *isl_union_map_from_map(__isl_take isl_map *map); +__isl_give isl_union_map *isl_union_map_empty(__isl_take isl_space *dim); +__isl_give isl_union_map *isl_union_map_copy(__isl_keep isl_union_map *umap); +__isl_null isl_union_map *isl_union_map_free(__isl_take isl_union_map *umap); + +isl_ctx *isl_union_map_get_ctx(__isl_keep isl_union_map *umap); +__isl_give isl_space *isl_union_map_get_space(__isl_keep isl_union_map *umap); + +__isl_give isl_union_map *isl_union_map_reset_user( + __isl_take isl_union_map *umap); + +int isl_union_map_find_dim_by_name(__isl_keep isl_union_map *umap, + enum isl_dim_type type, const char *name); + +__isl_give isl_union_map *isl_union_map_universe( + __isl_take isl_union_map *umap); +__isl_give isl_set *isl_union_map_params(__isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_set *isl_union_map_domain(__isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_set *isl_union_map_range(__isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_domain_map( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_pw_multi_aff *isl_union_map_domain_map_union_pw_multi_aff( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_range_map( + __isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_set_wrapped_domain_map( + __isl_take isl_union_set *uset); +__isl_give isl_union_map *isl_union_map_from_domain( + __isl_take isl_union_set *uset); +__isl_give isl_union_map *isl_union_map_from_range( + __isl_take isl_union_set *uset); + +__isl_export +__isl_give isl_union_map *isl_union_map_affine_hull( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_polyhedral_hull( + __isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_map_remove_redundancies( + __isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_map_simple_hull( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_coalesce( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_compute_divs( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_lexmin(__isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_lexmax(__isl_take isl_union_map *umap); + +__isl_give isl_union_map *isl_union_map_add_map(__isl_take isl_union_map *umap, + __isl_take isl_map *map); +__isl_export +__isl_give isl_union_map *isl_union_map_union(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_subtract( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_intersect( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_intersect_params( + __isl_take isl_union_map *umap, __isl_take isl_set *set); +__isl_export +__isl_give isl_union_map *isl_union_map_product(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_domain_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_give isl_union_map *isl_union_map_flat_domain_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_range_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_give isl_union_map *isl_union_map_flat_range_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_domain_factor_domain( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_domain_factor_range( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_range_factor_domain( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_range_factor_range( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_factor_domain( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_factor_range( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_gist(__isl_take isl_union_map *umap, + __isl_take isl_union_map *context); +__isl_export +__isl_give isl_union_map *isl_union_map_gist_params( + __isl_take isl_union_map *umap, __isl_take isl_set *set); +__isl_export +__isl_give isl_union_map *isl_union_map_gist_domain( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_map *isl_union_map_gist_range( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset); + +__isl_export +__isl_give isl_union_map *isl_union_map_intersect_domain( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_map *isl_union_map_intersect_range( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset); + +__isl_export +__isl_give isl_union_map *isl_union_map_subtract_domain( + __isl_take isl_union_map *umap, __isl_take isl_union_set *dom); +__isl_export +__isl_give isl_union_map *isl_union_map_subtract_range( + __isl_take isl_union_map *umap, __isl_take isl_union_set *dom); + +__isl_export +__isl_give isl_union_map *isl_union_map_apply_domain( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_export +__isl_give isl_union_map *isl_union_map_apply_range( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_give isl_union_map *isl_union_map_preimage_domain_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma); +__isl_give isl_union_map *isl_union_map_preimage_range_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma); +__isl_give isl_union_map *isl_union_map_preimage_domain_pw_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_union_map *isl_union_map_preimage_range_pw_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_union_map *isl_union_map_preimage_domain_multi_pw_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_pw_aff *mpa); +__isl_give isl_union_map *isl_union_map_preimage_domain_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma); +__isl_give isl_union_map *isl_union_map_preimage_range_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma); +__isl_export +__isl_give isl_union_map *isl_union_map_reverse(__isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_map_from_domain_and_range( + __isl_take isl_union_set *domain, __isl_take isl_union_set *range); + +__isl_export +__isl_give isl_union_map *isl_union_map_detect_equalities( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_set *isl_union_map_deltas(__isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_map_deltas_map( + __isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_set_identity(__isl_take isl_union_set *uset); + +__isl_give isl_union_map *isl_union_map_project_out( + __isl_take isl_union_map *umap, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_export +isl_bool isl_union_map_is_empty(__isl_keep isl_union_map *umap); +__isl_export +isl_bool isl_union_map_is_single_valued(__isl_keep isl_union_map *umap); +isl_bool isl_union_map_plain_is_injective(__isl_keep isl_union_map *umap); +__isl_export +isl_bool isl_union_map_is_injective(__isl_keep isl_union_map *umap); +__isl_export +isl_bool isl_union_map_is_bijective(__isl_keep isl_union_map *umap); +isl_bool isl_union_map_is_identity(__isl_keep isl_union_map *umap); + +__isl_export +isl_bool isl_union_map_is_subset(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); +__isl_export +isl_bool isl_union_map_is_equal(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); +isl_bool isl_union_map_is_disjoint(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); +__isl_export +isl_bool isl_union_map_is_strict_subset(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2); + +uint32_t isl_union_map_get_hash(__isl_keep isl_union_map *umap); + +int isl_union_map_n_map(__isl_keep isl_union_map *umap); +__isl_export +isl_stat isl_union_map_foreach_map(__isl_keep isl_union_map *umap, + isl_stat (*fn)(__isl_take isl_map *map, void *user), void *user); +__isl_give int isl_union_map_contains(__isl_keep isl_union_map *umap, + __isl_keep isl_space *dim); +__isl_give isl_map *isl_union_map_extract_map(__isl_keep isl_union_map *umap, + __isl_take isl_space *dim); +__isl_give isl_map *isl_map_from_union_map(__isl_take isl_union_map *umap); + +__isl_give isl_basic_map *isl_union_map_sample(__isl_take isl_union_map *umap); + +__isl_overload +__isl_give isl_union_map *isl_union_map_fixed_power_val( + __isl_take isl_union_map *umap, __isl_take isl_val *exp); +__isl_give isl_union_map *isl_union_map_power(__isl_take isl_union_map *umap, + int *exact); +__isl_give isl_union_map *isl_union_map_transitive_closure( + __isl_take isl_union_map *umap, int *exact); + +__isl_give isl_union_map *isl_union_map_lex_lt_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_give isl_union_map *isl_union_map_lex_le_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_give isl_union_map *isl_union_map_lex_gt_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); +__isl_give isl_union_map *isl_union_map_lex_ge_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2); + +__isl_give isl_union_map *isl_union_map_eq_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa); +__isl_give isl_union_map *isl_union_map_lex_lt_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa); +__isl_give isl_union_map *isl_union_map_lex_gt_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa); + +__isl_give isl_union_map *isl_union_map_read_from_file(isl_ctx *ctx, + FILE *input); +__isl_constructor +__isl_give isl_union_map *isl_union_map_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give char *isl_union_map_to_str(__isl_keep isl_union_map *umap); +__isl_give isl_printer *isl_printer_print_union_map(__isl_take isl_printer *p, + __isl_keep isl_union_map *umap); +void isl_union_map_dump(__isl_keep isl_union_map *umap); + +__isl_export +__isl_give isl_union_set *isl_union_map_wrap(__isl_take isl_union_map *umap); +__isl_export +__isl_give isl_union_map *isl_union_set_unwrap(__isl_take isl_union_set *uset); + +__isl_export +__isl_give isl_union_map *isl_union_map_zip(__isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_map_curry(__isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_map_range_curry( + __isl_take isl_union_map *umap); +__isl_give isl_union_map *isl_union_map_uncurry(__isl_take isl_union_map *umap); + +__isl_give isl_union_map *isl_union_map_align_params( + __isl_take isl_union_map *umap, __isl_take isl_space *model); +__isl_give isl_union_set *isl_union_set_align_params( + __isl_take isl_union_set *uset, __isl_take isl_space *model); + +ISL_DECLARE_LIST_FN(union_map) + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/union_map_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/union_map_type.h @@ -0,0 +1,24 @@ +#ifndef ISL_UNION_MAP_TYPE_H +#define ISL_UNION_MAP_TYPE_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct __isl_export isl_union_map; +typedef struct isl_union_map isl_union_map; +ISL_DECLARE_LIST_TYPE(union_map) +#ifndef isl_union_set +struct __isl_export isl_union_set; +typedef struct isl_union_set isl_union_set; +ISL_DECLARE_LIST_TYPE(union_set) +#endif + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/union_set.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/union_set.h @@ -0,0 +1,169 @@ +#ifndef ISL_UNION_SET_H +#define ISL_UNION_SET_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +unsigned isl_union_set_dim(__isl_keep isl_union_set *uset, + enum isl_dim_type type); + +__isl_constructor +__isl_give isl_union_set *isl_union_set_from_basic_set( + __isl_take isl_basic_set *bset); +__isl_constructor +__isl_give isl_union_set *isl_union_set_from_set(__isl_take isl_set *set); +__isl_give isl_union_set *isl_union_set_empty(__isl_take isl_space *dim); +__isl_give isl_union_set *isl_union_set_copy(__isl_keep isl_union_set *uset); +__isl_null isl_union_set *isl_union_set_free(__isl_take isl_union_set *uset); + +isl_ctx *isl_union_set_get_ctx(__isl_keep isl_union_set *uset); +__isl_give isl_space *isl_union_set_get_space(__isl_keep isl_union_set *uset); + +__isl_give isl_union_set *isl_union_set_reset_user( + __isl_take isl_union_set *uset); + +__isl_give isl_union_set *isl_union_set_universe( + __isl_take isl_union_set *uset); +__isl_give isl_set *isl_union_set_params(__isl_take isl_union_set *uset); + +__isl_export +__isl_give isl_union_set *isl_union_set_detect_equalities( + __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_set *isl_union_set_affine_hull( + __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_set *isl_union_set_polyhedral_hull( + __isl_take isl_union_set *uset); +__isl_give isl_union_set *isl_union_set_remove_redundancies( + __isl_take isl_union_set *uset); +__isl_give isl_union_set *isl_union_set_simple_hull( + __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_set *isl_union_set_coalesce( + __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_set *isl_union_set_compute_divs( + __isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_set *isl_union_set_lexmin(__isl_take isl_union_set *uset); +__isl_export +__isl_give isl_union_set *isl_union_set_lexmax(__isl_take isl_union_set *uset); + +__isl_give isl_union_set *isl_union_set_add_set(__isl_take isl_union_set *uset, + __isl_take isl_set *set); +__isl_export +__isl_give isl_union_set *isl_union_set_union(__isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2); +__isl_export +__isl_give isl_union_set *isl_union_set_subtract( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2); +__isl_export +__isl_give isl_union_set *isl_union_set_intersect( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2); +__isl_export +__isl_give isl_union_set *isl_union_set_intersect_params( + __isl_take isl_union_set *uset, __isl_take isl_set *set); +__isl_give isl_union_set *isl_union_set_product(__isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2); +__isl_export +__isl_give isl_union_set *isl_union_set_gist(__isl_take isl_union_set *uset, + __isl_take isl_union_set *context); +__isl_export +__isl_give isl_union_set *isl_union_set_gist_params( + __isl_take isl_union_set *uset, __isl_take isl_set *set); + +__isl_export +__isl_give isl_union_set *isl_union_set_apply( + __isl_take isl_union_set *uset, __isl_take isl_union_map *umap); +__isl_give isl_union_set *isl_union_set_preimage_multi_aff( + __isl_take isl_union_set *uset, __isl_take isl_multi_aff *ma); +__isl_give isl_union_set *isl_union_set_preimage_pw_multi_aff( + __isl_take isl_union_set *uset, __isl_take isl_pw_multi_aff *pma); +__isl_give isl_union_set *isl_union_set_preimage_union_pw_multi_aff( + __isl_take isl_union_set *uset, + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_union_set *isl_union_set_project_out( + __isl_take isl_union_set *uset, + enum isl_dim_type type, unsigned first, unsigned n); + +isl_bool isl_union_set_is_params(__isl_keep isl_union_set *uset); +__isl_export +isl_bool isl_union_set_is_empty(__isl_keep isl_union_set *uset); + +__isl_export +isl_bool isl_union_set_is_subset(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); +__isl_export +isl_bool isl_union_set_is_equal(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); +isl_bool isl_union_set_is_disjoint(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); +__isl_export +isl_bool isl_union_set_is_strict_subset(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2); + +uint32_t isl_union_set_get_hash(__isl_keep isl_union_set *uset); + +int isl_union_set_n_set(__isl_keep isl_union_set *uset); +__isl_export +isl_stat isl_union_set_foreach_set(__isl_keep isl_union_set *uset, + isl_stat (*fn)(__isl_take isl_set *set, void *user), void *user); +__isl_give int isl_union_set_contains(__isl_keep isl_union_set *uset, + __isl_keep isl_space *dim); +__isl_give isl_set *isl_union_set_extract_set(__isl_keep isl_union_set *uset, + __isl_take isl_space *dim); +__isl_give isl_set *isl_set_from_union_set(__isl_take isl_union_set *uset); +__isl_export +isl_stat isl_union_set_foreach_point(__isl_keep isl_union_set *uset, + isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user); + +__isl_give isl_basic_set *isl_union_set_sample(__isl_take isl_union_set *uset); +__isl_export +__isl_give isl_point *isl_union_set_sample_point( + __isl_take isl_union_set *uset); + +__isl_constructor +__isl_give isl_union_set *isl_union_set_from_point(__isl_take isl_point *pnt); + +__isl_give isl_union_set *isl_union_set_lift(__isl_take isl_union_set *uset); + +__isl_give isl_union_map *isl_union_set_lex_lt_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2); +__isl_give isl_union_map *isl_union_set_lex_le_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2); +__isl_give isl_union_map *isl_union_set_lex_gt_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2); +__isl_give isl_union_map *isl_union_set_lex_ge_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2); + +__isl_give isl_union_set *isl_union_set_coefficients( + __isl_take isl_union_set *bset); +__isl_give isl_union_set *isl_union_set_solutions( + __isl_take isl_union_set *bset); + +__isl_give isl_union_set *isl_union_set_read_from_file(isl_ctx *ctx, + FILE *input); +__isl_constructor +__isl_give isl_union_set *isl_union_set_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give char *isl_union_set_to_str(__isl_keep isl_union_set *uset); +__isl_give isl_printer *isl_printer_print_union_set(__isl_take isl_printer *p, + __isl_keep isl_union_set *uset); +void isl_union_set_dump(__isl_keep isl_union_set *uset); + +ISL_DECLARE_LIST_FN(union_set) + +__isl_give isl_union_set *isl_union_set_list_union( + __isl_take isl_union_set_list *list); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/union_set_type.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/union_set_type.h @@ -0,0 +1,6 @@ +#ifndef ISL_UNION_SET_TYPE_H +#define ISL_UNION_SET_TYPE_H + +#include + +#endif Index: lib/Analysis/isl/include/isl/val.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/val.h @@ -0,0 +1,166 @@ +#ifndef ISL_VAL_H +#define ISL_VAL_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct __isl_export isl_val; +typedef struct isl_val isl_val; + +ISL_DECLARE_LIST(val) + +struct __isl_export isl_multi_val; +typedef struct isl_multi_val isl_multi_val; + +ISL_DECLARE_MULTI(val) +ISL_DECLARE_MULTI_NEG(val) +ISL_DECLARE_MULTI_DIMS(val) +ISL_DECLARE_MULTI_WITH_DOMAIN(val) + +__isl_export +__isl_give isl_val *isl_val_zero(isl_ctx *ctx); +__isl_export +__isl_give isl_val *isl_val_one(isl_ctx *ctx); +__isl_export +__isl_give isl_val *isl_val_negone(isl_ctx *ctx); +__isl_export +__isl_give isl_val *isl_val_nan(isl_ctx *ctx); +__isl_export +__isl_give isl_val *isl_val_infty(isl_ctx *ctx); +__isl_export +__isl_give isl_val *isl_val_neginfty(isl_ctx *ctx); +__isl_constructor +__isl_give isl_val *isl_val_int_from_si(isl_ctx *ctx, long i); +__isl_give isl_val *isl_val_int_from_ui(isl_ctx *ctx, unsigned long u); +__isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, size_t n, + size_t size, const void *chunks); + +__isl_give isl_val *isl_val_copy(__isl_keep isl_val *v); +__isl_null isl_val *isl_val_free(__isl_take isl_val *v); + +isl_ctx *isl_val_get_ctx(__isl_keep isl_val *val); +uint32_t isl_val_get_hash(__isl_keep isl_val *val); +long isl_val_get_num_si(__isl_keep isl_val *v); +long isl_val_get_den_si(__isl_keep isl_val *v); +__isl_give isl_val *isl_val_get_den_val(__isl_keep isl_val *v); +double isl_val_get_d(__isl_keep isl_val *v); +size_t isl_val_n_abs_num_chunks(__isl_keep isl_val *v, size_t size); +int isl_val_get_abs_num_chunks(__isl_keep isl_val *v, size_t size, + void *chunks); + +__isl_give isl_val *isl_val_set_si(__isl_take isl_val *v, long i); + +__isl_export +__isl_give isl_val *isl_val_abs(__isl_take isl_val *v); +__isl_export +__isl_give isl_val *isl_val_neg(__isl_take isl_val *v); +__isl_export +__isl_give isl_val *isl_val_inv(__isl_take isl_val *v); +__isl_export +__isl_give isl_val *isl_val_floor(__isl_take isl_val *v); +__isl_export +__isl_give isl_val *isl_val_ceil(__isl_take isl_val *v); +__isl_export +__isl_give isl_val *isl_val_trunc(__isl_take isl_val *v); +__isl_give isl_val *isl_val_2exp(__isl_take isl_val *v); +__isl_export +__isl_give isl_val *isl_val_min(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_export +__isl_give isl_val *isl_val_max(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_export +__isl_give isl_val *isl_val_add(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_give isl_val *isl_val_add_ui(__isl_take isl_val *v1, unsigned long v2); +__isl_export +__isl_give isl_val *isl_val_sub(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_give isl_val *isl_val_sub_ui(__isl_take isl_val *v1, unsigned long v2); +__isl_export +__isl_give isl_val *isl_val_mul(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_give isl_val *isl_val_mul_ui(__isl_take isl_val *v1, unsigned long v2); +__isl_export +__isl_give isl_val *isl_val_div(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_export +__isl_give isl_val *isl_val_mod(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_export +__isl_give isl_val *isl_val_gcd(__isl_take isl_val *v1, __isl_take isl_val *v2); +__isl_give isl_val *isl_val_gcdext(__isl_take isl_val *v1, + __isl_take isl_val *v2, __isl_give isl_val **x, __isl_give isl_val **y); + +__isl_export +int isl_val_sgn(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_zero(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_one(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_negone(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_nonneg(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_nonpos(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_pos(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_neg(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_int(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_rat(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_nan(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_infty(__isl_keep isl_val *v); +__isl_export +isl_bool isl_val_is_neginfty(__isl_keep isl_val *v); + +__isl_export +int isl_val_cmp_si(__isl_keep isl_val *v, long i); + +__isl_export +isl_bool isl_val_lt(__isl_keep isl_val *v1, __isl_keep isl_val *v2); +__isl_export +isl_bool isl_val_le(__isl_keep isl_val *v1, __isl_keep isl_val *v2); +__isl_export +isl_bool isl_val_gt(__isl_keep isl_val *v1, __isl_keep isl_val *v2); +__isl_export +isl_bool isl_val_ge(__isl_keep isl_val *v1, __isl_keep isl_val *v2); +__isl_export +isl_bool isl_val_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2); +__isl_export +isl_bool isl_val_ne(__isl_keep isl_val *v1, __isl_keep isl_val *v2); +__isl_export +isl_bool isl_val_abs_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2); + +__isl_export +isl_bool isl_val_is_divisible_by(__isl_keep isl_val *v1, + __isl_keep isl_val *v2); + +__isl_constructor +__isl_give isl_val *isl_val_read_from_str(isl_ctx *ctx, const char *str); +__isl_give isl_printer *isl_printer_print_val(__isl_take isl_printer *p, + __isl_keep isl_val *v); +void isl_val_dump(__isl_keep isl_val *v); +__isl_give char *isl_val_to_str(__isl_keep isl_val *v); + +__isl_give isl_multi_val *isl_multi_val_add_val(__isl_take isl_multi_val *mv, + __isl_take isl_val *v); +__isl_give isl_multi_val *isl_multi_val_mod_val(__isl_take isl_multi_val *mv, + __isl_take isl_val *v); + +__isl_give isl_multi_val *isl_multi_val_read_from_str(isl_ctx *ctx, + const char *str); +__isl_give isl_printer *isl_printer_print_multi_val(__isl_take isl_printer *p, + __isl_keep isl_multi_val *mv); +void isl_multi_val_dump(__isl_keep isl_multi_val *mv); +__isl_give char *isl_multi_val_to_str(__isl_keep isl_multi_val *mv); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/val_gmp.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/val_gmp.h @@ -0,0 +1,21 @@ +#ifndef ISL_VAL_GMP_H +#define ISL_VAL_GMP_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_val *isl_val_int_from_gmp(isl_ctx *ctx, mpz_t z); +__isl_give isl_val *isl_val_from_gmp(isl_ctx *ctx, + const mpz_t n, const mpz_t d); +int isl_val_get_num_gmp(__isl_keep isl_val *v, mpz_t z); +int isl_val_get_den_gmp(__isl_keep isl_val *v, mpz_t z); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/vec.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/vec.h @@ -0,0 +1,78 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_VEC_H +#define ISL_VEC_H + +#include + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_vec; +typedef struct isl_vec isl_vec; + +__isl_give isl_vec *isl_vec_alloc(isl_ctx *ctx, unsigned size); +__isl_give isl_vec *isl_vec_copy(__isl_keep isl_vec *vec); +__isl_null isl_vec *isl_vec_free(__isl_take isl_vec *vec); + +isl_ctx *isl_vec_get_ctx(__isl_keep isl_vec *vec); + +int isl_vec_size(__isl_keep isl_vec *vec); +__isl_give isl_val *isl_vec_get_element_val(__isl_keep isl_vec *vec, int pos); +__isl_give isl_vec *isl_vec_set_element_si(__isl_take isl_vec *vec, + int pos, int v); +__isl_give isl_vec *isl_vec_set_element_val(__isl_take isl_vec *vec, + int pos, __isl_take isl_val *v); + +isl_bool isl_vec_is_equal(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2); +int isl_vec_cmp_element(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2, + int pos); + +void isl_vec_dump(__isl_keep isl_vec *vec); +__isl_give isl_printer *isl_printer_print_vec(__isl_take isl_printer *printer, + __isl_keep isl_vec *vec); + +struct isl_vec *isl_vec_ceil(struct isl_vec *vec); +struct isl_vec *isl_vec_normalize(struct isl_vec *vec); +__isl_give isl_vec *isl_vec_set_si(__isl_take isl_vec *vec, int v); +__isl_give isl_vec *isl_vec_set_val(__isl_take isl_vec *vec, + __isl_take isl_val *v); +__isl_give isl_vec *isl_vec_clr(__isl_take isl_vec *vec); +__isl_give isl_vec *isl_vec_neg(__isl_take isl_vec *vec); +__isl_give isl_vec *isl_vec_add(__isl_take isl_vec *vec1, + __isl_take isl_vec *vec2); +__isl_give isl_vec *isl_vec_extend(__isl_take isl_vec *vec, unsigned size); +__isl_give isl_vec *isl_vec_zero_extend(__isl_take isl_vec *vec, unsigned size); +__isl_give isl_vec *isl_vec_concat(__isl_take isl_vec *vec1, + __isl_take isl_vec *vec2); + +__isl_give isl_vec *isl_vec_sort(__isl_take isl_vec *vec); + +__isl_give isl_vec *isl_vec_read_from_file(isl_ctx *ctx, FILE *input); + +__isl_give isl_vec *isl_vec_drop_els(__isl_take isl_vec *vec, + unsigned pos, unsigned n); +__isl_give isl_vec *isl_vec_insert_els(__isl_take isl_vec *vec, + unsigned pos, unsigned n); +__isl_give isl_vec *isl_vec_insert_zero_els(__isl_take isl_vec *vec, + unsigned pos, unsigned n); +__isl_give isl_vec *isl_vec_move_els(__isl_take isl_vec *vec, + unsigned dst_col, unsigned src_col, unsigned n); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/version.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/version.h @@ -0,0 +1,14 @@ +#ifndef ISL_VERSION_H +#define ISL_VERSION_H + +#if defined(__cplusplus) +extern "C" { +#endif + +const char *isl_version(void); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/include/isl/vertices.h =================================================================== --- /dev/null +++ lib/Analysis/isl/include/isl/vertices.h @@ -0,0 +1,47 @@ +#ifndef ISL_VERTICES_H +#define ISL_VERTICES_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_external_vertex; +typedef struct isl_external_vertex isl_vertex; + +struct isl_cell; +typedef struct isl_cell isl_cell; + +struct isl_vertices; +typedef struct isl_vertices isl_vertices; + +isl_ctx *isl_vertex_get_ctx(__isl_keep isl_vertex *vertex); +int isl_vertex_get_id(__isl_keep isl_vertex *vertex); +__isl_give isl_basic_set *isl_vertex_get_domain(__isl_keep isl_vertex *vertex); +__isl_give isl_multi_aff *isl_vertex_get_expr(__isl_keep isl_vertex *vertex); +void isl_vertex_free(__isl_take isl_vertex *vertex); + +__isl_give isl_vertices *isl_basic_set_compute_vertices( + __isl_keep isl_basic_set *bset); +isl_ctx *isl_vertices_get_ctx(__isl_keep isl_vertices *vertices); +int isl_vertices_get_n_vertices(__isl_keep isl_vertices *vertices); +isl_stat isl_vertices_foreach_vertex(__isl_keep isl_vertices *vertices, + isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user); +void isl_vertices_free(__isl_take isl_vertices *vertices); + +isl_ctx *isl_cell_get_ctx(__isl_keep isl_cell *cell); +__isl_give isl_basic_set *isl_cell_get_domain(__isl_keep isl_cell *cell); +isl_stat isl_cell_foreach_vertex(__isl_keep isl_cell *cell, + isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user); +void isl_cell_free(__isl_take isl_cell *cell); + +isl_stat isl_vertices_foreach_cell(__isl_keep isl_vertices *vertices, + isl_stat (*fn)(__isl_take isl_cell *cell, void *user), void *user); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/install-sh =================================================================== --- /dev/null +++ lib/Analysis/isl/install-sh @@ -0,0 +1,501 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2013-12-25.23; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: Index: lib/Analysis/isl/interface/all.h =================================================================== --- /dev/null +++ lib/Analysis/isl/interface/all.h @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include Index: lib/Analysis/isl/interface/isl.py.top =================================================================== --- /dev/null +++ lib/Analysis/isl/interface/isl.py.top @@ -0,0 +1,29 @@ +from ctypes import * + +isl = cdll.LoadLibrary("libisl.so") +libc = cdll.LoadLibrary("libc.so.6") + +class Error(Exception): + pass + +class Context: + defaultInstance = None + + def __init__(self): + ptr = isl.isl_ctx_alloc() + self.ptr = ptr + + def __del__(self): + isl.isl_ctx_free(self) + + def from_param(self): + return self.ptr + + @staticmethod + def getDefaultInstance(): + if Context.defaultInstance == None: + Context.defaultInstance = Context() + return Context.defaultInstance + +isl.isl_ctx_alloc.restype = c_void_p +isl.isl_ctx_free.argtypes = [Context] Index: lib/Analysis/isl/isl.py =================================================================== --- /dev/null +++ lib/Analysis/isl/isl.py @@ -0,0 +1,100 @@ +import gdb +import re + +# GDB Pretty Printers for most isl objects +class IslObjectPrinter: + """Print an isl object""" + def __init__ (self, val, type): + self.val = val + self.type = type + + def to_string (self): + # Cast val to a void pointer to stop gdb using this pretty + # printer for the pointer which would lead to an infinite loop. + void_ptr = gdb.lookup_type('void').pointer() + value = str(self.val.cast(void_ptr)) + printer = gdb.parse_and_eval("isl_printer_to_str(isl_" + + str(self.type) + + "_get_ctx(" + value + "))") + printer = gdb.parse_and_eval("isl_printer_print_" + + str(self.type) + "(" + + str(printer) + ", " + + value + ")") + string = gdb.parse_and_eval("(char*)isl_printer_get_str(" + + str(printer) + ")") + gdb.parse_and_eval("isl_printer_free(" + str(printer) + ")") + return string + + def display_hint (self): + return 'string' + +class IslIntPrinter: + """Print an isl_int """ + def __init__ (self, val): + self.val = val + + def to_string (self): + # Cast val to a void pointer to stop gdb using this pretty + # printer for the pointer which would lead to an infinite loop. + void_ptr = gdb.lookup_type('void').pointer() + value = str(self.val.cast(void_ptr)) + + context = gdb.parse_and_eval("isl_ctx_alloc()") + printer = gdb.parse_and_eval("isl_printer_to_str(" + + str(context) + ")") + printer = gdb.parse_and_eval("isl_printer_print_isl_int(" + + str(printer) + ", " + + value + ")") + string = gdb.parse_and_eval("(char*)isl_printer_get_str(" + + str(printer) + ")") + gdb.parse_and_eval("isl_printer_free(" + str(printer) + ")") + gdb.parse_and_eval("isl_ctx_free(" + str(context) + ")") + return string + + def display_hint (self): + return 'string' + +class IslPrintCommand (gdb.Command): + """Print an isl value.""" + def __init__ (self): + super (IslPrintCommand, self).__init__ ("islprint", + gdb.COMMAND_OBSCURE) + def invoke (self, arg, from_tty): + arg = gdb.parse_and_eval(arg); + printer = str_lookup_function(arg) + + if printer == None: + print "No isl printer for this type" + return + + print printer.to_string() + +IslPrintCommand() + +def str_lookup_function (val): + if val.type.code != gdb.TYPE_CODE_PTR: + if str(val.type) == "isl_int": + return IslIntPrinter(val) + else: + return None + + lookup_tag = val.type.target() + regex = re.compile ("^isl_(.*)$") + + if lookup_tag == None: + return None + + m = regex.match (str(lookup_tag)) + + if m: + # Those types of printers defined in isl. + if m.group(1) in ["basic_set", "set", "union_set", "basic_map", + "map", "union_map", "qpolynomial", + "pw_qpolynomial", "pw_qpolynomial_fold", + "union_pw_qpolynomial", + "union_pw_qpolynomial_fold"]: + return IslObjectPrinter(val, m.group(1)) + return None + +# Do not register the pretty printer. +# gdb.current_objfile().pretty_printers.append(str_lookup_function) Index: lib/Analysis/isl/isl_aff.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_aff.c @@ -0,0 +1,8721 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2011 Sven Verdoolaege + * Copyright 2012-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#define ISL_DIM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef BASE +#define BASE aff + +#include + +#undef BASE +#define BASE pw_aff + +#include + +#undef BASE +#define BASE union_pw_aff + +#include + +#undef BASE +#define BASE union_pw_multi_aff + +#include + +__isl_give isl_aff *isl_aff_alloc_vec(__isl_take isl_local_space *ls, + __isl_take isl_vec *v) +{ + isl_aff *aff; + + if (!ls || !v) + goto error; + + aff = isl_calloc_type(v->ctx, struct isl_aff); + if (!aff) + goto error; + + aff->ref = 1; + aff->ls = ls; + aff->v = v; + + return aff; +error: + isl_local_space_free(ls); + isl_vec_free(v); + return NULL; +} + +__isl_give isl_aff *isl_aff_alloc(__isl_take isl_local_space *ls) +{ + isl_ctx *ctx; + isl_vec *v; + unsigned total; + + if (!ls) + return NULL; + + ctx = isl_local_space_get_ctx(ls); + if (!isl_local_space_divs_known(ls)) + isl_die(ctx, isl_error_invalid, "local space has unknown divs", + goto error); + if (!isl_local_space_is_set(ls)) + isl_die(ctx, isl_error_invalid, + "domain of affine expression should be a set", + goto error); + + total = isl_local_space_dim(ls, isl_dim_all); + v = isl_vec_alloc(ctx, 1 + 1 + total); + return isl_aff_alloc_vec(ls, v); +error: + isl_local_space_free(ls); + return NULL; +} + +__isl_give isl_aff *isl_aff_zero_on_domain(__isl_take isl_local_space *ls) +{ + isl_aff *aff; + + aff = isl_aff_alloc(ls); + if (!aff) + return NULL; + + isl_int_set_si(aff->v->el[0], 1); + isl_seq_clr(aff->v->el + 1, aff->v->size - 1); + + return aff; +} + +/* Return a piecewise affine expression defined on the specified domain + * that is equal to zero. + */ +__isl_give isl_pw_aff *isl_pw_aff_zero_on_domain(__isl_take isl_local_space *ls) +{ + return isl_pw_aff_from_aff(isl_aff_zero_on_domain(ls)); +} + +/* Return an affine expression defined on the specified domain + * that represents NaN. + */ +__isl_give isl_aff *isl_aff_nan_on_domain(__isl_take isl_local_space *ls) +{ + isl_aff *aff; + + aff = isl_aff_alloc(ls); + if (!aff) + return NULL; + + isl_seq_clr(aff->v->el, aff->v->size); + + return aff; +} + +/* Return a piecewise affine expression defined on the specified domain + * that represents NaN. + */ +__isl_give isl_pw_aff *isl_pw_aff_nan_on_domain(__isl_take isl_local_space *ls) +{ + return isl_pw_aff_from_aff(isl_aff_nan_on_domain(ls)); +} + +/* Return an affine expression that is equal to "val" on + * domain local space "ls". + */ +__isl_give isl_aff *isl_aff_val_on_domain(__isl_take isl_local_space *ls, + __isl_take isl_val *val) +{ + isl_aff *aff; + + if (!ls || !val) + goto error; + if (!isl_val_is_rat(val)) + isl_die(isl_val_get_ctx(val), isl_error_invalid, + "expecting rational value", goto error); + + aff = isl_aff_alloc(isl_local_space_copy(ls)); + if (!aff) + goto error; + + isl_seq_clr(aff->v->el + 2, aff->v->size - 2); + isl_int_set(aff->v->el[1], val->n); + isl_int_set(aff->v->el[0], val->d); + + isl_local_space_free(ls); + isl_val_free(val); + return aff; +error: + isl_local_space_free(ls); + isl_val_free(val); + return NULL; +} + +/* Return an affine expression that is equal to the specified dimension + * in "ls". + */ +__isl_give isl_aff *isl_aff_var_on_domain(__isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos) +{ + isl_space *space; + isl_aff *aff; + + if (!ls) + return NULL; + + space = isl_local_space_get_space(ls); + if (!space) + goto error; + if (isl_space_is_map(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "expecting (parameter) set space", goto error); + if (pos >= isl_local_space_dim(ls, type)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "position out of bounds", goto error); + + isl_space_free(space); + aff = isl_aff_alloc(ls); + if (!aff) + return NULL; + + pos += isl_local_space_offset(aff->ls, type); + + isl_int_set_si(aff->v->el[0], 1); + isl_seq_clr(aff->v->el + 1, aff->v->size - 1); + isl_int_set_si(aff->v->el[1 + pos], 1); + + return aff; +error: + isl_local_space_free(ls); + isl_space_free(space); + return NULL; +} + +/* Return a piecewise affine expression that is equal to + * the specified dimension in "ls". + */ +__isl_give isl_pw_aff *isl_pw_aff_var_on_domain(__isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos) +{ + return isl_pw_aff_from_aff(isl_aff_var_on_domain(ls, type, pos)); +} + +__isl_give isl_aff *isl_aff_copy(__isl_keep isl_aff *aff) +{ + if (!aff) + return NULL; + + aff->ref++; + return aff; +} + +__isl_give isl_aff *isl_aff_dup(__isl_keep isl_aff *aff) +{ + if (!aff) + return NULL; + + return isl_aff_alloc_vec(isl_local_space_copy(aff->ls), + isl_vec_copy(aff->v)); +} + +__isl_give isl_aff *isl_aff_cow(__isl_take isl_aff *aff) +{ + if (!aff) + return NULL; + + if (aff->ref == 1) + return aff; + aff->ref--; + return isl_aff_dup(aff); +} + +__isl_null isl_aff *isl_aff_free(__isl_take isl_aff *aff) +{ + if (!aff) + return NULL; + + if (--aff->ref > 0) + return NULL; + + isl_local_space_free(aff->ls); + isl_vec_free(aff->v); + + free(aff); + + return NULL; +} + +isl_ctx *isl_aff_get_ctx(__isl_keep isl_aff *aff) +{ + return aff ? isl_local_space_get_ctx(aff->ls) : NULL; +} + +/* Return a hash value that digests "aff". + */ +uint32_t isl_aff_get_hash(__isl_keep isl_aff *aff) +{ + uint32_t hash, ls_hash, v_hash; + + if (!aff) + return 0; + + hash = isl_hash_init(); + ls_hash = isl_local_space_get_hash(aff->ls); + isl_hash_hash(hash, ls_hash); + v_hash = isl_vec_get_hash(aff->v); + isl_hash_hash(hash, v_hash); + + return hash; +} + +/* Externally, an isl_aff has a map space, but internally, the + * ls field corresponds to the domain of that space. + */ +int isl_aff_dim(__isl_keep isl_aff *aff, enum isl_dim_type type) +{ + if (!aff) + return 0; + if (type == isl_dim_out) + return 1; + if (type == isl_dim_in) + type = isl_dim_set; + return isl_local_space_dim(aff->ls, type); +} + +/* Return the position of the dimension of the given type and name + * in "aff". + * Return -1 if no such dimension can be found. + */ +int isl_aff_find_dim_by_name(__isl_keep isl_aff *aff, enum isl_dim_type type, + const char *name) +{ + if (!aff) + return -1; + if (type == isl_dim_out) + return -1; + if (type == isl_dim_in) + type = isl_dim_set; + return isl_local_space_find_dim_by_name(aff->ls, type, name); +} + +__isl_give isl_space *isl_aff_get_domain_space(__isl_keep isl_aff *aff) +{ + return aff ? isl_local_space_get_space(aff->ls) : NULL; +} + +__isl_give isl_space *isl_aff_get_space(__isl_keep isl_aff *aff) +{ + isl_space *space; + if (!aff) + return NULL; + space = isl_local_space_get_space(aff->ls); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, 1); + return space; +} + +__isl_give isl_local_space *isl_aff_get_domain_local_space( + __isl_keep isl_aff *aff) +{ + return aff ? isl_local_space_copy(aff->ls) : NULL; +} + +__isl_give isl_local_space *isl_aff_get_local_space(__isl_keep isl_aff *aff) +{ + isl_local_space *ls; + if (!aff) + return NULL; + ls = isl_local_space_copy(aff->ls); + ls = isl_local_space_from_domain(ls); + ls = isl_local_space_add_dims(ls, isl_dim_out, 1); + return ls; +} + +/* Externally, an isl_aff has a map space, but internally, the + * ls field corresponds to the domain of that space. + */ +const char *isl_aff_get_dim_name(__isl_keep isl_aff *aff, + enum isl_dim_type type, unsigned pos) +{ + if (!aff) + return NULL; + if (type == isl_dim_out) + return NULL; + if (type == isl_dim_in) + type = isl_dim_set; + return isl_local_space_get_dim_name(aff->ls, type, pos); +} + +__isl_give isl_aff *isl_aff_reset_domain_space(__isl_take isl_aff *aff, + __isl_take isl_space *dim) +{ + aff = isl_aff_cow(aff); + if (!aff || !dim) + goto error; + + aff->ls = isl_local_space_reset_space(aff->ls, dim); + if (!aff->ls) + return isl_aff_free(aff); + + return aff; +error: + isl_aff_free(aff); + isl_space_free(dim); + return NULL; +} + +/* Reset the space of "aff". This function is called from isl_pw_templ.c + * and doesn't know if the space of an element object is represented + * directly or through its domain. It therefore passes along both. + */ +__isl_give isl_aff *isl_aff_reset_space_and_domain(__isl_take isl_aff *aff, + __isl_take isl_space *space, __isl_take isl_space *domain) +{ + isl_space_free(space); + return isl_aff_reset_domain_space(aff, domain); +} + +/* Reorder the coefficients of the affine expression based + * on the given reodering. + * The reordering r is assumed to have been extended with the local + * variables. + */ +static __isl_give isl_vec *vec_reorder(__isl_take isl_vec *vec, + __isl_take isl_reordering *r, int n_div) +{ + isl_vec *res; + int i; + + if (!vec || !r) + goto error; + + res = isl_vec_alloc(vec->ctx, + 2 + isl_space_dim(r->dim, isl_dim_all) + n_div); + isl_seq_cpy(res->el, vec->el, 2); + isl_seq_clr(res->el + 2, res->size - 2); + for (i = 0; i < r->len; ++i) + isl_int_set(res->el[2 + r->pos[i]], vec->el[2 + i]); + + isl_reordering_free(r); + isl_vec_free(vec); + return res; +error: + isl_vec_free(vec); + isl_reordering_free(r); + return NULL; +} + +/* Reorder the dimensions of the domain of "aff" according + * to the given reordering. + */ +__isl_give isl_aff *isl_aff_realign_domain(__isl_take isl_aff *aff, + __isl_take isl_reordering *r) +{ + aff = isl_aff_cow(aff); + if (!aff) + goto error; + + r = isl_reordering_extend(r, aff->ls->div->n_row); + aff->v = vec_reorder(aff->v, isl_reordering_copy(r), + aff->ls->div->n_row); + aff->ls = isl_local_space_realign(aff->ls, r); + + if (!aff->v || !aff->ls) + return isl_aff_free(aff); + + return aff; +error: + isl_aff_free(aff); + isl_reordering_free(r); + return NULL; +} + +__isl_give isl_aff *isl_aff_align_params(__isl_take isl_aff *aff, + __isl_take isl_space *model) +{ + if (!aff || !model) + goto error; + + if (!isl_space_match(aff->ls->dim, isl_dim_param, + model, isl_dim_param)) { + isl_reordering *exp; + + model = isl_space_drop_dims(model, isl_dim_in, + 0, isl_space_dim(model, isl_dim_in)); + model = isl_space_drop_dims(model, isl_dim_out, + 0, isl_space_dim(model, isl_dim_out)); + exp = isl_parameter_alignment_reordering(aff->ls->dim, model); + exp = isl_reordering_extend_space(exp, + isl_aff_get_domain_space(aff)); + aff = isl_aff_realign_domain(aff, exp); + } + + isl_space_free(model); + return aff; +error: + isl_space_free(model); + isl_aff_free(aff); + return NULL; +} + +/* Is "aff" obviously equal to zero? + * + * If the denominator is zero, then "aff" is not equal to zero. + */ +isl_bool isl_aff_plain_is_zero(__isl_keep isl_aff *aff) +{ + if (!aff) + return isl_bool_error; + + if (isl_int_is_zero(aff->v->el[0])) + return isl_bool_false; + return isl_seq_first_non_zero(aff->v->el + 1, aff->v->size - 1) < 0; +} + +/* Does "aff" represent NaN? + */ +isl_bool isl_aff_is_nan(__isl_keep isl_aff *aff) +{ + if (!aff) + return isl_bool_error; + + return isl_seq_first_non_zero(aff->v->el, 2) < 0; +} + +/* Does "pa" involve any NaNs? + */ +isl_bool isl_pw_aff_involves_nan(__isl_keep isl_pw_aff *pa) +{ + int i; + + if (!pa) + return isl_bool_error; + if (pa->n == 0) + return isl_bool_false; + + for (i = 0; i < pa->n; ++i) { + isl_bool is_nan = isl_aff_is_nan(pa->p[i].aff); + if (is_nan < 0 || is_nan) + return is_nan; + } + + return isl_bool_false; +} + +/* Are "aff1" and "aff2" obviously equal? + * + * NaN is not equal to anything, not even to another NaN. + */ +isl_bool isl_aff_plain_is_equal(__isl_keep isl_aff *aff1, + __isl_keep isl_aff *aff2) +{ + isl_bool equal; + + if (!aff1 || !aff2) + return isl_bool_error; + + if (isl_aff_is_nan(aff1) || isl_aff_is_nan(aff2)) + return isl_bool_false; + + equal = isl_local_space_is_equal(aff1->ls, aff2->ls); + if (equal < 0 || !equal) + return equal; + + return isl_vec_is_equal(aff1->v, aff2->v); +} + +/* Return the common denominator of "aff" in "v". + * + * We cannot return anything meaningful in case of a NaN. + */ +int isl_aff_get_denominator(__isl_keep isl_aff *aff, isl_int *v) +{ + if (!aff) + return -1; + if (isl_aff_is_nan(aff)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot get denominator of NaN", return -1); + isl_int_set(*v, aff->v->el[0]); + return 0; +} + +/* Return the common denominator of "aff". + */ +__isl_give isl_val *isl_aff_get_denominator_val(__isl_keep isl_aff *aff) +{ + isl_ctx *ctx; + + if (!aff) + return NULL; + + ctx = isl_aff_get_ctx(aff); + if (isl_aff_is_nan(aff)) + return isl_val_nan(ctx); + return isl_val_int_from_isl_int(ctx, aff->v->el[0]); +} + +/* Return the constant term of "aff" in "v". + * + * We cannot return anything meaningful in case of a NaN. + */ +int isl_aff_get_constant(__isl_keep isl_aff *aff, isl_int *v) +{ + if (!aff) + return -1; + if (isl_aff_is_nan(aff)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot get constant term of NaN", return -1); + isl_int_set(*v, aff->v->el[1]); + return 0; +} + +/* Return the constant term of "aff". + */ +__isl_give isl_val *isl_aff_get_constant_val(__isl_keep isl_aff *aff) +{ + isl_ctx *ctx; + isl_val *v; + + if (!aff) + return NULL; + + ctx = isl_aff_get_ctx(aff); + if (isl_aff_is_nan(aff)) + return isl_val_nan(ctx); + v = isl_val_rat_from_isl_int(ctx, aff->v->el[1], aff->v->el[0]); + return isl_val_normalize(v); +} + +/* Return the coefficient of the variable of type "type" at position "pos" + * of "aff" in "v". + * + * We cannot return anything meaningful in case of a NaN. + */ +int isl_aff_get_coefficient(__isl_keep isl_aff *aff, + enum isl_dim_type type, int pos, isl_int *v) +{ + if (!aff) + return -1; + + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return -1); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(aff->v->ctx, isl_error_invalid, + "position out of bounds", return -1); + + if (isl_aff_is_nan(aff)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot get coefficient of NaN", return -1); + pos += isl_local_space_offset(aff->ls, type); + isl_int_set(*v, aff->v->el[1 + pos]); + + return 0; +} + +/* Return the coefficient of the variable of type "type" at position "pos" + * of "aff". + */ +__isl_give isl_val *isl_aff_get_coefficient_val(__isl_keep isl_aff *aff, + enum isl_dim_type type, int pos) +{ + isl_ctx *ctx; + isl_val *v; + + if (!aff) + return NULL; + + ctx = isl_aff_get_ctx(aff); + if (type == isl_dim_out) + isl_die(ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return NULL); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(ctx, isl_error_invalid, + "position out of bounds", return NULL); + + if (isl_aff_is_nan(aff)) + return isl_val_nan(ctx); + pos += isl_local_space_offset(aff->ls, type); + v = isl_val_rat_from_isl_int(ctx, aff->v->el[1 + pos], aff->v->el[0]); + return isl_val_normalize(v); +} + +/* Return the sign of the coefficient of the variable of type "type" + * at position "pos" of "aff". + */ +int isl_aff_coefficient_sgn(__isl_keep isl_aff *aff, enum isl_dim_type type, + int pos) +{ + isl_ctx *ctx; + + if (!aff) + return 0; + + ctx = isl_aff_get_ctx(aff); + if (type == isl_dim_out) + isl_die(ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return 0); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(ctx, isl_error_invalid, + "position out of bounds", return 0); + + pos += isl_local_space_offset(aff->ls, type); + return isl_int_sgn(aff->v->el[1 + pos]); +} + +/* Replace the denominator of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_denominator(__isl_take isl_aff *aff, isl_int v) +{ + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_set(aff->v->el[0], v); + + return aff; +} + +/* Replace the numerator of the constant term of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_constant(__isl_take isl_aff *aff, isl_int v) +{ + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_set(aff->v->el[1], v); + + return aff; +} + +/* Replace the constant term of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_constant_val(__isl_take isl_aff *aff, + __isl_take isl_val *v) +{ + if (!aff || !v) + goto error; + + if (isl_aff_is_nan(aff)) { + isl_val_free(v); + return aff; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting rational value", goto error); + + if (isl_int_eq(aff->v->el[1], v->n) && + isl_int_eq(aff->v->el[0], v->d)) { + isl_val_free(v); + return aff; + } + + aff = isl_aff_cow(aff); + if (!aff) + goto error; + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + goto error; + + if (isl_int_eq(aff->v->el[0], v->d)) { + isl_int_set(aff->v->el[1], v->n); + } else if (isl_int_is_one(v->d)) { + isl_int_mul(aff->v->el[1], aff->v->el[0], v->n); + } else { + isl_seq_scale(aff->v->el + 1, + aff->v->el + 1, v->d, aff->v->size - 1); + isl_int_mul(aff->v->el[1], aff->v->el[0], v->n); + isl_int_mul(aff->v->el[0], aff->v->el[0], v->d); + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + goto error; + } + + isl_val_free(v); + return aff; +error: + isl_aff_free(aff); + isl_val_free(v); + return NULL; +} + +/* Add "v" to the constant term of "aff". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_add_constant(__isl_take isl_aff *aff, isl_int v) +{ + if (isl_int_is_zero(v)) + return aff; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_addmul(aff->v->el[1], aff->v->el[0], v); + + return aff; +} + +/* Add "v" to the constant term of "aff". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_add_constant_val(__isl_take isl_aff *aff, + __isl_take isl_val *v) +{ + if (!aff || !v) + goto error; + + if (isl_aff_is_nan(aff) || isl_val_is_zero(v)) { + isl_val_free(v); + return aff; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting rational value", goto error); + + aff = isl_aff_cow(aff); + if (!aff) + goto error; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + goto error; + + if (isl_int_is_one(v->d)) { + isl_int_addmul(aff->v->el[1], aff->v->el[0], v->n); + } else if (isl_int_eq(aff->v->el[0], v->d)) { + isl_int_add(aff->v->el[1], aff->v->el[1], v->n); + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + goto error; + } else { + isl_seq_scale(aff->v->el + 1, + aff->v->el + 1, v->d, aff->v->size - 1); + isl_int_addmul(aff->v->el[1], aff->v->el[0], v->n); + isl_int_mul(aff->v->el[0], aff->v->el[0], v->d); + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + goto error; + } + + isl_val_free(v); + return aff; +error: + isl_aff_free(aff); + isl_val_free(v); + return NULL; +} + +__isl_give isl_aff *isl_aff_add_constant_si(__isl_take isl_aff *aff, int v) +{ + isl_int t; + + isl_int_init(t); + isl_int_set_si(t, v); + aff = isl_aff_add_constant(aff, t); + isl_int_clear(t); + + return aff; +} + +/* Add "v" to the numerator of the constant term of "aff". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_add_constant_num(__isl_take isl_aff *aff, isl_int v) +{ + if (isl_int_is_zero(v)) + return aff; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_add(aff->v->el[1], aff->v->el[1], v); + + return aff; +} + +/* Add "v" to the numerator of the constant term of "aff". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_add_constant_num_si(__isl_take isl_aff *aff, int v) +{ + isl_int t; + + if (v == 0) + return aff; + + isl_int_init(t); + isl_int_set_si(t, v); + aff = isl_aff_add_constant_num(aff, t); + isl_int_clear(t); + + return aff; +} + +/* Replace the numerator of the constant term of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_constant_si(__isl_take isl_aff *aff, int v) +{ + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_set_si(aff->v->el[1], v); + + return aff; +} + +/* Replace the numerator of the coefficient of the variable of type "type" + * at position "pos" of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_coefficient(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, isl_int v) +{ + if (!aff) + return NULL; + + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return isl_aff_free(aff)); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(aff->v->ctx, isl_error_invalid, + "position out of bounds", return isl_aff_free(aff)); + + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + pos += isl_local_space_offset(aff->ls, type); + isl_int_set(aff->v->el[1 + pos], v); + + return aff; +} + +/* Replace the numerator of the coefficient of the variable of type "type" + * at position "pos" of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_coefficient_si(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, int v) +{ + if (!aff) + return NULL; + + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return isl_aff_free(aff)); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos < 0 || pos >= isl_local_space_dim(aff->ls, type)) + isl_die(aff->v->ctx, isl_error_invalid, + "position out of bounds", return isl_aff_free(aff)); + + if (isl_aff_is_nan(aff)) + return aff; + pos += isl_local_space_offset(aff->ls, type); + if (isl_int_cmp_si(aff->v->el[1 + pos], v) == 0) + return aff; + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_set_si(aff->v->el[1 + pos], v); + + return aff; +} + +/* Replace the coefficient of the variable of type "type" at position "pos" + * of "aff" by "v". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_set_coefficient_val(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, __isl_take isl_val *v) +{ + if (!aff || !v) + goto error; + + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + goto error); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(aff->v->ctx, isl_error_invalid, + "position out of bounds", goto error); + + if (isl_aff_is_nan(aff)) { + isl_val_free(v); + return aff; + } + if (!isl_val_is_rat(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting rational value", goto error); + + pos += isl_local_space_offset(aff->ls, type); + if (isl_int_eq(aff->v->el[1 + pos], v->n) && + isl_int_eq(aff->v->el[0], v->d)) { + isl_val_free(v); + return aff; + } + + aff = isl_aff_cow(aff); + if (!aff) + goto error; + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + goto error; + + if (isl_int_eq(aff->v->el[0], v->d)) { + isl_int_set(aff->v->el[1 + pos], v->n); + } else if (isl_int_is_one(v->d)) { + isl_int_mul(aff->v->el[1 + pos], aff->v->el[0], v->n); + } else { + isl_seq_scale(aff->v->el + 1, + aff->v->el + 1, v->d, aff->v->size - 1); + isl_int_mul(aff->v->el[1 + pos], aff->v->el[0], v->n); + isl_int_mul(aff->v->el[0], aff->v->el[0], v->d); + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + goto error; + } + + isl_val_free(v); + return aff; +error: + isl_aff_free(aff); + isl_val_free(v); + return NULL; +} + +/* Add "v" to the coefficient of the variable of type "type" + * at position "pos" of "aff". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_add_coefficient(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, isl_int v) +{ + if (!aff) + return NULL; + + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return isl_aff_free(aff)); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(aff->v->ctx, isl_error_invalid, + "position out of bounds", return isl_aff_free(aff)); + + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + pos += isl_local_space_offset(aff->ls, type); + isl_int_addmul(aff->v->el[1 + pos], aff->v->el[0], v); + + return aff; +} + +/* Add "v" to the coefficient of the variable of type "type" + * at position "pos" of "aff". + * + * A NaN is unaffected by this operation. + */ +__isl_give isl_aff *isl_aff_add_coefficient_val(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, __isl_take isl_val *v) +{ + if (!aff || !v) + goto error; + + if (isl_val_is_zero(v)) { + isl_val_free(v); + return aff; + } + + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + goto error); + if (type == isl_dim_in) + type = isl_dim_set; + + if (pos >= isl_local_space_dim(aff->ls, type)) + isl_die(aff->v->ctx, isl_error_invalid, + "position out of bounds", goto error); + + if (isl_aff_is_nan(aff)) { + isl_val_free(v); + return aff; + } + if (!isl_val_is_rat(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting rational value", goto error); + + aff = isl_aff_cow(aff); + if (!aff) + goto error; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + goto error; + + pos += isl_local_space_offset(aff->ls, type); + if (isl_int_is_one(v->d)) { + isl_int_addmul(aff->v->el[1 + pos], aff->v->el[0], v->n); + } else if (isl_int_eq(aff->v->el[0], v->d)) { + isl_int_add(aff->v->el[1 + pos], aff->v->el[1 + pos], v->n); + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + goto error; + } else { + isl_seq_scale(aff->v->el + 1, + aff->v->el + 1, v->d, aff->v->size - 1); + isl_int_addmul(aff->v->el[1 + pos], aff->v->el[0], v->n); + isl_int_mul(aff->v->el[0], aff->v->el[0], v->d); + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + goto error; + } + + isl_val_free(v); + return aff; +error: + isl_aff_free(aff); + isl_val_free(v); + return NULL; +} + +__isl_give isl_aff *isl_aff_add_coefficient_si(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, int v) +{ + isl_int t; + + isl_int_init(t); + isl_int_set_si(t, v); + aff = isl_aff_add_coefficient(aff, type, pos, t); + isl_int_clear(t); + + return aff; +} + +__isl_give isl_aff *isl_aff_get_div(__isl_keep isl_aff *aff, int pos) +{ + if (!aff) + return NULL; + + return isl_local_space_get_div(aff->ls, pos); +} + +/* Return the negation of "aff". + * + * As a special case, -NaN = NaN. + */ +__isl_give isl_aff *isl_aff_neg(__isl_take isl_aff *aff) +{ + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_seq_neg(aff->v->el + 1, aff->v->el + 1, aff->v->size - 1); + + return aff; +} + +/* Remove divs from the local space that do not appear in the affine + * expression. + * We currently only remove divs at the end. + * Some intermediate divs may also not appear directly in the affine + * expression, but we would also need to check that no other divs are + * defined in terms of them. + */ +__isl_give isl_aff *isl_aff_remove_unused_divs(__isl_take isl_aff *aff) +{ + int pos; + int off; + int n; + + if (!aff) + return NULL; + + n = isl_local_space_dim(aff->ls, isl_dim_div); + off = isl_local_space_offset(aff->ls, isl_dim_div); + + pos = isl_seq_last_non_zero(aff->v->el + 1 + off, n) + 1; + if (pos == n) + return aff; + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->ls = isl_local_space_drop_dims(aff->ls, isl_dim_div, pos, n - pos); + aff->v = isl_vec_drop_els(aff->v, 1 + off + pos, n - pos); + if (!aff->ls || !aff->v) + return isl_aff_free(aff); + + return aff; +} + +/* Given two affine expressions "p" of length p_len (including the + * denominator and the constant term) and "subs" of length subs_len, + * plug in "subs" for the variable at position "pos". + * The variables of "subs" and "p" are assumed to match up to subs_len, + * but "p" may have additional variables. + * "v" is an initialized isl_int that can be used internally. + * + * In particular, if "p" represents the expression + * + * (a i + g)/m + * + * with i the variable at position "pos" and "subs" represents the expression + * + * f/d + * + * then the result represents the expression + * + * (a f + d g)/(m d) + * + */ +void isl_seq_substitute(isl_int *p, int pos, isl_int *subs, + int p_len, int subs_len, isl_int v) +{ + isl_int_set(v, p[1 + pos]); + isl_int_set_si(p[1 + pos], 0); + isl_seq_combine(p + 1, subs[0], p + 1, v, subs + 1, subs_len - 1); + isl_seq_scale(p + subs_len, p + subs_len, subs[0], p_len - subs_len); + isl_int_mul(p[0], p[0], subs[0]); +} + +/* Look for any divs in the aff->ls with a denominator equal to one + * and plug them into the affine expression and any subsequent divs + * that may reference the div. + */ +static __isl_give isl_aff *plug_in_integral_divs(__isl_take isl_aff *aff) +{ + int i, n; + int len; + isl_int v; + isl_vec *vec; + isl_local_space *ls; + unsigned pos; + + if (!aff) + return NULL; + + n = isl_local_space_dim(aff->ls, isl_dim_div); + len = aff->v->size; + for (i = 0; i < n; ++i) { + if (!isl_int_is_one(aff->ls->div->row[i][0])) + continue; + ls = isl_local_space_copy(aff->ls); + ls = isl_local_space_substitute_seq(ls, isl_dim_div, i, + aff->ls->div->row[i], len, i + 1, n - (i + 1)); + vec = isl_vec_copy(aff->v); + vec = isl_vec_cow(vec); + if (!ls || !vec) + goto error; + + isl_int_init(v); + + pos = isl_local_space_offset(aff->ls, isl_dim_div) + i; + isl_seq_substitute(vec->el, pos, aff->ls->div->row[i], + len, len, v); + + isl_int_clear(v); + + isl_vec_free(aff->v); + aff->v = vec; + isl_local_space_free(aff->ls); + aff->ls = ls; + } + + return aff; +error: + isl_vec_free(vec); + isl_local_space_free(ls); + return isl_aff_free(aff); +} + +/* Look for any divs j that appear with a unit coefficient inside + * the definitions of other divs i and plug them into the definitions + * of the divs i. + * + * In particular, an expression of the form + * + * floor((f(..) + floor(g(..)/n))/m) + * + * is simplified to + * + * floor((n * f(..) + g(..))/(n * m)) + * + * This simplification is correct because we can move the expression + * f(..) into the inner floor in the original expression to obtain + * + * floor(floor((n * f(..) + g(..))/n)/m) + * + * from which we can derive the simplified expression. + */ +static __isl_give isl_aff *plug_in_unit_divs(__isl_take isl_aff *aff) +{ + int i, j, n; + int off; + + if (!aff) + return NULL; + + n = isl_local_space_dim(aff->ls, isl_dim_div); + off = isl_local_space_offset(aff->ls, isl_dim_div); + for (i = 1; i < n; ++i) { + for (j = 0; j < i; ++j) { + if (!isl_int_is_one(aff->ls->div->row[i][1 + off + j])) + continue; + aff->ls = isl_local_space_substitute_seq(aff->ls, + isl_dim_div, j, aff->ls->div->row[j], + aff->v->size, i, 1); + if (!aff->ls) + return isl_aff_free(aff); + } + } + + return aff; +} + +/* Swap divs "a" and "b" in "aff", which is assumed to be non-NULL. + * + * Even though this function is only called on isl_affs with a single + * reference, we are careful to only change aff->v and aff->ls together. + */ +static __isl_give isl_aff *swap_div(__isl_take isl_aff *aff, int a, int b) +{ + unsigned off = isl_local_space_offset(aff->ls, isl_dim_div); + isl_local_space *ls; + isl_vec *v; + + ls = isl_local_space_copy(aff->ls); + ls = isl_local_space_swap_div(ls, a, b); + v = isl_vec_copy(aff->v); + v = isl_vec_cow(v); + if (!ls || !v) + goto error; + + isl_int_swap(v->el[1 + off + a], v->el[1 + off + b]); + isl_vec_free(aff->v); + aff->v = v; + isl_local_space_free(aff->ls); + aff->ls = ls; + + return aff; +error: + isl_vec_free(v); + isl_local_space_free(ls); + return isl_aff_free(aff); +} + +/* Merge divs "a" and "b" in "aff", which is assumed to be non-NULL. + * + * We currently do not actually remove div "b", but simply add its + * coefficient to that of "a" and then zero it out. + */ +static __isl_give isl_aff *merge_divs(__isl_take isl_aff *aff, int a, int b) +{ + unsigned off = isl_local_space_offset(aff->ls, isl_dim_div); + + if (isl_int_is_zero(aff->v->el[1 + off + b])) + return aff; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_add(aff->v->el[1 + off + a], + aff->v->el[1 + off + a], aff->v->el[1 + off + b]); + isl_int_set_si(aff->v->el[1 + off + b], 0); + + return aff; +} + +/* Sort the divs in the local space of "aff" according to + * the comparison function "cmp_row" in isl_local_space.c, + * combining the coefficients of identical divs. + * + * Reordering divs does not change the semantics of "aff", + * so there is no need to call isl_aff_cow. + * Moreover, this function is currently only called on isl_affs + * with a single reference. + */ +static __isl_give isl_aff *sort_divs(__isl_take isl_aff *aff) +{ + int i, j, n; + + if (!aff) + return NULL; + + n = isl_aff_dim(aff, isl_dim_div); + for (i = 1; i < n; ++i) { + for (j = i - 1; j >= 0; --j) { + int cmp = isl_mat_cmp_div(aff->ls->div, j, j + 1); + if (cmp < 0) + break; + if (cmp == 0) + aff = merge_divs(aff, j, j + 1); + else + aff = swap_div(aff, j, j + 1); + if (!aff) + return NULL; + } + } + + return aff; +} + +/* Normalize the representation of "aff". + * + * This function should only be called of "new" isl_affs, i.e., + * with only a single reference. We therefore do not need to + * worry about affecting other instances. + */ +__isl_give isl_aff *isl_aff_normalize(__isl_take isl_aff *aff) +{ + if (!aff) + return NULL; + aff->v = isl_vec_normalize(aff->v); + if (!aff->v) + return isl_aff_free(aff); + aff = plug_in_integral_divs(aff); + aff = plug_in_unit_divs(aff); + aff = sort_divs(aff); + aff = isl_aff_remove_unused_divs(aff); + return aff; +} + +/* Given f, return floor(f). + * If f is an integer expression, then just return f. + * If f is a constant, then return the constant floor(f). + * Otherwise, if f = g/m, write g = q m + r, + * create a new div d = [r/m] and return the expression q + d. + * The coefficients in r are taken to lie between -m/2 and m/2. + * + * As a special case, floor(NaN) = NaN. + */ +__isl_give isl_aff *isl_aff_floor(__isl_take isl_aff *aff) +{ + int i; + int size; + isl_ctx *ctx; + isl_vec *div; + + if (!aff) + return NULL; + + if (isl_aff_is_nan(aff)) + return aff; + if (isl_int_is_one(aff->v->el[0])) + return aff; + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + if (isl_aff_is_cst(aff)) { + isl_int_fdiv_q(aff->v->el[1], aff->v->el[1], aff->v->el[0]); + isl_int_set_si(aff->v->el[0], 1); + return aff; + } + + div = isl_vec_copy(aff->v); + div = isl_vec_cow(div); + if (!div) + return isl_aff_free(aff); + + ctx = isl_aff_get_ctx(aff); + isl_int_fdiv_q(aff->v->el[0], aff->v->el[0], ctx->two); + for (i = 1; i < aff->v->size; ++i) { + isl_int_fdiv_r(div->el[i], div->el[i], div->el[0]); + isl_int_fdiv_q(aff->v->el[i], aff->v->el[i], div->el[0]); + if (isl_int_gt(div->el[i], aff->v->el[0])) { + isl_int_sub(div->el[i], div->el[i], div->el[0]); + isl_int_add_ui(aff->v->el[i], aff->v->el[i], 1); + } + } + + aff->ls = isl_local_space_add_div(aff->ls, div); + if (!aff->ls) + return isl_aff_free(aff); + + size = aff->v->size; + aff->v = isl_vec_extend(aff->v, size + 1); + if (!aff->v) + return isl_aff_free(aff); + isl_int_set_si(aff->v->el[0], 1); + isl_int_set_si(aff->v->el[size], 1); + + aff = isl_aff_normalize(aff); + + return aff; +} + +/* Compute + * + * aff mod m = aff - m * floor(aff/m) + */ +__isl_give isl_aff *isl_aff_mod(__isl_take isl_aff *aff, isl_int m) +{ + isl_aff *res; + + res = isl_aff_copy(aff); + aff = isl_aff_scale_down(aff, m); + aff = isl_aff_floor(aff); + aff = isl_aff_scale(aff, m); + res = isl_aff_sub(res, aff); + + return res; +} + +/* Compute + * + * aff mod m = aff - m * floor(aff/m) + * + * with m an integer value. + */ +__isl_give isl_aff *isl_aff_mod_val(__isl_take isl_aff *aff, + __isl_take isl_val *m) +{ + isl_aff *res; + + if (!aff || !m) + goto error; + + if (!isl_val_is_int(m)) + isl_die(isl_val_get_ctx(m), isl_error_invalid, + "expecting integer modulo", goto error); + + res = isl_aff_copy(aff); + aff = isl_aff_scale_down_val(aff, isl_val_copy(m)); + aff = isl_aff_floor(aff); + aff = isl_aff_scale_val(aff, m); + res = isl_aff_sub(res, aff); + + return res; +error: + isl_aff_free(aff); + isl_val_free(m); + return NULL; +} + +/* Compute + * + * pwaff mod m = pwaff - m * floor(pwaff/m) + */ +__isl_give isl_pw_aff *isl_pw_aff_mod(__isl_take isl_pw_aff *pwaff, isl_int m) +{ + isl_pw_aff *res; + + res = isl_pw_aff_copy(pwaff); + pwaff = isl_pw_aff_scale_down(pwaff, m); + pwaff = isl_pw_aff_floor(pwaff); + pwaff = isl_pw_aff_scale(pwaff, m); + res = isl_pw_aff_sub(res, pwaff); + + return res; +} + +/* Compute + * + * pa mod m = pa - m * floor(pa/m) + * + * with m an integer value. + */ +__isl_give isl_pw_aff *isl_pw_aff_mod_val(__isl_take isl_pw_aff *pa, + __isl_take isl_val *m) +{ + if (!pa || !m) + goto error; + if (!isl_val_is_int(m)) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "expecting integer modulo", goto error); + pa = isl_pw_aff_mod(pa, m->n); + isl_val_free(m); + return pa; +error: + isl_pw_aff_free(pa); + isl_val_free(m); + return NULL; +} + +/* Given f, return ceil(f). + * If f is an integer expression, then just return f. + * Otherwise, let f be the expression + * + * e/m + * + * then return + * + * floor((e + m - 1)/m) + * + * As a special case, ceil(NaN) = NaN. + */ +__isl_give isl_aff *isl_aff_ceil(__isl_take isl_aff *aff) +{ + if (!aff) + return NULL; + + if (isl_aff_is_nan(aff)) + return aff; + if (isl_int_is_one(aff->v->el[0])) + return aff; + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_add(aff->v->el[1], aff->v->el[1], aff->v->el[0]); + isl_int_sub_ui(aff->v->el[1], aff->v->el[1], 1); + aff = isl_aff_floor(aff); + + return aff; +} + +/* Apply the expansion computed by isl_merge_divs. + * The expansion itself is given by "exp" while the resulting + * list of divs is given by "div". + */ +__isl_give isl_aff *isl_aff_expand_divs(__isl_take isl_aff *aff, + __isl_take isl_mat *div, int *exp) +{ + int old_n_div; + int new_n_div; + int offset; + + aff = isl_aff_cow(aff); + if (!aff || !div) + goto error; + + old_n_div = isl_local_space_dim(aff->ls, isl_dim_div); + new_n_div = isl_mat_rows(div); + offset = 1 + isl_local_space_offset(aff->ls, isl_dim_div); + + aff->v = isl_vec_expand(aff->v, offset, old_n_div, exp, new_n_div); + aff->ls = isl_local_space_replace_divs(aff->ls, div); + if (!aff->v || !aff->ls) + return isl_aff_free(aff); + return aff; +error: + isl_aff_free(aff); + isl_mat_free(div); + return NULL; +} + +/* Add two affine expressions that live in the same local space. + */ +static __isl_give isl_aff *add_expanded(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + isl_int gcd, f; + + aff1 = isl_aff_cow(aff1); + if (!aff1 || !aff2) + goto error; + + aff1->v = isl_vec_cow(aff1->v); + if (!aff1->v) + goto error; + + isl_int_init(gcd); + isl_int_init(f); + isl_int_gcd(gcd, aff1->v->el[0], aff2->v->el[0]); + isl_int_divexact(f, aff2->v->el[0], gcd); + isl_seq_scale(aff1->v->el + 1, aff1->v->el + 1, f, aff1->v->size - 1); + isl_int_divexact(f, aff1->v->el[0], gcd); + isl_seq_addmul(aff1->v->el + 1, f, aff2->v->el + 1, aff1->v->size - 1); + isl_int_divexact(f, aff2->v->el[0], gcd); + isl_int_mul(aff1->v->el[0], aff1->v->el[0], f); + isl_int_clear(f); + isl_int_clear(gcd); + + isl_aff_free(aff2); + return aff1; +error: + isl_aff_free(aff1); + isl_aff_free(aff2); + return NULL; +} + +/* Return the sum of "aff1" and "aff2". + * + * If either of the two is NaN, then the result is NaN. + */ +__isl_give isl_aff *isl_aff_add(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + isl_ctx *ctx; + int *exp1 = NULL; + int *exp2 = NULL; + isl_mat *div; + int n_div1, n_div2; + + if (!aff1 || !aff2) + goto error; + + ctx = isl_aff_get_ctx(aff1); + if (!isl_space_is_equal(aff1->ls->dim, aff2->ls->dim)) + isl_die(ctx, isl_error_invalid, + "spaces don't match", goto error); + + if (isl_aff_is_nan(aff1)) { + isl_aff_free(aff2); + return aff1; + } + if (isl_aff_is_nan(aff2)) { + isl_aff_free(aff1); + return aff2; + } + + n_div1 = isl_aff_dim(aff1, isl_dim_div); + n_div2 = isl_aff_dim(aff2, isl_dim_div); + if (n_div1 == 0 && n_div2 == 0) + return add_expanded(aff1, aff2); + + exp1 = isl_alloc_array(ctx, int, n_div1); + exp2 = isl_alloc_array(ctx, int, n_div2); + if ((n_div1 && !exp1) || (n_div2 && !exp2)) + goto error; + + div = isl_merge_divs(aff1->ls->div, aff2->ls->div, exp1, exp2); + aff1 = isl_aff_expand_divs(aff1, isl_mat_copy(div), exp1); + aff2 = isl_aff_expand_divs(aff2, div, exp2); + free(exp1); + free(exp2); + + return add_expanded(aff1, aff2); +error: + free(exp1); + free(exp2); + isl_aff_free(aff1); + isl_aff_free(aff2); + return NULL; +} + +__isl_give isl_aff *isl_aff_sub(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + return isl_aff_add(aff1, isl_aff_neg(aff2)); +} + +/* Return the result of scaling "aff" by a factor of "f". + * + * As a special case, f * NaN = NaN. + */ +__isl_give isl_aff *isl_aff_scale(__isl_take isl_aff *aff, isl_int f) +{ + isl_int gcd; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + + if (isl_int_is_one(f)) + return aff; + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + if (isl_int_is_pos(f) && isl_int_is_divisible_by(aff->v->el[0], f)) { + isl_int_divexact(aff->v->el[0], aff->v->el[0], f); + return aff; + } + + isl_int_init(gcd); + isl_int_gcd(gcd, aff->v->el[0], f); + isl_int_divexact(aff->v->el[0], aff->v->el[0], gcd); + isl_int_divexact(gcd, f, gcd); + isl_seq_scale(aff->v->el + 1, aff->v->el + 1, gcd, aff->v->size - 1); + isl_int_clear(gcd); + + return aff; +} + +/* Multiple "aff" by "v". + */ +__isl_give isl_aff *isl_aff_scale_val(__isl_take isl_aff *aff, + __isl_take isl_val *v) +{ + if (!aff || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return aff; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting rational factor", goto error); + + aff = isl_aff_scale(aff, v->n); + aff = isl_aff_scale_down(aff, v->d); + + isl_val_free(v); + return aff; +error: + isl_aff_free(aff); + isl_val_free(v); + return NULL; +} + +/* Return the result of scaling "aff" down by a factor of "f". + * + * As a special case, NaN/f = NaN. + */ +__isl_give isl_aff *isl_aff_scale_down(__isl_take isl_aff *aff, isl_int f) +{ + isl_int gcd; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) + return aff; + + if (isl_int_is_one(f)) + return aff; + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + if (isl_int_is_zero(f)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot scale down by zero", return isl_aff_free(aff)); + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + isl_int_init(gcd); + isl_seq_gcd(aff->v->el + 1, aff->v->size - 1, &gcd); + isl_int_gcd(gcd, gcd, f); + isl_seq_scale_down(aff->v->el + 1, aff->v->el + 1, gcd, aff->v->size - 1); + isl_int_divexact(gcd, f, gcd); + isl_int_mul(aff->v->el[0], aff->v->el[0], gcd); + isl_int_clear(gcd); + + return aff; +} + +/* Divide "aff" by "v". + */ +__isl_give isl_aff *isl_aff_scale_down_val(__isl_take isl_aff *aff, + __isl_take isl_val *v) +{ + if (!aff || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return aff; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting rational factor", goto error); + if (!isl_val_is_pos(v)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "factor needs to be positive", goto error); + + aff = isl_aff_scale(aff, v->d); + aff = isl_aff_scale_down(aff, v->n); + + isl_val_free(v); + return aff; +error: + isl_aff_free(aff); + isl_val_free(v); + return NULL; +} + +__isl_give isl_aff *isl_aff_scale_down_ui(__isl_take isl_aff *aff, unsigned f) +{ + isl_int v; + + if (f == 1) + return aff; + + isl_int_init(v); + isl_int_set_ui(v, f); + aff = isl_aff_scale_down(aff, v); + isl_int_clear(v); + + return aff; +} + +__isl_give isl_aff *isl_aff_set_dim_name(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned pos, const char *s) +{ + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "cannot set name of output/set dimension", + return isl_aff_free(aff)); + if (type == isl_dim_in) + type = isl_dim_set; + aff->ls = isl_local_space_set_dim_name(aff->ls, type, pos, s); + if (!aff->ls) + return isl_aff_free(aff); + + return aff; +} + +__isl_give isl_aff *isl_aff_set_dim_id(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + aff = isl_aff_cow(aff); + if (!aff) + goto error; + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "cannot set name of output/set dimension", + goto error); + if (type == isl_dim_in) + type = isl_dim_set; + aff->ls = isl_local_space_set_dim_id(aff->ls, type, pos, id); + if (!aff->ls) + return isl_aff_free(aff); + + return aff; +error: + isl_id_free(id); + isl_aff_free(aff); + return NULL; +} + +/* Replace the identifier of the input tuple of "aff" by "id". + * type is currently required to be equal to isl_dim_in + */ +__isl_give isl_aff *isl_aff_set_tuple_id(__isl_take isl_aff *aff, + enum isl_dim_type type, __isl_take isl_id *id) +{ + aff = isl_aff_cow(aff); + if (!aff) + goto error; + if (type != isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "cannot only set id of input tuple", goto error); + aff->ls = isl_local_space_set_tuple_id(aff->ls, isl_dim_set, id); + if (!aff->ls) + return isl_aff_free(aff); + + return aff; +error: + isl_id_free(id); + isl_aff_free(aff); + return NULL; +} + +/* Exploit the equalities in "eq" to simplify the affine expression + * and the expressions of the integer divisions in the local space. + * The integer divisions in this local space are assumed to appear + * as regular dimensions in "eq". + */ +static __isl_give isl_aff *isl_aff_substitute_equalities_lifted( + __isl_take isl_aff *aff, __isl_take isl_basic_set *eq) +{ + int i, j; + unsigned total; + unsigned n_div; + + if (!eq) + goto error; + if (eq->n_eq == 0) { + isl_basic_set_free(eq); + return aff; + } + + aff = isl_aff_cow(aff); + if (!aff) + goto error; + + aff->ls = isl_local_space_substitute_equalities(aff->ls, + isl_basic_set_copy(eq)); + aff->v = isl_vec_cow(aff->v); + if (!aff->ls || !aff->v) + goto error; + + total = 1 + isl_space_dim(eq->dim, isl_dim_all); + n_div = eq->n_div; + for (i = 0; i < eq->n_eq; ++i) { + j = isl_seq_last_non_zero(eq->eq[i], total + n_div); + if (j < 0 || j == 0 || j >= total) + continue; + + isl_seq_elim(aff->v->el + 1, eq->eq[i], j, total, + &aff->v->el[0]); + } + + isl_basic_set_free(eq); + aff = isl_aff_normalize(aff); + return aff; +error: + isl_basic_set_free(eq); + isl_aff_free(aff); + return NULL; +} + +/* Exploit the equalities in "eq" to simplify the affine expression + * and the expressions of the integer divisions in the local space. + */ +__isl_give isl_aff *isl_aff_substitute_equalities(__isl_take isl_aff *aff, + __isl_take isl_basic_set *eq) +{ + int n_div; + + if (!aff || !eq) + goto error; + n_div = isl_local_space_dim(aff->ls, isl_dim_div); + if (n_div > 0) + eq = isl_basic_set_add_dims(eq, isl_dim_set, n_div); + return isl_aff_substitute_equalities_lifted(aff, eq); +error: + isl_basic_set_free(eq); + isl_aff_free(aff); + return NULL; +} + +/* Look for equalities among the variables shared by context and aff + * and the integer divisions of aff, if any. + * The equalities are then used to eliminate coefficients and/or integer + * divisions from aff. + */ +__isl_give isl_aff *isl_aff_gist(__isl_take isl_aff *aff, + __isl_take isl_set *context) +{ + isl_basic_set *hull; + int n_div; + + if (!aff) + goto error; + n_div = isl_local_space_dim(aff->ls, isl_dim_div); + if (n_div > 0) { + isl_basic_set *bset; + isl_local_space *ls; + context = isl_set_add_dims(context, isl_dim_set, n_div); + ls = isl_aff_get_domain_local_space(aff); + bset = isl_basic_set_from_local_space(ls); + bset = isl_basic_set_lift(bset); + bset = isl_basic_set_flatten(bset); + context = isl_set_intersect(context, + isl_set_from_basic_set(bset)); + } + + hull = isl_set_affine_hull(context); + return isl_aff_substitute_equalities_lifted(aff, hull); +error: + isl_aff_free(aff); + isl_set_free(context); + return NULL; +} + +__isl_give isl_aff *isl_aff_gist_params(__isl_take isl_aff *aff, + __isl_take isl_set *context) +{ + isl_set *dom_context = isl_set_universe(isl_aff_get_domain_space(aff)); + dom_context = isl_set_intersect_params(dom_context, context); + return isl_aff_gist(aff, dom_context); +} + +/* Return a basic set containing those elements in the space + * of aff where it is positive. "rational" should not be set. + * + * If "aff" is NaN, then it is not positive. + */ +static __isl_give isl_basic_set *aff_pos_basic_set(__isl_take isl_aff *aff, + int rational) +{ + isl_constraint *ineq; + isl_basic_set *bset; + isl_val *c; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) { + isl_space *space = isl_aff_get_domain_space(aff); + isl_aff_free(aff); + return isl_basic_set_empty(space); + } + if (rational) + isl_die(isl_aff_get_ctx(aff), isl_error_unsupported, + "rational sets not supported", goto error); + + ineq = isl_inequality_from_aff(aff); + c = isl_constraint_get_constant_val(ineq); + c = isl_val_sub_ui(c, 1); + ineq = isl_constraint_set_constant_val(ineq, c); + + bset = isl_basic_set_from_constraint(ineq); + bset = isl_basic_set_simplify(bset); + return bset; +error: + isl_aff_free(aff); + return NULL; +} + +/* Return a basic set containing those elements in the space + * of aff where it is non-negative. + * If "rational" is set, then return a rational basic set. + * + * If "aff" is NaN, then it is not non-negative (it's not negative either). + */ +static __isl_give isl_basic_set *aff_nonneg_basic_set( + __isl_take isl_aff *aff, int rational) +{ + isl_constraint *ineq; + isl_basic_set *bset; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) { + isl_space *space = isl_aff_get_domain_space(aff); + isl_aff_free(aff); + return isl_basic_set_empty(space); + } + + ineq = isl_inequality_from_aff(aff); + + bset = isl_basic_set_from_constraint(ineq); + if (rational) + bset = isl_basic_set_set_rational(bset); + bset = isl_basic_set_simplify(bset); + return bset; +} + +/* Return a basic set containing those elements in the space + * of aff where it is non-negative. + */ +__isl_give isl_basic_set *isl_aff_nonneg_basic_set(__isl_take isl_aff *aff) +{ + return aff_nonneg_basic_set(aff, 0); +} + +/* Return a basic set containing those elements in the domain space + * of aff where it is negative. + */ +__isl_give isl_basic_set *isl_aff_neg_basic_set(__isl_take isl_aff *aff) +{ + aff = isl_aff_neg(aff); + aff = isl_aff_add_constant_num_si(aff, -1); + return isl_aff_nonneg_basic_set(aff); +} + +/* Return a basic set containing those elements in the space + * of aff where it is zero. + * If "rational" is set, then return a rational basic set. + * + * If "aff" is NaN, then it is not zero. + */ +static __isl_give isl_basic_set *aff_zero_basic_set(__isl_take isl_aff *aff, + int rational) +{ + isl_constraint *ineq; + isl_basic_set *bset; + + if (!aff) + return NULL; + if (isl_aff_is_nan(aff)) { + isl_space *space = isl_aff_get_domain_space(aff); + isl_aff_free(aff); + return isl_basic_set_empty(space); + } + + ineq = isl_equality_from_aff(aff); + + bset = isl_basic_set_from_constraint(ineq); + if (rational) + bset = isl_basic_set_set_rational(bset); + bset = isl_basic_set_simplify(bset); + return bset; +} + +/* Return a basic set containing those elements in the space + * of aff where it is zero. + */ +__isl_give isl_basic_set *isl_aff_zero_basic_set(__isl_take isl_aff *aff) +{ + return aff_zero_basic_set(aff, 0); +} + +/* Return a basic set containing those elements in the shared space + * of aff1 and aff2 where aff1 is greater than or equal to aff2. + */ +__isl_give isl_basic_set *isl_aff_ge_basic_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + aff1 = isl_aff_sub(aff1, aff2); + + return isl_aff_nonneg_basic_set(aff1); +} + +/* Return a set containing those elements in the shared space + * of aff1 and aff2 where aff1 is greater than or equal to aff2. + */ +__isl_give isl_set *isl_aff_ge_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + return isl_set_from_basic_set(isl_aff_ge_basic_set(aff1, aff2)); +} + +/* Return a basic set containing those elements in the shared space + * of aff1 and aff2 where aff1 is smaller than or equal to aff2. + */ +__isl_give isl_basic_set *isl_aff_le_basic_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + return isl_aff_ge_basic_set(aff2, aff1); +} + +/* Return a set containing those elements in the shared space + * of aff1 and aff2 where aff1 is smaller than or equal to aff2. + */ +__isl_give isl_set *isl_aff_le_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + return isl_aff_ge_set(aff2, aff1); +} + +/* Return a basic set containing those elements in the shared space + * of aff1 and aff2 where aff1 and aff2 are equal. + */ +__isl_give isl_basic_set *isl_aff_eq_basic_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + aff1 = isl_aff_sub(aff1, aff2); + + return isl_aff_zero_basic_set(aff1); +} + +/* Return a set containing those elements in the shared space + * of aff1 and aff2 where aff1 and aff2 are equal. + */ +__isl_give isl_set *isl_aff_eq_set(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + return isl_set_from_basic_set(isl_aff_eq_basic_set(aff1, aff2)); +} + +__isl_give isl_aff *isl_aff_add_on_domain(__isl_keep isl_set *dom, + __isl_take isl_aff *aff1, __isl_take isl_aff *aff2) +{ + aff1 = isl_aff_add(aff1, aff2); + aff1 = isl_aff_gist(aff1, isl_set_copy(dom)); + return aff1; +} + +int isl_aff_is_empty(__isl_keep isl_aff *aff) +{ + if (!aff) + return -1; + + return 0; +} + +/* Check whether the given affine expression has non-zero coefficient + * for any dimension in the given range or if any of these dimensions + * appear with non-zero coefficients in any of the integer divisions + * involved in the affine expression. + */ +isl_bool isl_aff_involves_dims(__isl_keep isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + isl_ctx *ctx; + int *active = NULL; + isl_bool involves = isl_bool_false; + + if (!aff) + return isl_bool_error; + if (n == 0) + return isl_bool_false; + + ctx = isl_aff_get_ctx(aff); + if (first + n > isl_aff_dim(aff, type)) + isl_die(ctx, isl_error_invalid, + "range out of bounds", return isl_bool_error); + + active = isl_local_space_get_active(aff->ls, aff->v->el + 2); + if (!active) + goto error; + + first += isl_local_space_offset(aff->ls, type) - 1; + for (i = 0; i < n; ++i) + if (active[first + i]) { + involves = isl_bool_true; + break; + } + + free(active); + + return involves; +error: + free(active); + return isl_bool_error; +} + +__isl_give isl_aff *isl_aff_drop_dims(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_ctx *ctx; + + if (!aff) + return NULL; + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "cannot drop output/set dimension", + return isl_aff_free(aff)); + if (type == isl_dim_in) + type = isl_dim_set; + if (n == 0 && !isl_local_space_is_named_or_nested(aff->ls, type)) + return aff; + + ctx = isl_aff_get_ctx(aff); + if (first + n > isl_local_space_dim(aff->ls, type)) + isl_die(ctx, isl_error_invalid, "range out of bounds", + return isl_aff_free(aff)); + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->ls = isl_local_space_drop_dims(aff->ls, type, first, n); + if (!aff->ls) + return isl_aff_free(aff); + + first += 1 + isl_local_space_offset(aff->ls, type); + aff->v = isl_vec_drop_els(aff->v, first, n); + if (!aff->v) + return isl_aff_free(aff); + + return aff; +} + +/* Project the domain of the affine expression onto its parameter space. + * The affine expression may not involve any of the domain dimensions. + */ +__isl_give isl_aff *isl_aff_project_domain_on_params(__isl_take isl_aff *aff) +{ + isl_space *space; + unsigned n; + int involves; + + n = isl_aff_dim(aff, isl_dim_in); + involves = isl_aff_involves_dims(aff, isl_dim_in, 0, n); + if (involves < 0) + return isl_aff_free(aff); + if (involves) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "affine expression involves some of the domain dimensions", + return isl_aff_free(aff)); + aff = isl_aff_drop_dims(aff, isl_dim_in, 0, n); + space = isl_aff_get_domain_space(aff); + space = isl_space_params(space); + aff = isl_aff_reset_domain_space(aff, space); + return aff; +} + +__isl_give isl_aff *isl_aff_insert_dims(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_ctx *ctx; + + if (!aff) + return NULL; + if (type == isl_dim_out) + isl_die(aff->v->ctx, isl_error_invalid, + "cannot insert output/set dimensions", + return isl_aff_free(aff)); + if (type == isl_dim_in) + type = isl_dim_set; + if (n == 0 && !isl_local_space_is_named_or_nested(aff->ls, type)) + return aff; + + ctx = isl_aff_get_ctx(aff); + if (first > isl_local_space_dim(aff->ls, type)) + isl_die(ctx, isl_error_invalid, "position out of bounds", + return isl_aff_free(aff)); + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->ls = isl_local_space_insert_dims(aff->ls, type, first, n); + if (!aff->ls) + return isl_aff_free(aff); + + first += 1 + isl_local_space_offset(aff->ls, type); + aff->v = isl_vec_insert_zero_els(aff->v, first, n); + if (!aff->v) + return isl_aff_free(aff); + + return aff; +} + +__isl_give isl_aff *isl_aff_add_dims(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned n) +{ + unsigned pos; + + pos = isl_aff_dim(aff, type); + + return isl_aff_insert_dims(aff, type, pos, n); +} + +__isl_give isl_pw_aff *isl_pw_aff_add_dims(__isl_take isl_pw_aff *pwaff, + enum isl_dim_type type, unsigned n) +{ + unsigned pos; + + pos = isl_pw_aff_dim(pwaff, type); + + return isl_pw_aff_insert_dims(pwaff, type, pos, n); +} + +/* Move the "n" dimensions of "src_type" starting at "src_pos" of "aff" + * to dimensions of "dst_type" at "dst_pos". + * + * We only support moving input dimensions to parameters and vice versa. + */ +__isl_give isl_aff *isl_aff_move_dims(__isl_take isl_aff *aff, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + unsigned g_dst_pos; + unsigned g_src_pos; + + if (!aff) + return NULL; + if (n == 0 && + !isl_local_space_is_named_or_nested(aff->ls, src_type) && + !isl_local_space_is_named_or_nested(aff->ls, dst_type)) + return aff; + + if (dst_type == isl_dim_out || src_type == isl_dim_out) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot move output/set dimension", + return isl_aff_free(aff)); + if (dst_type == isl_dim_div || src_type == isl_dim_div) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot move divs", return isl_aff_free(aff)); + if (dst_type == isl_dim_in) + dst_type = isl_dim_set; + if (src_type == isl_dim_in) + src_type = isl_dim_set; + + if (src_pos + n > isl_local_space_dim(aff->ls, src_type)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "range out of bounds", return isl_aff_free(aff)); + if (dst_type == src_type) + isl_die(isl_aff_get_ctx(aff), isl_error_unsupported, + "moving dims within the same type not supported", + return isl_aff_free(aff)); + + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + g_src_pos = 1 + isl_local_space_offset(aff->ls, src_type) + src_pos; + g_dst_pos = 1 + isl_local_space_offset(aff->ls, dst_type) + dst_pos; + if (dst_type > src_type) + g_dst_pos -= n; + + aff->v = isl_vec_move_els(aff->v, g_dst_pos, g_src_pos, n); + aff->ls = isl_local_space_move_dims(aff->ls, dst_type, dst_pos, + src_type, src_pos, n); + if (!aff->v || !aff->ls) + return isl_aff_free(aff); + + aff = sort_divs(aff); + + return aff; +} + +__isl_give isl_pw_aff *isl_pw_aff_from_aff(__isl_take isl_aff *aff) +{ + isl_set *dom = isl_set_universe(isl_aff_get_domain_space(aff)); + return isl_pw_aff_alloc(dom, aff); +} + +#undef PW +#define PW isl_pw_aff +#undef EL +#define EL isl_aff +#undef EL_IS_ZERO +#define EL_IS_ZERO is_empty +#undef ZERO +#define ZERO empty +#undef IS_ZERO +#define IS_ZERO is_empty +#undef FIELD +#define FIELD aff +#undef DEFAULT_IS_ZERO +#define DEFAULT_IS_ZERO 0 + +#define NO_EVAL +#define NO_OPT +#define NO_LIFT +#define NO_MORPH + +#include +#include +#include + +#undef UNION +#define UNION isl_union_pw_aff +#undef PART +#define PART isl_pw_aff +#undef PARTS +#define PARTS pw_aff + +#include +#include + +static __isl_give isl_set *align_params_pw_pw_set_and( + __isl_take isl_pw_aff *pwaff1, __isl_take isl_pw_aff *pwaff2, + __isl_give isl_set *(*fn)(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2)) +{ + if (!pwaff1 || !pwaff2) + goto error; + if (isl_space_match(pwaff1->dim, isl_dim_param, + pwaff2->dim, isl_dim_param)) + return fn(pwaff1, pwaff2); + if (!isl_space_has_named_params(pwaff1->dim) || + !isl_space_has_named_params(pwaff2->dim)) + isl_die(isl_pw_aff_get_ctx(pwaff1), isl_error_invalid, + "unaligned unnamed parameters", goto error); + pwaff1 = isl_pw_aff_align_params(pwaff1, isl_pw_aff_get_space(pwaff2)); + pwaff2 = isl_pw_aff_align_params(pwaff2, isl_pw_aff_get_space(pwaff1)); + return fn(pwaff1, pwaff2); +error: + isl_pw_aff_free(pwaff1); + isl_pw_aff_free(pwaff2); + return NULL; +} + +/* Align the parameters of the to isl_pw_aff arguments and + * then apply a function "fn" on them that returns an isl_map. + */ +static __isl_give isl_map *align_params_pw_pw_map_and( + __isl_take isl_pw_aff *pa1, __isl_take isl_pw_aff *pa2, + __isl_give isl_map *(*fn)(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2)) +{ + if (!pa1 || !pa2) + goto error; + if (isl_space_match(pa1->dim, isl_dim_param, pa2->dim, isl_dim_param)) + return fn(pa1, pa2); + if (!isl_space_has_named_params(pa1->dim) || + !isl_space_has_named_params(pa2->dim)) + isl_die(isl_pw_aff_get_ctx(pa1), isl_error_invalid, + "unaligned unnamed parameters", goto error); + pa1 = isl_pw_aff_align_params(pa1, isl_pw_aff_get_space(pa2)); + pa2 = isl_pw_aff_align_params(pa2, isl_pw_aff_get_space(pa1)); + return fn(pa1, pa2); +error: + isl_pw_aff_free(pa1); + isl_pw_aff_free(pa2); + return NULL; +} + +/* Compute a piecewise quasi-affine expression with a domain that + * is the union of those of pwaff1 and pwaff2 and such that on each + * cell, the quasi-affine expression is the maximum of those of pwaff1 + * and pwaff2. If only one of pwaff1 or pwaff2 is defined on a given + * cell, then the associated expression is the defined one. + */ +static __isl_give isl_pw_aff *pw_aff_union_max(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_union_opt_cmp(pwaff1, pwaff2, &isl_aff_ge_set); +} + +__isl_give isl_pw_aff *isl_pw_aff_union_max(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_align_params_pw_pw_and(pwaff1, pwaff2, + &pw_aff_union_max); +} + +/* Compute a piecewise quasi-affine expression with a domain that + * is the union of those of pwaff1 and pwaff2 and such that on each + * cell, the quasi-affine expression is the minimum of those of pwaff1 + * and pwaff2. If only one of pwaff1 or pwaff2 is defined on a given + * cell, then the associated expression is the defined one. + */ +static __isl_give isl_pw_aff *pw_aff_union_min(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_union_opt_cmp(pwaff1, pwaff2, &isl_aff_le_set); +} + +__isl_give isl_pw_aff *isl_pw_aff_union_min(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_align_params_pw_pw_and(pwaff1, pwaff2, + &pw_aff_union_min); +} + +__isl_give isl_pw_aff *isl_pw_aff_union_opt(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2, int max) +{ + if (max) + return isl_pw_aff_union_max(pwaff1, pwaff2); + else + return isl_pw_aff_union_min(pwaff1, pwaff2); +} + +/* Construct a map with as domain the domain of pwaff and + * one-dimensional range corresponding to the affine expressions. + */ +static __isl_give isl_map *map_from_pw_aff(__isl_take isl_pw_aff *pwaff) +{ + int i; + isl_space *dim; + isl_map *map; + + if (!pwaff) + return NULL; + + dim = isl_pw_aff_get_space(pwaff); + map = isl_map_empty(dim); + + for (i = 0; i < pwaff->n; ++i) { + isl_basic_map *bmap; + isl_map *map_i; + + bmap = isl_basic_map_from_aff(isl_aff_copy(pwaff->p[i].aff)); + map_i = isl_map_from_basic_map(bmap); + map_i = isl_map_intersect_domain(map_i, + isl_set_copy(pwaff->p[i].set)); + map = isl_map_union_disjoint(map, map_i); + } + + isl_pw_aff_free(pwaff); + + return map; +} + +/* Construct a map with as domain the domain of pwaff and + * one-dimensional range corresponding to the affine expressions. + */ +__isl_give isl_map *isl_map_from_pw_aff(__isl_take isl_pw_aff *pwaff) +{ + if (!pwaff) + return NULL; + if (isl_space_is_set(pwaff->dim)) + isl_die(isl_pw_aff_get_ctx(pwaff), isl_error_invalid, + "space of input is not a map", goto error); + return map_from_pw_aff(pwaff); +error: + isl_pw_aff_free(pwaff); + return NULL; +} + +/* Construct a one-dimensional set with as parameter domain + * the domain of pwaff and the single set dimension + * corresponding to the affine expressions. + */ +__isl_give isl_set *isl_set_from_pw_aff(__isl_take isl_pw_aff *pwaff) +{ + if (!pwaff) + return NULL; + if (!isl_space_is_set(pwaff->dim)) + isl_die(isl_pw_aff_get_ctx(pwaff), isl_error_invalid, + "space of input is not a set", goto error); + return map_from_pw_aff(pwaff); +error: + isl_pw_aff_free(pwaff); + return NULL; +} + +/* Return a set containing those elements in the domain + * of "pwaff" where it satisfies "fn" (if complement is 0) or + * does not satisfy "fn" (if complement is 1). + * + * The pieces with a NaN never belong to the result since + * NaN does not satisfy any property. + */ +static __isl_give isl_set *pw_aff_locus(__isl_take isl_pw_aff *pwaff, + __isl_give isl_basic_set *(*fn)(__isl_take isl_aff *aff, int rational), + int complement) +{ + int i; + isl_set *set; + + if (!pwaff) + return NULL; + + set = isl_set_empty(isl_pw_aff_get_domain_space(pwaff)); + + for (i = 0; i < pwaff->n; ++i) { + isl_basic_set *bset; + isl_set *set_i, *locus; + int rational; + + if (isl_aff_is_nan(pwaff->p[i].aff)) + continue; + + rational = isl_set_has_rational(pwaff->p[i].set); + bset = fn(isl_aff_copy(pwaff->p[i].aff), rational); + locus = isl_set_from_basic_set(bset); + set_i = isl_set_copy(pwaff->p[i].set); + if (complement) + set_i = isl_set_subtract(set_i, locus); + else + set_i = isl_set_intersect(set_i, locus); + set = isl_set_union_disjoint(set, set_i); + } + + isl_pw_aff_free(pwaff); + + return set; +} + +/* Return a set containing those elements in the domain + * of "pa" where it is positive. + */ +__isl_give isl_set *isl_pw_aff_pos_set(__isl_take isl_pw_aff *pa) +{ + return pw_aff_locus(pa, &aff_pos_basic_set, 0); +} + +/* Return a set containing those elements in the domain + * of pwaff where it is non-negative. + */ +__isl_give isl_set *isl_pw_aff_nonneg_set(__isl_take isl_pw_aff *pwaff) +{ + return pw_aff_locus(pwaff, &aff_nonneg_basic_set, 0); +} + +/* Return a set containing those elements in the domain + * of pwaff where it is zero. + */ +__isl_give isl_set *isl_pw_aff_zero_set(__isl_take isl_pw_aff *pwaff) +{ + return pw_aff_locus(pwaff, &aff_zero_basic_set, 0); +} + +/* Return a set containing those elements in the domain + * of pwaff where it is not zero. + */ +__isl_give isl_set *isl_pw_aff_non_zero_set(__isl_take isl_pw_aff *pwaff) +{ + return pw_aff_locus(pwaff, &aff_zero_basic_set, 1); +} + +/* Return a set containing those elements in the shared domain + * of pwaff1 and pwaff2 where pwaff1 is greater than (or equal) to pwaff2. + * + * We compute the difference on the shared domain and then construct + * the set of values where this difference is non-negative. + * If strict is set, we first subtract 1 from the difference. + * If equal is set, we only return the elements where pwaff1 and pwaff2 + * are equal. + */ +static __isl_give isl_set *pw_aff_gte_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2, int strict, int equal) +{ + isl_set *set1, *set2; + + set1 = isl_pw_aff_domain(isl_pw_aff_copy(pwaff1)); + set2 = isl_pw_aff_domain(isl_pw_aff_copy(pwaff2)); + set1 = isl_set_intersect(set1, set2); + pwaff1 = isl_pw_aff_intersect_domain(pwaff1, isl_set_copy(set1)); + pwaff2 = isl_pw_aff_intersect_domain(pwaff2, isl_set_copy(set1)); + pwaff1 = isl_pw_aff_add(pwaff1, isl_pw_aff_neg(pwaff2)); + + if (strict) { + isl_space *dim = isl_set_get_space(set1); + isl_aff *aff; + aff = isl_aff_zero_on_domain(isl_local_space_from_space(dim)); + aff = isl_aff_add_constant_si(aff, -1); + pwaff1 = isl_pw_aff_add(pwaff1, isl_pw_aff_alloc(set1, aff)); + } else + isl_set_free(set1); + + if (equal) + return isl_pw_aff_zero_set(pwaff1); + return isl_pw_aff_nonneg_set(pwaff1); +} + +/* Return a set containing those elements in the shared domain + * of pwaff1 and pwaff2 where pwaff1 is equal to pwaff2. + */ +static __isl_give isl_set *pw_aff_eq_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return pw_aff_gte_set(pwaff1, pwaff2, 0, 1); +} + +__isl_give isl_set *isl_pw_aff_eq_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return align_params_pw_pw_set_and(pwaff1, pwaff2, &pw_aff_eq_set); +} + +/* Return a set containing those elements in the shared domain + * of pwaff1 and pwaff2 where pwaff1 is greater than or equal to pwaff2. + */ +static __isl_give isl_set *pw_aff_ge_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return pw_aff_gte_set(pwaff1, pwaff2, 0, 0); +} + +__isl_give isl_set *isl_pw_aff_ge_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return align_params_pw_pw_set_and(pwaff1, pwaff2, &pw_aff_ge_set); +} + +/* Return a set containing those elements in the shared domain + * of pwaff1 and pwaff2 where pwaff1 is strictly greater than pwaff2. + */ +static __isl_give isl_set *pw_aff_gt_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return pw_aff_gte_set(pwaff1, pwaff2, 1, 0); +} + +__isl_give isl_set *isl_pw_aff_gt_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return align_params_pw_pw_set_and(pwaff1, pwaff2, &pw_aff_gt_set); +} + +__isl_give isl_set *isl_pw_aff_le_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_ge_set(pwaff2, pwaff1); +} + +__isl_give isl_set *isl_pw_aff_lt_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_gt_set(pwaff2, pwaff1); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function values are ordered in the same way as "order", + * which returns a set in the shared domain of its two arguments. + * The parameters of "pa1" and "pa2" are assumed to have been aligned. + * + * Let "pa1" and "pa2" be defined on domains A and B respectively. + * We first pull back the two functions such that they are defined on + * the domain [A -> B]. Then we apply "order", resulting in a set + * in the space [A -> B]. Finally, we unwrap this set to obtain + * a map in the space A -> B. + */ +static __isl_give isl_map *isl_pw_aff_order_map_aligned( + __isl_take isl_pw_aff *pa1, __isl_take isl_pw_aff *pa2, + __isl_give isl_set *(*order)(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2)) +{ + isl_space *space1, *space2; + isl_multi_aff *ma; + isl_set *set; + + space1 = isl_space_domain(isl_pw_aff_get_space(pa1)); + space2 = isl_space_domain(isl_pw_aff_get_space(pa2)); + space1 = isl_space_map_from_domain_and_range(space1, space2); + ma = isl_multi_aff_domain_map(isl_space_copy(space1)); + pa1 = isl_pw_aff_pullback_multi_aff(pa1, ma); + ma = isl_multi_aff_range_map(space1); + pa2 = isl_pw_aff_pullback_multi_aff(pa2, ma); + set = order(pa1, pa2); + + return isl_set_unwrap(set); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function values are equal. + * The parameters of "pa1" and "pa2" are assumed to have been aligned. + */ +static __isl_give isl_map *isl_pw_aff_eq_map_aligned(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return isl_pw_aff_order_map_aligned(pa1, pa2, &isl_pw_aff_eq_set); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function values are equal. + */ +__isl_give isl_map *isl_pw_aff_eq_map(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return align_params_pw_pw_map_and(pa1, pa2, &isl_pw_aff_eq_map_aligned); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function value of "pa1" is less than the function value of "pa2". + * The parameters of "pa1" and "pa2" are assumed to have been aligned. + */ +static __isl_give isl_map *isl_pw_aff_lt_map_aligned(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return isl_pw_aff_order_map_aligned(pa1, pa2, &isl_pw_aff_lt_set); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function value of "pa1" is less than the function value of "pa2". + */ +__isl_give isl_map *isl_pw_aff_lt_map(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return align_params_pw_pw_map_and(pa1, pa2, &isl_pw_aff_lt_map_aligned); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function value of "pa1" is greater than the function value + * of "pa2". + * The parameters of "pa1" and "pa2" are assumed to have been aligned. + */ +static __isl_give isl_map *isl_pw_aff_gt_map_aligned(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return isl_pw_aff_order_map_aligned(pa1, pa2, &isl_pw_aff_gt_set); +} + +/* Return a map containing pairs of elements in the domains of "pa1" and "pa2" + * where the function value of "pa1" is greater than the function value + * of "pa2". + */ +__isl_give isl_map *isl_pw_aff_gt_map(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return align_params_pw_pw_map_and(pa1, pa2, &isl_pw_aff_gt_map_aligned); +} + +/* Return a set containing those elements in the shared domain + * of the elements of list1 and list2 where each element in list1 + * has the relation specified by "fn" with each element in list2. + */ +static __isl_give isl_set *pw_aff_list_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2, + __isl_give isl_set *(*fn)(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2)) +{ + int i, j; + isl_ctx *ctx; + isl_set *set; + + if (!list1 || !list2) + goto error; + + ctx = isl_pw_aff_list_get_ctx(list1); + if (list1->n < 1 || list2->n < 1) + isl_die(ctx, isl_error_invalid, + "list should contain at least one element", goto error); + + set = isl_set_universe(isl_pw_aff_get_domain_space(list1->p[0])); + for (i = 0; i < list1->n; ++i) + for (j = 0; j < list2->n; ++j) { + isl_set *set_ij; + + set_ij = fn(isl_pw_aff_copy(list1->p[i]), + isl_pw_aff_copy(list2->p[j])); + set = isl_set_intersect(set, set_ij); + } + + isl_pw_aff_list_free(list1); + isl_pw_aff_list_free(list2); + return set; +error: + isl_pw_aff_list_free(list1); + isl_pw_aff_list_free(list2); + return NULL; +} + +/* Return a set containing those elements in the shared domain + * of the elements of list1 and list2 where each element in list1 + * is equal to each element in list2. + */ +__isl_give isl_set *isl_pw_aff_list_eq_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2) +{ + return pw_aff_list_set(list1, list2, &isl_pw_aff_eq_set); +} + +__isl_give isl_set *isl_pw_aff_list_ne_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2) +{ + return pw_aff_list_set(list1, list2, &isl_pw_aff_ne_set); +} + +/* Return a set containing those elements in the shared domain + * of the elements of list1 and list2 where each element in list1 + * is less than or equal to each element in list2. + */ +__isl_give isl_set *isl_pw_aff_list_le_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2) +{ + return pw_aff_list_set(list1, list2, &isl_pw_aff_le_set); +} + +__isl_give isl_set *isl_pw_aff_list_lt_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2) +{ + return pw_aff_list_set(list1, list2, &isl_pw_aff_lt_set); +} + +__isl_give isl_set *isl_pw_aff_list_ge_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2) +{ + return pw_aff_list_set(list1, list2, &isl_pw_aff_ge_set); +} + +__isl_give isl_set *isl_pw_aff_list_gt_set(__isl_take isl_pw_aff_list *list1, + __isl_take isl_pw_aff_list *list2) +{ + return pw_aff_list_set(list1, list2, &isl_pw_aff_gt_set); +} + + +/* Return a set containing those elements in the shared domain + * of pwaff1 and pwaff2 where pwaff1 is not equal to pwaff2. + */ +static __isl_give isl_set *pw_aff_ne_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + isl_set *set_lt, *set_gt; + + set_lt = isl_pw_aff_lt_set(isl_pw_aff_copy(pwaff1), + isl_pw_aff_copy(pwaff2)); + set_gt = isl_pw_aff_gt_set(pwaff1, pwaff2); + return isl_set_union_disjoint(set_lt, set_gt); +} + +__isl_give isl_set *isl_pw_aff_ne_set(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return align_params_pw_pw_set_and(pwaff1, pwaff2, &pw_aff_ne_set); +} + +__isl_give isl_pw_aff *isl_pw_aff_scale_down(__isl_take isl_pw_aff *pwaff, + isl_int v) +{ + int i; + + if (isl_int_is_one(v)) + return pwaff; + if (!isl_int_is_pos(v)) + isl_die(isl_pw_aff_get_ctx(pwaff), isl_error_invalid, + "factor needs to be positive", + return isl_pw_aff_free(pwaff)); + pwaff = isl_pw_aff_cow(pwaff); + if (!pwaff) + return NULL; + if (pwaff->n == 0) + return pwaff; + + for (i = 0; i < pwaff->n; ++i) { + pwaff->p[i].aff = isl_aff_scale_down(pwaff->p[i].aff, v); + if (!pwaff->p[i].aff) + return isl_pw_aff_free(pwaff); + } + + return pwaff; +} + +__isl_give isl_pw_aff *isl_pw_aff_floor(__isl_take isl_pw_aff *pwaff) +{ + int i; + + pwaff = isl_pw_aff_cow(pwaff); + if (!pwaff) + return NULL; + if (pwaff->n == 0) + return pwaff; + + for (i = 0; i < pwaff->n; ++i) { + pwaff->p[i].aff = isl_aff_floor(pwaff->p[i].aff); + if (!pwaff->p[i].aff) + return isl_pw_aff_free(pwaff); + } + + return pwaff; +} + +__isl_give isl_pw_aff *isl_pw_aff_ceil(__isl_take isl_pw_aff *pwaff) +{ + int i; + + pwaff = isl_pw_aff_cow(pwaff); + if (!pwaff) + return NULL; + if (pwaff->n == 0) + return pwaff; + + for (i = 0; i < pwaff->n; ++i) { + pwaff->p[i].aff = isl_aff_ceil(pwaff->p[i].aff); + if (!pwaff->p[i].aff) + return isl_pw_aff_free(pwaff); + } + + return pwaff; +} + +/* Assuming that "cond1" and "cond2" are disjoint, + * return an affine expression that is equal to pwaff1 on cond1 + * and to pwaff2 on cond2. + */ +static __isl_give isl_pw_aff *isl_pw_aff_select( + __isl_take isl_set *cond1, __isl_take isl_pw_aff *pwaff1, + __isl_take isl_set *cond2, __isl_take isl_pw_aff *pwaff2) +{ + pwaff1 = isl_pw_aff_intersect_domain(pwaff1, cond1); + pwaff2 = isl_pw_aff_intersect_domain(pwaff2, cond2); + + return isl_pw_aff_add_disjoint(pwaff1, pwaff2); +} + +/* Return an affine expression that is equal to pwaff_true for elements + * where "cond" is non-zero and to pwaff_false for elements where "cond" + * is zero. + * That is, return cond ? pwaff_true : pwaff_false; + * + * If "cond" involves and NaN, then we conservatively return a NaN + * on its entire domain. In principle, we could consider the pieces + * where it is NaN separately from those where it is not. + * + * If "pwaff_true" and "pwaff_false" are obviously equal to each other, + * then only use the domain of "cond" to restrict the domain. + */ +__isl_give isl_pw_aff *isl_pw_aff_cond(__isl_take isl_pw_aff *cond, + __isl_take isl_pw_aff *pwaff_true, __isl_take isl_pw_aff *pwaff_false) +{ + isl_set *cond_true, *cond_false; + isl_bool equal; + + if (!cond) + goto error; + if (isl_pw_aff_involves_nan(cond)) { + isl_space *space = isl_pw_aff_get_domain_space(cond); + isl_local_space *ls = isl_local_space_from_space(space); + isl_pw_aff_free(cond); + isl_pw_aff_free(pwaff_true); + isl_pw_aff_free(pwaff_false); + return isl_pw_aff_nan_on_domain(ls); + } + + pwaff_true = isl_pw_aff_align_params(pwaff_true, + isl_pw_aff_get_space(pwaff_false)); + pwaff_false = isl_pw_aff_align_params(pwaff_false, + isl_pw_aff_get_space(pwaff_true)); + equal = isl_pw_aff_plain_is_equal(pwaff_true, pwaff_false); + if (equal < 0) + goto error; + if (equal) { + isl_set *dom; + + dom = isl_set_coalesce(isl_pw_aff_domain(cond)); + isl_pw_aff_free(pwaff_false); + return isl_pw_aff_intersect_domain(pwaff_true, dom); + } + + cond_true = isl_pw_aff_non_zero_set(isl_pw_aff_copy(cond)); + cond_false = isl_pw_aff_zero_set(cond); + return isl_pw_aff_select(cond_true, pwaff_true, + cond_false, pwaff_false); +error: + isl_pw_aff_free(cond); + isl_pw_aff_free(pwaff_true); + isl_pw_aff_free(pwaff_false); + return NULL; +} + +isl_bool isl_aff_is_cst(__isl_keep isl_aff *aff) +{ + if (!aff) + return isl_bool_error; + + return isl_seq_first_non_zero(aff->v->el + 2, aff->v->size - 2) == -1; +} + +/* Check whether pwaff is a piecewise constant. + */ +isl_bool isl_pw_aff_is_cst(__isl_keep isl_pw_aff *pwaff) +{ + int i; + + if (!pwaff) + return isl_bool_error; + + for (i = 0; i < pwaff->n; ++i) { + isl_bool is_cst = isl_aff_is_cst(pwaff->p[i].aff); + if (is_cst < 0 || !is_cst) + return is_cst; + } + + return isl_bool_true; +} + +/* Are all elements of "mpa" piecewise constants? + */ +isl_bool isl_multi_pw_aff_is_cst(__isl_keep isl_multi_pw_aff *mpa) +{ + int i; + + if (!mpa) + return isl_bool_error; + + for (i = 0; i < mpa->n; ++i) { + isl_bool is_cst = isl_pw_aff_is_cst(mpa->p[i]); + if (is_cst < 0 || !is_cst) + return is_cst; + } + + return isl_bool_true; +} + +/* Return the product of "aff1" and "aff2". + * + * If either of the two is NaN, then the result is NaN. + * + * Otherwise, at least one of "aff1" or "aff2" needs to be a constant. + */ +__isl_give isl_aff *isl_aff_mul(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + if (!aff1 || !aff2) + goto error; + + if (isl_aff_is_nan(aff1)) { + isl_aff_free(aff2); + return aff1; + } + if (isl_aff_is_nan(aff2)) { + isl_aff_free(aff1); + return aff2; + } + + if (!isl_aff_is_cst(aff2) && isl_aff_is_cst(aff1)) + return isl_aff_mul(aff2, aff1); + + if (!isl_aff_is_cst(aff2)) + isl_die(isl_aff_get_ctx(aff1), isl_error_invalid, + "at least one affine expression should be constant", + goto error); + + aff1 = isl_aff_cow(aff1); + if (!aff1 || !aff2) + goto error; + + aff1 = isl_aff_scale(aff1, aff2->v->el[1]); + aff1 = isl_aff_scale_down(aff1, aff2->v->el[0]); + + isl_aff_free(aff2); + return aff1; +error: + isl_aff_free(aff1); + isl_aff_free(aff2); + return NULL; +} + +/* Divide "aff1" by "aff2", assuming "aff2" is a constant. + * + * If either of the two is NaN, then the result is NaN. + */ +__isl_give isl_aff *isl_aff_div(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + int is_cst; + int neg; + + if (!aff1 || !aff2) + goto error; + + if (isl_aff_is_nan(aff1)) { + isl_aff_free(aff2); + return aff1; + } + if (isl_aff_is_nan(aff2)) { + isl_aff_free(aff1); + return aff2; + } + + is_cst = isl_aff_is_cst(aff2); + if (is_cst < 0) + goto error; + if (!is_cst) + isl_die(isl_aff_get_ctx(aff2), isl_error_invalid, + "second argument should be a constant", goto error); + + if (!aff2) + goto error; + + neg = isl_int_is_neg(aff2->v->el[1]); + if (neg) { + isl_int_neg(aff2->v->el[0], aff2->v->el[0]); + isl_int_neg(aff2->v->el[1], aff2->v->el[1]); + } + + aff1 = isl_aff_scale(aff1, aff2->v->el[0]); + aff1 = isl_aff_scale_down(aff1, aff2->v->el[1]); + + if (neg) { + isl_int_neg(aff2->v->el[0], aff2->v->el[0]); + isl_int_neg(aff2->v->el[1], aff2->v->el[1]); + } + + isl_aff_free(aff2); + return aff1; +error: + isl_aff_free(aff1); + isl_aff_free(aff2); + return NULL; +} + +static __isl_give isl_pw_aff *pw_aff_add(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_on_shared_domain(pwaff1, pwaff2, &isl_aff_add); +} + +__isl_give isl_pw_aff *isl_pw_aff_add(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_align_params_pw_pw_and(pwaff1, pwaff2, &pw_aff_add); +} + +__isl_give isl_pw_aff *isl_pw_aff_union_add(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_union_add_(pwaff1, pwaff2); +} + +static __isl_give isl_pw_aff *pw_aff_mul(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_on_shared_domain(pwaff1, pwaff2, &isl_aff_mul); +} + +__isl_give isl_pw_aff *isl_pw_aff_mul(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_align_params_pw_pw_and(pwaff1, pwaff2, &pw_aff_mul); +} + +static __isl_give isl_pw_aff *pw_aff_div(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + return isl_pw_aff_on_shared_domain(pa1, pa2, &isl_aff_div); +} + +/* Divide "pa1" by "pa2", assuming "pa2" is a piecewise constant. + */ +__isl_give isl_pw_aff *isl_pw_aff_div(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + int is_cst; + + is_cst = isl_pw_aff_is_cst(pa2); + if (is_cst < 0) + goto error; + if (!is_cst) + isl_die(isl_pw_aff_get_ctx(pa2), isl_error_invalid, + "second argument should be a piecewise constant", + goto error); + return isl_pw_aff_align_params_pw_pw_and(pa1, pa2, &pw_aff_div); +error: + isl_pw_aff_free(pa1); + isl_pw_aff_free(pa2); + return NULL; +} + +/* Compute the quotient of the integer division of "pa1" by "pa2" + * with rounding towards zero. + * "pa2" is assumed to be a piecewise constant. + * + * In particular, return + * + * pa1 >= 0 ? floor(pa1/pa2) : ceil(pa1/pa2) + * + */ +__isl_give isl_pw_aff *isl_pw_aff_tdiv_q(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + int is_cst; + isl_set *cond; + isl_pw_aff *f, *c; + + is_cst = isl_pw_aff_is_cst(pa2); + if (is_cst < 0) + goto error; + if (!is_cst) + isl_die(isl_pw_aff_get_ctx(pa2), isl_error_invalid, + "second argument should be a piecewise constant", + goto error); + + pa1 = isl_pw_aff_div(pa1, pa2); + + cond = isl_pw_aff_nonneg_set(isl_pw_aff_copy(pa1)); + f = isl_pw_aff_floor(isl_pw_aff_copy(pa1)); + c = isl_pw_aff_ceil(pa1); + return isl_pw_aff_cond(isl_set_indicator_function(cond), f, c); +error: + isl_pw_aff_free(pa1); + isl_pw_aff_free(pa2); + return NULL; +} + +/* Compute the remainder of the integer division of "pa1" by "pa2" + * with rounding towards zero. + * "pa2" is assumed to be a piecewise constant. + * + * In particular, return + * + * pa1 - pa2 * (pa1 >= 0 ? floor(pa1/pa2) : ceil(pa1/pa2)) + * + */ +__isl_give isl_pw_aff *isl_pw_aff_tdiv_r(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2) +{ + int is_cst; + isl_pw_aff *res; + + is_cst = isl_pw_aff_is_cst(pa2); + if (is_cst < 0) + goto error; + if (!is_cst) + isl_die(isl_pw_aff_get_ctx(pa2), isl_error_invalid, + "second argument should be a piecewise constant", + goto error); + res = isl_pw_aff_tdiv_q(isl_pw_aff_copy(pa1), isl_pw_aff_copy(pa2)); + res = isl_pw_aff_mul(pa2, res); + res = isl_pw_aff_sub(pa1, res); + return res; +error: + isl_pw_aff_free(pa1); + isl_pw_aff_free(pa2); + return NULL; +} + +static __isl_give isl_pw_aff *pw_aff_min(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + isl_set *le; + isl_set *dom; + + dom = isl_set_intersect(isl_pw_aff_domain(isl_pw_aff_copy(pwaff1)), + isl_pw_aff_domain(isl_pw_aff_copy(pwaff2))); + le = isl_pw_aff_le_set(isl_pw_aff_copy(pwaff1), + isl_pw_aff_copy(pwaff2)); + dom = isl_set_subtract(dom, isl_set_copy(le)); + return isl_pw_aff_select(le, pwaff1, dom, pwaff2); +} + +__isl_give isl_pw_aff *isl_pw_aff_min(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_align_params_pw_pw_and(pwaff1, pwaff2, &pw_aff_min); +} + +static __isl_give isl_pw_aff *pw_aff_max(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + isl_set *ge; + isl_set *dom; + + dom = isl_set_intersect(isl_pw_aff_domain(isl_pw_aff_copy(pwaff1)), + isl_pw_aff_domain(isl_pw_aff_copy(pwaff2))); + ge = isl_pw_aff_ge_set(isl_pw_aff_copy(pwaff1), + isl_pw_aff_copy(pwaff2)); + dom = isl_set_subtract(dom, isl_set_copy(ge)); + return isl_pw_aff_select(ge, pwaff1, dom, pwaff2); +} + +__isl_give isl_pw_aff *isl_pw_aff_max(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2) +{ + return isl_pw_aff_align_params_pw_pw_and(pwaff1, pwaff2, &pw_aff_max); +} + +static __isl_give isl_pw_aff *pw_aff_list_reduce( + __isl_take isl_pw_aff_list *list, + __isl_give isl_pw_aff *(*fn)(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2)) +{ + int i; + isl_ctx *ctx; + isl_pw_aff *res; + + if (!list) + return NULL; + + ctx = isl_pw_aff_list_get_ctx(list); + if (list->n < 1) + isl_die(ctx, isl_error_invalid, + "list should contain at least one element", goto error); + + res = isl_pw_aff_copy(list->p[0]); + for (i = 1; i < list->n; ++i) + res = fn(res, isl_pw_aff_copy(list->p[i])); + + isl_pw_aff_list_free(list); + return res; +error: + isl_pw_aff_list_free(list); + return NULL; +} + +/* Return an isl_pw_aff that maps each element in the intersection of the + * domains of the elements of list to the minimal corresponding affine + * expression. + */ +__isl_give isl_pw_aff *isl_pw_aff_list_min(__isl_take isl_pw_aff_list *list) +{ + return pw_aff_list_reduce(list, &isl_pw_aff_min); +} + +/* Return an isl_pw_aff that maps each element in the intersection of the + * domains of the elements of list to the maximal corresponding affine + * expression. + */ +__isl_give isl_pw_aff *isl_pw_aff_list_max(__isl_take isl_pw_aff_list *list) +{ + return pw_aff_list_reduce(list, &isl_pw_aff_max); +} + +/* Mark the domains of "pwaff" as rational. + */ +__isl_give isl_pw_aff *isl_pw_aff_set_rational(__isl_take isl_pw_aff *pwaff) +{ + int i; + + pwaff = isl_pw_aff_cow(pwaff); + if (!pwaff) + return NULL; + if (pwaff->n == 0) + return pwaff; + + for (i = 0; i < pwaff->n; ++i) { + pwaff->p[i].set = isl_set_set_rational(pwaff->p[i].set); + if (!pwaff->p[i].set) + return isl_pw_aff_free(pwaff); + } + + return pwaff; +} + +/* Mark the domains of the elements of "list" as rational. + */ +__isl_give isl_pw_aff_list *isl_pw_aff_list_set_rational( + __isl_take isl_pw_aff_list *list) +{ + int i, n; + + if (!list) + return NULL; + if (list->n == 0) + return list; + + n = list->n; + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + + pa = isl_pw_aff_list_get_pw_aff(list, i); + pa = isl_pw_aff_set_rational(pa); + list = isl_pw_aff_list_set_pw_aff(list, i, pa); + } + + return list; +} + +/* Do the parameters of "aff" match those of "space"? + */ +int isl_aff_matching_params(__isl_keep isl_aff *aff, + __isl_keep isl_space *space) +{ + isl_space *aff_space; + int match; + + if (!aff || !space) + return -1; + + aff_space = isl_aff_get_domain_space(aff); + + match = isl_space_match(space, isl_dim_param, aff_space, isl_dim_param); + + isl_space_free(aff_space); + return match; +} + +/* Check that the domain space of "aff" matches "space". + * + * Return 0 on success and -1 on error. + */ +int isl_aff_check_match_domain_space(__isl_keep isl_aff *aff, + __isl_keep isl_space *space) +{ + isl_space *aff_space; + int match; + + if (!aff || !space) + return -1; + + aff_space = isl_aff_get_domain_space(aff); + + match = isl_space_match(space, isl_dim_param, aff_space, isl_dim_param); + if (match < 0) + goto error; + if (!match) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "parameters don't match", goto error); + match = isl_space_tuple_is_equal(space, isl_dim_in, + aff_space, isl_dim_set); + if (match < 0) + goto error; + if (!match) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "domains don't match", goto error); + isl_space_free(aff_space); + return 0; +error: + isl_space_free(aff_space); + return -1; +} + +#undef BASE +#define BASE aff +#undef DOMBASE +#define DOMBASE set +#define NO_DOMAIN + +#include +#include +#include +#include +#include + +#undef NO_DOMAIN + +/* Remove any internal structure of the domain of "ma". + * If there is any such internal structure in the input, + * then the name of the corresponding space is also removed. + */ +__isl_give isl_multi_aff *isl_multi_aff_flatten_domain( + __isl_take isl_multi_aff *ma) +{ + isl_space *space; + + if (!ma) + return NULL; + + if (!ma->space->nested[0]) + return ma; + + space = isl_multi_aff_get_space(ma); + space = isl_space_flatten_domain(space); + ma = isl_multi_aff_reset_space(ma, space); + + return ma; +} + +/* Given a map space, return an isl_multi_aff that maps a wrapped copy + * of the space to its domain. + */ +__isl_give isl_multi_aff *isl_multi_aff_domain_map(__isl_take isl_space *space) +{ + int i, n_in; + isl_local_space *ls; + isl_multi_aff *ma; + + if (!space) + return NULL; + if (!isl_space_is_map(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "not a map space", goto error); + + n_in = isl_space_dim(space, isl_dim_in); + space = isl_space_domain_map(space); + + ma = isl_multi_aff_alloc(isl_space_copy(space)); + if (n_in == 0) { + isl_space_free(space); + return ma; + } + + space = isl_space_domain(space); + ls = isl_local_space_from_space(space); + for (i = 0; i < n_in; ++i) { + isl_aff *aff; + + aff = isl_aff_var_on_domain(isl_local_space_copy(ls), + isl_dim_set, i); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + isl_local_space_free(ls); + return ma; +error: + isl_space_free(space); + return NULL; +} + +/* Given a map space, return an isl_multi_aff that maps a wrapped copy + * of the space to its range. + */ +__isl_give isl_multi_aff *isl_multi_aff_range_map(__isl_take isl_space *space) +{ + int i, n_in, n_out; + isl_local_space *ls; + isl_multi_aff *ma; + + if (!space) + return NULL; + if (!isl_space_is_map(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "not a map space", goto error); + + n_in = isl_space_dim(space, isl_dim_in); + n_out = isl_space_dim(space, isl_dim_out); + space = isl_space_range_map(space); + + ma = isl_multi_aff_alloc(isl_space_copy(space)); + if (n_out == 0) { + isl_space_free(space); + return ma; + } + + space = isl_space_domain(space); + ls = isl_local_space_from_space(space); + for (i = 0; i < n_out; ++i) { + isl_aff *aff; + + aff = isl_aff_var_on_domain(isl_local_space_copy(ls), + isl_dim_set, n_in + i); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + isl_local_space_free(ls); + return ma; +error: + isl_space_free(space); + return NULL; +} + +/* Given a map space, return an isl_pw_multi_aff that maps a wrapped copy + * of the space to its range. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_map( + __isl_take isl_space *space) +{ + return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_range_map(space)); +} + +/* Given the space of a set and a range of set dimensions, + * construct an isl_multi_aff that projects out those dimensions. + */ +__isl_give isl_multi_aff *isl_multi_aff_project_out_map( + __isl_take isl_space *space, enum isl_dim_type type, + unsigned first, unsigned n) +{ + int i, dim; + isl_local_space *ls; + isl_multi_aff *ma; + + if (!space) + return NULL; + if (!isl_space_is_set(space)) + isl_die(isl_space_get_ctx(space), isl_error_unsupported, + "expecting set space", goto error); + if (type != isl_dim_set) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "only set dimensions can be projected out", goto error); + + dim = isl_space_dim(space, isl_dim_set); + if (first + n > dim) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "range out of bounds", goto error); + + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, dim - n); + + if (dim == n) + return isl_multi_aff_alloc(space); + + ma = isl_multi_aff_alloc(isl_space_copy(space)); + space = isl_space_domain(space); + ls = isl_local_space_from_space(space); + + for (i = 0; i < first; ++i) { + isl_aff *aff; + + aff = isl_aff_var_on_domain(isl_local_space_copy(ls), + isl_dim_set, i); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + + for (i = 0; i < dim - (first + n); ++i) { + isl_aff *aff; + + aff = isl_aff_var_on_domain(isl_local_space_copy(ls), + isl_dim_set, first + n + i); + ma = isl_multi_aff_set_aff(ma, first + i, aff); + } + + isl_local_space_free(ls); + return ma; +error: + isl_space_free(space); + return NULL; +} + +/* Given the space of a set and a range of set dimensions, + * construct an isl_pw_multi_aff that projects out those dimensions. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_out_map( + __isl_take isl_space *space, enum isl_dim_type type, + unsigned first, unsigned n) +{ + isl_multi_aff *ma; + + ma = isl_multi_aff_project_out_map(space, type, first, n); + return isl_pw_multi_aff_from_multi_aff(ma); +} + +/* Create an isl_pw_multi_aff with the given isl_multi_aff on a universe + * domain. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_aff( + __isl_take isl_multi_aff *ma) +{ + isl_set *dom = isl_set_universe(isl_multi_aff_get_domain_space(ma)); + return isl_pw_multi_aff_alloc(dom, ma); +} + +/* Create a piecewise multi-affine expression in the given space that maps each + * input dimension to the corresponding output dimension. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity( + __isl_take isl_space *space) +{ + return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_identity(space)); +} + +/* Exploit the equalities in "eq" to simplify the affine expressions. + */ +static __isl_give isl_multi_aff *isl_multi_aff_substitute_equalities( + __isl_take isl_multi_aff *maff, __isl_take isl_basic_set *eq) +{ + int i; + + maff = isl_multi_aff_cow(maff); + if (!maff || !eq) + goto error; + + for (i = 0; i < maff->n; ++i) { + maff->p[i] = isl_aff_substitute_equalities(maff->p[i], + isl_basic_set_copy(eq)); + if (!maff->p[i]) + goto error; + } + + isl_basic_set_free(eq); + return maff; +error: + isl_basic_set_free(eq); + isl_multi_aff_free(maff); + return NULL; +} + +__isl_give isl_multi_aff *isl_multi_aff_scale(__isl_take isl_multi_aff *maff, + isl_int f) +{ + int i; + + maff = isl_multi_aff_cow(maff); + if (!maff) + return NULL; + + for (i = 0; i < maff->n; ++i) { + maff->p[i] = isl_aff_scale(maff->p[i], f); + if (!maff->p[i]) + return isl_multi_aff_free(maff); + } + + return maff; +} + +__isl_give isl_multi_aff *isl_multi_aff_add_on_domain(__isl_keep isl_set *dom, + __isl_take isl_multi_aff *maff1, __isl_take isl_multi_aff *maff2) +{ + maff1 = isl_multi_aff_add(maff1, maff2); + maff1 = isl_multi_aff_gist(maff1, isl_set_copy(dom)); + return maff1; +} + +int isl_multi_aff_is_empty(__isl_keep isl_multi_aff *maff) +{ + if (!maff) + return -1; + + return 0; +} + +/* Return the set of domain elements where "ma1" is lexicographically + * smaller than or equal to "ma2". + */ +__isl_give isl_set *isl_multi_aff_lex_le_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2) +{ + return isl_multi_aff_lex_ge_set(ma2, ma1); +} + +/* Return the set of domain elements where "ma1" is lexicographically + * smaller than "ma2". + */ +__isl_give isl_set *isl_multi_aff_lex_lt_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2) +{ + return isl_multi_aff_lex_gt_set(ma2, ma1); +} + +/* Return the set of domain elements where "ma1" and "ma2" + * satisfy "order". + */ +static __isl_give isl_set *isl_multi_aff_order_set( + __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2, + __isl_give isl_map *order(__isl_take isl_space *set_space)) +{ + isl_space *space; + isl_map *map1, *map2; + isl_map *map, *ge; + + map1 = isl_map_from_multi_aff(ma1); + map2 = isl_map_from_multi_aff(ma2); + map = isl_map_range_product(map1, map2); + space = isl_space_range(isl_map_get_space(map)); + space = isl_space_domain(isl_space_unwrap(space)); + ge = order(space); + map = isl_map_intersect_range(map, isl_map_wrap(ge)); + + return isl_map_domain(map); +} + +/* Return the set of domain elements where "ma1" is lexicographically + * greater than or equal to "ma2". + */ +__isl_give isl_set *isl_multi_aff_lex_ge_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2) +{ + return isl_multi_aff_order_set(ma1, ma2, &isl_map_lex_ge); +} + +/* Return the set of domain elements where "ma1" is lexicographically + * greater than "ma2". + */ +__isl_give isl_set *isl_multi_aff_lex_gt_set(__isl_take isl_multi_aff *ma1, + __isl_take isl_multi_aff *ma2) +{ + return isl_multi_aff_order_set(ma1, ma2, &isl_map_lex_gt); +} + +#undef PW +#define PW isl_pw_multi_aff +#undef EL +#define EL isl_multi_aff +#undef EL_IS_ZERO +#define EL_IS_ZERO is_empty +#undef ZERO +#define ZERO empty +#undef IS_ZERO +#define IS_ZERO is_empty +#undef FIELD +#define FIELD maff +#undef DEFAULT_IS_ZERO +#define DEFAULT_IS_ZERO 0 + +#define NO_SUB +#define NO_EVAL +#define NO_OPT +#define NO_INVOLVES_DIMS +#define NO_INSERT_DIMS +#define NO_LIFT +#define NO_MORPH + +#include +#include + +#undef NO_SUB + +#undef UNION +#define UNION isl_union_pw_multi_aff +#undef PART +#define PART isl_pw_multi_aff +#undef PARTS +#define PARTS pw_multi_aff + +#include +#include + +static __isl_give isl_pw_multi_aff *pw_multi_aff_union_lexmax( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_union_opt_cmp(pma1, pma2, + &isl_multi_aff_lex_ge_set); +} + +/* Given two piecewise multi affine expressions, return a piecewise + * multi-affine expression defined on the union of the definition domains + * of the inputs that is equal to the lexicographic maximum of the two + * inputs on each cell. If only one of the two inputs is defined on + * a given cell, then it is considered to be the maximum. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmax( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_union_lexmax); +} + +static __isl_give isl_pw_multi_aff *pw_multi_aff_union_lexmin( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_union_opt_cmp(pma1, pma2, + &isl_multi_aff_lex_le_set); +} + +/* Given two piecewise multi affine expressions, return a piecewise + * multi-affine expression defined on the union of the definition domains + * of the inputs that is equal to the lexicographic minimum of the two + * inputs on each cell. If only one of the two inputs is defined on + * a given cell, then it is considered to be the minimum. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmin( + __isl_take isl_pw_multi_aff *pma1, + __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_union_lexmin); +} + +static __isl_give isl_pw_multi_aff *pw_multi_aff_add( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_on_shared_domain(pma1, pma2, + &isl_multi_aff_add); +} + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_add); +} + +static __isl_give isl_pw_multi_aff *pw_multi_aff_sub( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_on_shared_domain(pma1, pma2, + &isl_multi_aff_sub); +} + +/* Subtract "pma2" from "pma1" and return the result. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_sub( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_sub); +} + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_add( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_union_add_(pma1, pma2); +} + +/* Compute the sum of "upa1" and "upa2" on the union of their domains, + * with the actual sum on the shared domain and + * the defined expression on the symmetric difference of the domains. + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_union_add( + __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2) +{ + return isl_union_pw_aff_union_add_(upa1, upa2); +} + +/* Compute the sum of "upma1" and "upma2" on the union of their domains, + * with the actual sum on the shared domain and + * the defined expression on the symmetric difference of the domains. + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_union_add( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2) +{ + return isl_union_pw_multi_aff_union_add_(upma1, upma2); +} + +/* Given two piecewise multi-affine expressions A -> B and C -> D, + * construct a piecewise multi-affine expression [A -> C] -> [B -> D]. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + int i, j, n; + isl_space *space; + isl_pw_multi_aff *res; + + if (!pma1 || !pma2) + goto error; + + n = pma1->n * pma2->n; + space = isl_space_product(isl_space_copy(pma1->dim), + isl_space_copy(pma2->dim)); + res = isl_pw_multi_aff_alloc_size(space, n); + + for (i = 0; i < pma1->n; ++i) { + for (j = 0; j < pma2->n; ++j) { + isl_set *domain; + isl_multi_aff *ma; + + domain = isl_set_product(isl_set_copy(pma1->p[i].set), + isl_set_copy(pma2->p[j].set)); + ma = isl_multi_aff_product( + isl_multi_aff_copy(pma1->p[i].maff), + isl_multi_aff_copy(pma2->p[j].maff)); + res = isl_pw_multi_aff_add_piece(res, domain, ma); + } + } + + isl_pw_multi_aff_free(pma1); + isl_pw_multi_aff_free(pma2); + return res; +error: + isl_pw_multi_aff_free(pma1); + isl_pw_multi_aff_free(pma2); + return NULL; +} + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_product); +} + +/* Construct a map mapping the domain of the piecewise multi-affine expression + * to its range, with each dimension in the range equated to the + * corresponding affine expression on its cell. + */ +__isl_give isl_map *isl_map_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma) +{ + int i; + isl_map *map; + + if (!pma) + return NULL; + + map = isl_map_empty(isl_pw_multi_aff_get_space(pma)); + + for (i = 0; i < pma->n; ++i) { + isl_multi_aff *maff; + isl_basic_map *bmap; + isl_map *map_i; + + maff = isl_multi_aff_copy(pma->p[i].maff); + bmap = isl_basic_map_from_multi_aff(maff); + map_i = isl_map_from_basic_map(bmap); + map_i = isl_map_intersect_domain(map_i, + isl_set_copy(pma->p[i].set)); + map = isl_map_union_disjoint(map, map_i); + } + + isl_pw_multi_aff_free(pma); + return map; +} + +__isl_give isl_set *isl_set_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma) +{ + if (!pma) + return NULL; + + if (!isl_space_is_set(pma->dim)) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "isl_pw_multi_aff cannot be converted into an isl_set", + goto error); + + return isl_map_from_pw_multi_aff(pma); +error: + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Subtract the initial "n" elements in "ma" with coefficients in "c" and + * denominator "denom". + * "denom" is allowed to be negative, in which case the actual denominator + * is -denom and the expressions are added instead. + */ +static __isl_give isl_aff *subtract_initial(__isl_take isl_aff *aff, + __isl_keep isl_multi_aff *ma, int n, isl_int *c, isl_int denom) +{ + int i, first; + int sign; + isl_int d; + + first = isl_seq_first_non_zero(c, n); + if (first == -1) + return aff; + + sign = isl_int_sgn(denom); + isl_int_init(d); + isl_int_abs(d, denom); + for (i = first; i < n; ++i) { + isl_aff *aff_i; + + if (isl_int_is_zero(c[i])) + continue; + aff_i = isl_multi_aff_get_aff(ma, i); + aff_i = isl_aff_scale(aff_i, c[i]); + aff_i = isl_aff_scale_down(aff_i, d); + if (sign >= 0) + aff = isl_aff_sub(aff, aff_i); + else + aff = isl_aff_add(aff, aff_i); + } + isl_int_clear(d); + + return aff; +} + +/* Extract an affine expression that expresses the output dimension "pos" + * of "bmap" in terms of the parameters and input dimensions from + * equality "eq". + * Note that this expression may involve integer divisions defined + * in terms of parameters and input dimensions. + * The equality may also involve references to earlier (but not later) + * output dimensions. These are replaced by the corresponding elements + * in "ma". + * + * If the equality is of the form + * + * f(i) + h(j) + a x + g(i) = 0, + * + * with f(i) a linear combinations of the parameters and input dimensions, + * g(i) a linear combination of integer divisions defined in terms of the same + * and h(j) a linear combinations of earlier output dimensions, + * then the affine expression is + * + * (-f(i) - g(i))/a - h(j)/a + * + * If the equality is of the form + * + * f(i) + h(j) - a x + g(i) = 0, + * + * then the affine expression is + * + * (f(i) + g(i))/a - h(j)/(-a) + * + * + * If "div" refers to an integer division (i.e., it is smaller than + * the number of integer divisions), then the equality constraint + * does involve an integer division (the one at position "div") that + * is defined in terms of output dimensions. However, this integer + * division can be eliminated by exploiting a pair of constraints + * x >= l and x <= l + n, with n smaller than the coefficient of "div" + * in the equality constraint. "ineq" refers to inequality x >= l, i.e., + * -l + x >= 0. + * In particular, let + * + * x = e(i) + m floor(...) + * + * with e(i) the expression derived above and floor(...) the integer + * division involving output dimensions. + * From + * + * l <= x <= l + n, + * + * we have + * + * 0 <= x - l <= n + * + * This means + * + * e(i) + m floor(...) - l = (e(i) + m floor(...) - l) mod m + * = (e(i) - l) mod m + * + * Therefore, + * + * x - l = (e(i) - l) mod m + * + * or + * + * x = ((e(i) - l) mod m) + l + * + * The variable "shift" below contains the expression -l, which may + * also involve a linear combination of earlier output dimensions. + */ +static __isl_give isl_aff *extract_aff_from_equality( + __isl_keep isl_basic_map *bmap, int pos, int eq, int div, int ineq, + __isl_keep isl_multi_aff *ma) +{ + unsigned o_out; + unsigned n_div, n_out; + isl_ctx *ctx; + isl_local_space *ls; + isl_aff *aff, *shift; + isl_val *mod; + + ctx = isl_basic_map_get_ctx(bmap); + ls = isl_basic_map_get_local_space(bmap); + ls = isl_local_space_domain(ls); + aff = isl_aff_alloc(isl_local_space_copy(ls)); + if (!aff) + goto error; + o_out = isl_basic_map_offset(bmap, isl_dim_out); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + if (isl_int_is_neg(bmap->eq[eq][o_out + pos])) { + isl_seq_cpy(aff->v->el + 1, bmap->eq[eq], o_out); + isl_seq_cpy(aff->v->el + 1 + o_out, + bmap->eq[eq] + o_out + n_out, n_div); + } else { + isl_seq_neg(aff->v->el + 1, bmap->eq[eq], o_out); + isl_seq_neg(aff->v->el + 1 + o_out, + bmap->eq[eq] + o_out + n_out, n_div); + } + if (div < n_div) + isl_int_set_si(aff->v->el[1 + o_out + div], 0); + isl_int_abs(aff->v->el[0], bmap->eq[eq][o_out + pos]); + aff = subtract_initial(aff, ma, pos, bmap->eq[eq] + o_out, + bmap->eq[eq][o_out + pos]); + if (div < n_div) { + shift = isl_aff_alloc(isl_local_space_copy(ls)); + if (!shift) + goto error; + isl_seq_cpy(shift->v->el + 1, bmap->ineq[ineq], o_out); + isl_seq_cpy(shift->v->el + 1 + o_out, + bmap->ineq[ineq] + o_out + n_out, n_div); + isl_int_set_si(shift->v->el[0], 1); + shift = subtract_initial(shift, ma, pos, + bmap->ineq[ineq] + o_out, ctx->negone); + aff = isl_aff_add(aff, isl_aff_copy(shift)); + mod = isl_val_int_from_isl_int(ctx, + bmap->eq[eq][o_out + n_out + div]); + mod = isl_val_abs(mod); + aff = isl_aff_mod_val(aff, mod); + aff = isl_aff_sub(aff, shift); + } + + isl_local_space_free(ls); + return aff; +error: + isl_local_space_free(ls); + isl_aff_free(aff); + return NULL; +} + +/* Given a basic map with output dimensions defined + * in terms of the parameters input dimensions and earlier + * output dimensions using an equality (and possibly a pair on inequalities), + * extract an isl_aff that expresses output dimension "pos" in terms + * of the parameters and input dimensions. + * Note that this expression may involve integer divisions defined + * in terms of parameters and input dimensions. + * "ma" contains the expressions corresponding to earlier output dimensions. + * + * This function shares some similarities with + * isl_basic_map_has_defining_equality and isl_constraint_get_bound. + */ +static __isl_give isl_aff *extract_isl_aff_from_basic_map( + __isl_keep isl_basic_map *bmap, int pos, __isl_keep isl_multi_aff *ma) +{ + int eq, div, ineq; + isl_aff *aff; + + if (!bmap) + return NULL; + eq = isl_basic_map_output_defining_equality(bmap, pos, &div, &ineq); + if (eq >= bmap->n_eq) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "unable to find suitable equality", return NULL); + aff = extract_aff_from_equality(bmap, pos, eq, div, ineq, ma); + + aff = isl_aff_remove_unused_divs(aff); + return aff; +} + +/* Given a basic map where each output dimension is defined + * in terms of the parameters and input dimensions using an equality, + * extract an isl_multi_aff that expresses the output dimensions in terms + * of the parameters and input dimensions. + */ +static __isl_give isl_multi_aff *extract_isl_multi_aff_from_basic_map( + __isl_take isl_basic_map *bmap) +{ + int i; + unsigned n_out; + isl_multi_aff *ma; + + if (!bmap) + return NULL; + + ma = isl_multi_aff_alloc(isl_basic_map_get_space(bmap)); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + + for (i = 0; i < n_out; ++i) { + isl_aff *aff; + + aff = extract_isl_aff_from_basic_map(bmap, i, ma); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + + isl_basic_map_free(bmap); + + return ma; +} + +/* Given a basic set where each set dimension is defined + * in terms of the parameters using an equality, + * extract an isl_multi_aff that expresses the set dimensions in terms + * of the parameters. + */ +__isl_give isl_multi_aff *isl_multi_aff_from_basic_set_equalities( + __isl_take isl_basic_set *bset) +{ + return extract_isl_multi_aff_from_basic_map(bset); +} + +/* Create an isl_pw_multi_aff that is equivalent to + * isl_map_intersect_domain(isl_map_from_basic_map(bmap), domain). + * The given basic map is such that each output dimension is defined + * in terms of the parameters and input dimensions using an equality. + * + * Since some applications expect the result of isl_pw_multi_aff_from_map + * to only contain integer affine expressions, we compute the floor + * of the expression before returning. + * + * Remove all constraints involving local variables without + * an explicit representation (resulting in the removal of those + * local variables) prior to the actual extraction to ensure + * that the local spaces in which the resulting affine expressions + * are created do not contain any unknown local variables. + * Removing such constraints is safe because constraints involving + * unknown local variables are not used to determine whether + * a basic map is obviously single-valued. + */ +static __isl_give isl_pw_multi_aff *plain_pw_multi_aff_from_map( + __isl_take isl_set *domain, __isl_take isl_basic_map *bmap) +{ + isl_multi_aff *ma; + + bmap = isl_basic_map_drop_constraint_involving_unknown_divs(bmap); + ma = extract_isl_multi_aff_from_basic_map(bmap); + ma = isl_multi_aff_floor(ma); + return isl_pw_multi_aff_alloc(domain, ma); +} + +/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map. + * This obviously only works if the input "map" is single-valued. + * If so, we compute the lexicographic minimum of the image in the form + * of an isl_pw_multi_aff. Since the image is unique, it is equal + * to its lexicographic minimum. + * If the input is not single-valued, we produce an error. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_base( + __isl_take isl_map *map) +{ + int i; + int sv; + isl_pw_multi_aff *pma; + + sv = isl_map_is_single_valued(map); + if (sv < 0) + goto error; + if (!sv) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "map is not single-valued", goto error); + map = isl_map_make_disjoint(map); + if (!map) + return NULL; + + pma = isl_pw_multi_aff_empty(isl_map_get_space(map)); + + for (i = 0; i < map->n; ++i) { + isl_pw_multi_aff *pma_i; + isl_basic_map *bmap; + bmap = isl_basic_map_copy(map->p[i]); + pma_i = isl_basic_map_lexmin_pw_multi_aff(bmap); + pma = isl_pw_multi_aff_add_disjoint(pma, pma_i); + } + + isl_map_free(map); + return pma; +error: + isl_map_free(map); + return NULL; +} + +/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map, + * taking into account that the output dimension at position "d" + * can be represented as + * + * x = floor((e(...) + c1) / m) + * + * given that constraint "i" is of the form + * + * e(...) + c1 - m x >= 0 + * + * + * Let "map" be of the form + * + * A -> B + * + * We construct a mapping + * + * A -> [A -> x = floor(...)] + * + * apply that to the map, obtaining + * + * [A -> x = floor(...)] -> B + * + * and equate dimension "d" to x. + * We then compute a isl_pw_multi_aff representation of the resulting map + * and plug in the mapping above. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_div( + __isl_take isl_map *map, __isl_take isl_basic_map *hull, int d, int i) +{ + isl_ctx *ctx; + isl_space *space; + isl_local_space *ls; + isl_multi_aff *ma; + isl_aff *aff; + isl_vec *v; + isl_map *insert; + int offset; + int n; + int n_in; + isl_pw_multi_aff *pma; + int is_set; + + is_set = isl_map_is_set(map); + + offset = isl_basic_map_offset(hull, isl_dim_out); + ctx = isl_map_get_ctx(map); + space = isl_space_domain(isl_map_get_space(map)); + n_in = isl_space_dim(space, isl_dim_set); + n = isl_space_dim(space, isl_dim_all); + + v = isl_vec_alloc(ctx, 1 + 1 + n); + if (v) { + isl_int_neg(v->el[0], hull->ineq[i][offset + d]); + isl_seq_cpy(v->el + 1, hull->ineq[i], 1 + n); + } + isl_basic_map_free(hull); + + ls = isl_local_space_from_space(isl_space_copy(space)); + aff = isl_aff_alloc_vec(ls, v); + aff = isl_aff_floor(aff); + if (is_set) { + isl_space_free(space); + ma = isl_multi_aff_from_aff(aff); + } else { + ma = isl_multi_aff_identity(isl_space_map_from_set(space)); + ma = isl_multi_aff_range_product(ma, + isl_multi_aff_from_aff(aff)); + } + + insert = isl_map_from_multi_aff(isl_multi_aff_copy(ma)); + map = isl_map_apply_domain(map, insert); + map = isl_map_equate(map, isl_dim_in, n_in, isl_dim_out, d); + pma = isl_pw_multi_aff_from_map(map); + pma = isl_pw_multi_aff_pullback_multi_aff(pma, ma); + + return pma; +} + +/* Is constraint "c" of the form + * + * e(...) + c1 - m x >= 0 + * + * or + * + * -e(...) + c2 + m x >= 0 + * + * where m > 1 and e only depends on parameters and input dimemnsions? + * + * "offset" is the offset of the output dimensions + * "pos" is the position of output dimension x. + */ +static int is_potential_div_constraint(isl_int *c, int offset, int d, int total) +{ + if (isl_int_is_zero(c[offset + d])) + return 0; + if (isl_int_is_one(c[offset + d])) + return 0; + if (isl_int_is_negone(c[offset + d])) + return 0; + if (isl_seq_first_non_zero(c + offset, d) != -1) + return 0; + if (isl_seq_first_non_zero(c + offset + d + 1, + total - (offset + d + 1)) != -1) + return 0; + return 1; +} + +/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map. + * + * As a special case, we first check if there is any pair of constraints, + * shared by all the basic maps in "map" that force a given dimension + * to be equal to the floor of some affine combination of the input dimensions. + * + * In particular, if we can find two constraints + * + * e(...) + c1 - m x >= 0 i.e., m x <= e(...) + c1 + * + * and + * + * -e(...) + c2 + m x >= 0 i.e., m x >= e(...) - c2 + * + * where m > 1 and e only depends on parameters and input dimemnsions, + * and such that + * + * c1 + c2 < m i.e., -c2 >= c1 - (m - 1) + * + * then we know that we can take + * + * x = floor((e(...) + c1) / m) + * + * without having to perform any computation. + * + * Note that we know that + * + * c1 + c2 >= 1 + * + * If c1 + c2 were 0, then we would have detected an equality during + * simplification. If c1 + c2 were negative, then we would have detected + * a contradiction. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_check_div( + __isl_take isl_map *map) +{ + int d, dim; + int i, j, n; + int offset, total; + isl_int sum; + isl_basic_map *hull; + + hull = isl_map_unshifted_simple_hull(isl_map_copy(map)); + if (!hull) + goto error; + + isl_int_init(sum); + dim = isl_map_dim(map, isl_dim_out); + offset = isl_basic_map_offset(hull, isl_dim_out); + total = 1 + isl_basic_map_total_dim(hull); + n = hull->n_ineq; + for (d = 0; d < dim; ++d) { + for (i = 0; i < n; ++i) { + if (!is_potential_div_constraint(hull->ineq[i], + offset, d, total)) + continue; + for (j = i + 1; j < n; ++j) { + if (!isl_seq_is_neg(hull->ineq[i] + 1, + hull->ineq[j] + 1, total - 1)) + continue; + isl_int_add(sum, hull->ineq[i][0], + hull->ineq[j][0]); + if (isl_int_abs_lt(sum, + hull->ineq[i][offset + d])) + break; + + } + if (j >= n) + continue; + isl_int_clear(sum); + if (isl_int_is_pos(hull->ineq[j][offset + d])) + j = i; + return pw_multi_aff_from_map_div(map, hull, d, j); + } + } + isl_int_clear(sum); + isl_basic_map_free(hull); + return pw_multi_aff_from_map_base(map); +error: + isl_map_free(map); + isl_basic_map_free(hull); + return NULL; +} + +/* Given an affine expression + * + * [A -> B] -> f(A,B) + * + * construct an isl_multi_aff + * + * [A -> B] -> B' + * + * such that dimension "d" in B' is set to "aff" and the remaining + * dimensions are set equal to the corresponding dimensions in B. + * "n_in" is the dimension of the space A. + * "n_out" is the dimension of the space B. + * + * If "is_set" is set, then the affine expression is of the form + * + * [B] -> f(B) + * + * and we construct an isl_multi_aff + * + * B -> B' + */ +static __isl_give isl_multi_aff *range_map(__isl_take isl_aff *aff, int d, + unsigned n_in, unsigned n_out, int is_set) +{ + int i; + isl_multi_aff *ma; + isl_space *space, *space2; + isl_local_space *ls; + + space = isl_aff_get_domain_space(aff); + ls = isl_local_space_from_space(isl_space_copy(space)); + space2 = isl_space_copy(space); + if (!is_set) + space2 = isl_space_range(isl_space_unwrap(space2)); + space = isl_space_map_from_domain_and_range(space, space2); + ma = isl_multi_aff_alloc(space); + ma = isl_multi_aff_set_aff(ma, d, aff); + + for (i = 0; i < n_out; ++i) { + if (i == d) + continue; + aff = isl_aff_var_on_domain(isl_local_space_copy(ls), + isl_dim_set, n_in + i); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + + isl_local_space_free(ls); + + return ma; +} + +/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map, + * taking into account that the dimension at position "d" can be written as + * + * x = m a + f(..) (1) + * + * where m is equal to "gcd". + * "i" is the index of the equality in "hull" that defines f(..). + * In particular, the equality is of the form + * + * f(..) - x + m g(existentials) = 0 + * + * or + * + * -f(..) + x + m g(existentials) = 0 + * + * We basically plug (1) into "map", resulting in a map with "a" + * in the range instead of "x". The corresponding isl_pw_multi_aff + * defining "a" is then plugged back into (1) to obtain a definition for "x". + * + * Specifically, given the input map + * + * A -> B + * + * We first wrap it into a set + * + * [A -> B] + * + * and define (1) on top of the corresponding space, resulting in "aff". + * We use this to create an isl_multi_aff that maps the output position "d" + * from "a" to "x", leaving all other (intput and output) dimensions unchanged. + * We plug this into the wrapped map, unwrap the result and compute the + * corresponding isl_pw_multi_aff. + * The result is an expression + * + * A -> T(A) + * + * We adjust that to + * + * A -> [A -> T(A)] + * + * so that we can plug that into "aff", after extending the latter to + * a mapping + * + * [A -> B] -> B' + * + * + * If "map" is actually a set, then there is no "A" space, meaning + * that we do not need to perform any wrapping, and that the result + * of the recursive call is of the form + * + * [T] + * + * which is plugged into a mapping of the form + * + * B -> B' + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_stride( + __isl_take isl_map *map, __isl_take isl_basic_map *hull, int d, int i, + isl_int gcd) +{ + isl_set *set; + isl_space *space; + isl_local_space *ls; + isl_aff *aff; + isl_multi_aff *ma; + isl_pw_multi_aff *pma, *id; + unsigned n_in; + unsigned o_out; + unsigned n_out; + int is_set; + + is_set = isl_map_is_set(map); + + n_in = isl_basic_map_dim(hull, isl_dim_in); + n_out = isl_basic_map_dim(hull, isl_dim_out); + o_out = isl_basic_map_offset(hull, isl_dim_out); + + if (is_set) + set = map; + else + set = isl_map_wrap(map); + space = isl_space_map_from_set(isl_set_get_space(set)); + ma = isl_multi_aff_identity(space); + ls = isl_local_space_from_space(isl_set_get_space(set)); + aff = isl_aff_alloc(ls); + if (aff) { + isl_int_set_si(aff->v->el[0], 1); + if (isl_int_is_one(hull->eq[i][o_out + d])) + isl_seq_neg(aff->v->el + 1, hull->eq[i], + aff->v->size - 1); + else + isl_seq_cpy(aff->v->el + 1, hull->eq[i], + aff->v->size - 1); + isl_int_set(aff->v->el[1 + o_out + d], gcd); + } + ma = isl_multi_aff_set_aff(ma, n_in + d, isl_aff_copy(aff)); + set = isl_set_preimage_multi_aff(set, ma); + + ma = range_map(aff, d, n_in, n_out, is_set); + + if (is_set) + map = set; + else + map = isl_set_unwrap(set); + pma = isl_pw_multi_aff_from_map(map); + + if (!is_set) { + space = isl_pw_multi_aff_get_domain_space(pma); + space = isl_space_map_from_set(space); + id = isl_pw_multi_aff_identity(space); + pma = isl_pw_multi_aff_range_product(id, pma); + } + id = isl_pw_multi_aff_from_multi_aff(ma); + pma = isl_pw_multi_aff_pullback_pw_multi_aff(id, pma); + + isl_basic_map_free(hull); + return pma; +} + +/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map. + * "hull" contains the equalities valid for "map". + * + * Check if any of the output dimensions is "strided". + * That is, we check if it can be written as + * + * x = m a + f(..) + * + * with m greater than 1, a some combination of existentially quantified + * variables and f an expression in the parameters and input dimensions. + * If so, we remove the stride in pw_multi_aff_from_map_stride. + * + * Otherwise, we continue with pw_multi_aff_from_map_check_div for a further + * special case. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_check_strides( + __isl_take isl_map *map, __isl_take isl_basic_map *hull) +{ + int i, j; + unsigned n_out; + unsigned o_out; + unsigned n_div; + unsigned o_div; + isl_int gcd; + + n_div = isl_basic_map_dim(hull, isl_dim_div); + o_div = isl_basic_map_offset(hull, isl_dim_div); + + if (n_div == 0) { + isl_basic_map_free(hull); + return pw_multi_aff_from_map_check_div(map); + } + + isl_int_init(gcd); + + n_out = isl_basic_map_dim(hull, isl_dim_out); + o_out = isl_basic_map_offset(hull, isl_dim_out); + + for (i = 0; i < n_out; ++i) { + for (j = 0; j < hull->n_eq; ++j) { + isl_int *eq = hull->eq[j]; + isl_pw_multi_aff *res; + + if (!isl_int_is_one(eq[o_out + i]) && + !isl_int_is_negone(eq[o_out + i])) + continue; + if (isl_seq_first_non_zero(eq + o_out, i) != -1) + continue; + if (isl_seq_first_non_zero(eq + o_out + i + 1, + n_out - (i + 1)) != -1) + continue; + isl_seq_gcd(eq + o_div, n_div, &gcd); + if (isl_int_is_zero(gcd)) + continue; + if (isl_int_is_one(gcd)) + continue; + + res = pw_multi_aff_from_map_stride(map, hull, + i, j, gcd); + isl_int_clear(gcd); + return res; + } + } + + isl_int_clear(gcd); + isl_basic_map_free(hull); + return pw_multi_aff_from_map_check_div(map); +} + +/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map. + * + * As a special case, we first check if all output dimensions are uniquely + * defined in terms of the parameters and input dimensions over the entire + * domain. If so, we extract the desired isl_pw_multi_aff directly + * from the affine hull of "map" and its domain. + * + * Otherwise, continue with pw_multi_aff_from_map_check_strides for more + * special cases. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_map(__isl_take isl_map *map) +{ + isl_bool sv; + isl_basic_map *hull; + + if (!map) + return NULL; + + if (isl_map_n_basic_map(map) == 1) { + hull = isl_map_unshifted_simple_hull(isl_map_copy(map)); + hull = isl_basic_map_plain_affine_hull(hull); + sv = isl_basic_map_plain_is_single_valued(hull); + if (sv >= 0 && sv) + return plain_pw_multi_aff_from_map(isl_map_domain(map), + hull); + isl_basic_map_free(hull); + } + map = isl_map_detect_equalities(map); + hull = isl_map_unshifted_simple_hull(isl_map_copy(map)); + sv = isl_basic_map_plain_is_single_valued(hull); + if (sv >= 0 && sv) + return plain_pw_multi_aff_from_map(isl_map_domain(map), hull); + if (sv >= 0) + return pw_multi_aff_from_map_check_strides(map, hull); + isl_basic_map_free(hull); + isl_map_free(map); + return NULL; +} + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_set(__isl_take isl_set *set) +{ + return isl_pw_multi_aff_from_map(set); +} + +/* Convert "map" into an isl_pw_multi_aff (if possible) and + * add it to *user. + */ +static isl_stat pw_multi_aff_from_map(__isl_take isl_map *map, void *user) +{ + isl_union_pw_multi_aff **upma = user; + isl_pw_multi_aff *pma; + + pma = isl_pw_multi_aff_from_map(map); + *upma = isl_union_pw_multi_aff_add_pw_multi_aff(*upma, pma); + + return *upma ? isl_stat_ok : isl_stat_error; +} + +/* Create an isl_union_pw_multi_aff with the given isl_aff on a universe + * domain. + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_aff( + __isl_take isl_aff *aff) +{ + isl_multi_aff *ma; + isl_pw_multi_aff *pma; + + ma = isl_multi_aff_from_aff(aff); + pma = isl_pw_multi_aff_from_multi_aff(ma); + return isl_union_pw_multi_aff_from_pw_multi_aff(pma); +} + +/* Try and create an isl_union_pw_multi_aff that is equivalent + * to the given isl_union_map. + * The isl_union_map is required to be single-valued in each space. + * Otherwise, an error is produced. + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_map( + __isl_take isl_union_map *umap) +{ + isl_space *space; + isl_union_pw_multi_aff *upma; + + space = isl_union_map_get_space(umap); + upma = isl_union_pw_multi_aff_empty(space); + if (isl_union_map_foreach_map(umap, &pw_multi_aff_from_map, &upma) < 0) + upma = isl_union_pw_multi_aff_free(upma); + isl_union_map_free(umap); + + return upma; +} + +/* Try and create an isl_union_pw_multi_aff that is equivalent + * to the given isl_union_set. + * The isl_union_set is required to be a singleton in each space. + * Otherwise, an error is produced. + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_set( + __isl_take isl_union_set *uset) +{ + return isl_union_pw_multi_aff_from_union_map(uset); +} + +/* Return the piecewise affine expression "set ? 1 : 0". + */ +__isl_give isl_pw_aff *isl_set_indicator_function(__isl_take isl_set *set) +{ + isl_pw_aff *pa; + isl_space *space = isl_set_get_space(set); + isl_local_space *ls = isl_local_space_from_space(space); + isl_aff *zero = isl_aff_zero_on_domain(isl_local_space_copy(ls)); + isl_aff *one = isl_aff_zero_on_domain(ls); + + one = isl_aff_add_constant_si(one, 1); + pa = isl_pw_aff_alloc(isl_set_copy(set), one); + set = isl_set_complement(set); + pa = isl_pw_aff_add_disjoint(pa, isl_pw_aff_alloc(set, zero)); + + return pa; +} + +/* Plug in "subs" for dimension "type", "pos" of "aff". + * + * Let i be the dimension to replace and let "subs" be of the form + * + * f/d + * + * and "aff" of the form + * + * (a i + g)/m + * + * The result is + * + * (a f + d g')/(m d) + * + * where g' is the result of plugging in "subs" in each of the integer + * divisions in g. + */ +__isl_give isl_aff *isl_aff_substitute(__isl_take isl_aff *aff, + enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs) +{ + isl_ctx *ctx; + isl_int v; + + aff = isl_aff_cow(aff); + if (!aff || !subs) + return isl_aff_free(aff); + + ctx = isl_aff_get_ctx(aff); + if (!isl_space_is_equal(aff->ls->dim, subs->ls->dim)) + isl_die(ctx, isl_error_invalid, + "spaces don't match", return isl_aff_free(aff)); + if (isl_local_space_dim(subs->ls, isl_dim_div) != 0) + isl_die(ctx, isl_error_unsupported, + "cannot handle divs yet", return isl_aff_free(aff)); + + aff->ls = isl_local_space_substitute(aff->ls, type, pos, subs); + if (!aff->ls) + return isl_aff_free(aff); + + aff->v = isl_vec_cow(aff->v); + if (!aff->v) + return isl_aff_free(aff); + + pos += isl_local_space_offset(aff->ls, type); + + isl_int_init(v); + isl_seq_substitute(aff->v->el, pos, subs->v->el, + aff->v->size, subs->v->size, v); + isl_int_clear(v); + + return aff; +} + +/* Plug in "subs" for dimension "type", "pos" in each of the affine + * expressions in "maff". + */ +__isl_give isl_multi_aff *isl_multi_aff_substitute( + __isl_take isl_multi_aff *maff, enum isl_dim_type type, unsigned pos, + __isl_keep isl_aff *subs) +{ + int i; + + maff = isl_multi_aff_cow(maff); + if (!maff || !subs) + return isl_multi_aff_free(maff); + + if (type == isl_dim_in) + type = isl_dim_set; + + for (i = 0; i < maff->n; ++i) { + maff->p[i] = isl_aff_substitute(maff->p[i], type, pos, subs); + if (!maff->p[i]) + return isl_multi_aff_free(maff); + } + + return maff; +} + +/* Plug in "subs" for dimension "type", "pos" of "pma". + * + * pma is of the form + * + * A_i(v) -> M_i(v) + * + * while subs is of the form + * + * v' = B_j(v) -> S_j + * + * Each pair i,j such that C_ij = A_i \cap B_i is non-empty + * has a contribution in the result, in particular + * + * C_ij(S_j) -> M_i(S_j) + * + * Note that plugging in S_j in C_ij may also result in an empty set + * and this contribution should simply be discarded. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_substitute( + __isl_take isl_pw_multi_aff *pma, enum isl_dim_type type, unsigned pos, + __isl_keep isl_pw_aff *subs) +{ + int i, j, n; + isl_pw_multi_aff *res; + + if (!pma || !subs) + return isl_pw_multi_aff_free(pma); + + n = pma->n * subs->n; + res = isl_pw_multi_aff_alloc_size(isl_space_copy(pma->dim), n); + + for (i = 0; i < pma->n; ++i) { + for (j = 0; j < subs->n; ++j) { + isl_set *common; + isl_multi_aff *res_ij; + int empty; + + common = isl_set_intersect( + isl_set_copy(pma->p[i].set), + isl_set_copy(subs->p[j].set)); + common = isl_set_substitute(common, + type, pos, subs->p[j].aff); + empty = isl_set_plain_is_empty(common); + if (empty < 0 || empty) { + isl_set_free(common); + if (empty < 0) + goto error; + continue; + } + + res_ij = isl_multi_aff_substitute( + isl_multi_aff_copy(pma->p[i].maff), + type, pos, subs->p[j].aff); + + res = isl_pw_multi_aff_add_piece(res, common, res_ij); + } + } + + isl_pw_multi_aff_free(pma); + return res; +error: + isl_pw_multi_aff_free(pma); + isl_pw_multi_aff_free(res); + return NULL; +} + +/* Compute the preimage of a range of dimensions in the affine expression "src" + * under "ma" and put the result in "dst". The number of dimensions in "src" + * that precede the range is given by "n_before". The number of dimensions + * in the range is given by the number of output dimensions of "ma". + * The number of dimensions that follow the range is given by "n_after". + * If "has_denom" is set (to one), + * then "src" and "dst" have an extra initial denominator. + * "n_div_ma" is the number of existentials in "ma" + * "n_div_bset" is the number of existentials in "src" + * The resulting "dst" (which is assumed to have been allocated by + * the caller) contains coefficients for both sets of existentials, + * first those in "ma" and then those in "src". + * f, c1, c2 and g are temporary objects that have been initialized + * by the caller. + * + * Let src represent the expression + * + * (a(p) + f_u u + b v + f_w w + c(divs))/d + * + * and let ma represent the expressions + * + * v_i = (r_i(p) + s_i(y) + t_i(divs'))/m_i + * + * We start out with the following expression for dst: + * + * (a(p) + f_u u + 0 y + f_w w + 0 divs' + c(divs) + f \sum_i b_i v_i)/d + * + * with the multiplication factor f initially equal to 1 + * and f \sum_i b_i v_i kept separately. + * For each x_i that we substitute, we multiply the numerator + * (and denominator) of dst by c_1 = m_i and add the numerator + * of the x_i expression multiplied by c_2 = f b_i, + * after removing the common factors of c_1 and c_2. + * The multiplication factor f also needs to be multiplied by c_1 + * for the next x_j, j > i. + */ +void isl_seq_preimage(isl_int *dst, isl_int *src, + __isl_keep isl_multi_aff *ma, int n_before, int n_after, + int n_div_ma, int n_div_bmap, + isl_int f, isl_int c1, isl_int c2, isl_int g, int has_denom) +{ + int i; + int n_param, n_in, n_out; + int o_dst, o_src; + + n_param = isl_multi_aff_dim(ma, isl_dim_param); + n_in = isl_multi_aff_dim(ma, isl_dim_in); + n_out = isl_multi_aff_dim(ma, isl_dim_out); + + isl_seq_cpy(dst, src, has_denom + 1 + n_param + n_before); + o_dst = o_src = has_denom + 1 + n_param + n_before; + isl_seq_clr(dst + o_dst, n_in); + o_dst += n_in; + o_src += n_out; + isl_seq_cpy(dst + o_dst, src + o_src, n_after); + o_dst += n_after; + o_src += n_after; + isl_seq_clr(dst + o_dst, n_div_ma); + o_dst += n_div_ma; + isl_seq_cpy(dst + o_dst, src + o_src, n_div_bmap); + + isl_int_set_si(f, 1); + + for (i = 0; i < n_out; ++i) { + int offset = has_denom + 1 + n_param + n_before + i; + + if (isl_int_is_zero(src[offset])) + continue; + isl_int_set(c1, ma->p[i]->v->el[0]); + isl_int_mul(c2, f, src[offset]); + isl_int_gcd(g, c1, c2); + isl_int_divexact(c1, c1, g); + isl_int_divexact(c2, c2, g); + + isl_int_mul(f, f, c1); + o_dst = has_denom; + o_src = 1; + isl_seq_combine(dst + o_dst, c1, dst + o_dst, + c2, ma->p[i]->v->el + o_src, 1 + n_param); + o_dst += 1 + n_param; + o_src += 1 + n_param; + isl_seq_scale(dst + o_dst, dst + o_dst, c1, n_before); + o_dst += n_before; + isl_seq_combine(dst + o_dst, c1, dst + o_dst, + c2, ma->p[i]->v->el + o_src, n_in); + o_dst += n_in; + o_src += n_in; + isl_seq_scale(dst + o_dst, dst + o_dst, c1, n_after); + o_dst += n_after; + isl_seq_combine(dst + o_dst, c1, dst + o_dst, + c2, ma->p[i]->v->el + o_src, n_div_ma); + o_dst += n_div_ma; + o_src += n_div_ma; + isl_seq_scale(dst + o_dst, dst + o_dst, c1, n_div_bmap); + if (has_denom) + isl_int_mul(dst[0], dst[0], c1); + } +} + +/* Compute the pullback of "aff" by the function represented by "ma". + * In other words, plug in "ma" in "aff". The result is an affine expression + * defined over the domain space of "ma". + * + * If "aff" is represented by + * + * (a(p) + b x + c(divs))/d + * + * and ma is represented by + * + * x = D(p) + F(y) + G(divs') + * + * then the result is + * + * (a(p) + b D(p) + b F(y) + b G(divs') + c(divs))/d + * + * The divs in the local space of the input are similarly adjusted + * through a call to isl_local_space_preimage_multi_aff. + */ +__isl_give isl_aff *isl_aff_pullback_multi_aff(__isl_take isl_aff *aff, + __isl_take isl_multi_aff *ma) +{ + isl_aff *res = NULL; + isl_local_space *ls; + int n_div_aff, n_div_ma; + isl_int f, c1, c2, g; + + ma = isl_multi_aff_align_divs(ma); + if (!aff || !ma) + goto error; + + n_div_aff = isl_aff_dim(aff, isl_dim_div); + n_div_ma = ma->n ? isl_aff_dim(ma->p[0], isl_dim_div) : 0; + + ls = isl_aff_get_domain_local_space(aff); + ls = isl_local_space_preimage_multi_aff(ls, isl_multi_aff_copy(ma)); + res = isl_aff_alloc(ls); + if (!res) + goto error; + + isl_int_init(f); + isl_int_init(c1); + isl_int_init(c2); + isl_int_init(g); + + isl_seq_preimage(res->v->el, aff->v->el, ma, 0, 0, n_div_ma, n_div_aff, + f, c1, c2, g, 1); + + isl_int_clear(f); + isl_int_clear(c1); + isl_int_clear(c2); + isl_int_clear(g); + + isl_aff_free(aff); + isl_multi_aff_free(ma); + res = isl_aff_normalize(res); + return res; +error: + isl_aff_free(aff); + isl_multi_aff_free(ma); + isl_aff_free(res); + return NULL; +} + +/* Compute the pullback of "aff1" by the function represented by "aff2". + * In other words, plug in "aff2" in "aff1". The result is an affine expression + * defined over the domain space of "aff1". + * + * The domain of "aff1" should match the range of "aff2", which means + * that it should be single-dimensional. + */ +__isl_give isl_aff *isl_aff_pullback_aff(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2) +{ + isl_multi_aff *ma; + + ma = isl_multi_aff_from_aff(aff2); + return isl_aff_pullback_multi_aff(aff1, ma); +} + +/* Compute the pullback of "ma1" by the function represented by "ma2". + * In other words, plug in "ma2" in "ma1". + * + * The parameters of "ma1" and "ma2" are assumed to have been aligned. + */ +static __isl_give isl_multi_aff *isl_multi_aff_pullback_multi_aff_aligned( + __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2) +{ + int i; + isl_space *space = NULL; + + ma2 = isl_multi_aff_align_divs(ma2); + ma1 = isl_multi_aff_cow(ma1); + if (!ma1 || !ma2) + goto error; + + space = isl_space_join(isl_multi_aff_get_space(ma2), + isl_multi_aff_get_space(ma1)); + + for (i = 0; i < ma1->n; ++i) { + ma1->p[i] = isl_aff_pullback_multi_aff(ma1->p[i], + isl_multi_aff_copy(ma2)); + if (!ma1->p[i]) + goto error; + } + + ma1 = isl_multi_aff_reset_space(ma1, space); + isl_multi_aff_free(ma2); + return ma1; +error: + isl_space_free(space); + isl_multi_aff_free(ma2); + isl_multi_aff_free(ma1); + return NULL; +} + +/* Compute the pullback of "ma1" by the function represented by "ma2". + * In other words, plug in "ma2" in "ma1". + */ +__isl_give isl_multi_aff *isl_multi_aff_pullback_multi_aff( + __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2) +{ + return isl_multi_aff_align_params_multi_multi_and(ma1, ma2, + &isl_multi_aff_pullback_multi_aff_aligned); +} + +/* Extend the local space of "dst" to include the divs + * in the local space of "src". + * + * If "src" does not have any divs or if the local spaces of "dst" and + * "src" are the same, then no extension is required. + */ +__isl_give isl_aff *isl_aff_align_divs(__isl_take isl_aff *dst, + __isl_keep isl_aff *src) +{ + isl_ctx *ctx; + int src_n_div, dst_n_div; + int *exp1 = NULL; + int *exp2 = NULL; + isl_bool equal; + isl_mat *div; + + if (!src || !dst) + return isl_aff_free(dst); + + ctx = isl_aff_get_ctx(src); + equal = isl_local_space_has_equal_space(src->ls, dst->ls); + if (equal < 0) + return isl_aff_free(dst); + if (!equal) + isl_die(ctx, isl_error_invalid, + "spaces don't match", goto error); + + src_n_div = isl_local_space_dim(src->ls, isl_dim_div); + if (src_n_div == 0) + return dst; + equal = isl_local_space_is_equal(src->ls, dst->ls); + if (equal < 0) + return isl_aff_free(dst); + if (equal) + return dst; + + dst_n_div = isl_local_space_dim(dst->ls, isl_dim_div); + exp1 = isl_alloc_array(ctx, int, src_n_div); + exp2 = isl_alloc_array(ctx, int, dst_n_div); + if (!exp1 || (dst_n_div && !exp2)) + goto error; + + div = isl_merge_divs(src->ls->div, dst->ls->div, exp1, exp2); + dst = isl_aff_expand_divs(dst, div, exp2); + free(exp1); + free(exp2); + + return dst; +error: + free(exp1); + free(exp2); + return isl_aff_free(dst); +} + +/* Adjust the local spaces of the affine expressions in "maff" + * such that they all have the save divs. + */ +__isl_give isl_multi_aff *isl_multi_aff_align_divs( + __isl_take isl_multi_aff *maff) +{ + int i; + + if (!maff) + return NULL; + if (maff->n == 0) + return maff; + maff = isl_multi_aff_cow(maff); + if (!maff) + return NULL; + + for (i = 1; i < maff->n; ++i) + maff->p[0] = isl_aff_align_divs(maff->p[0], maff->p[i]); + for (i = 1; i < maff->n; ++i) { + maff->p[i] = isl_aff_align_divs(maff->p[i], maff->p[0]); + if (!maff->p[i]) + return isl_multi_aff_free(maff); + } + + return maff; +} + +__isl_give isl_aff *isl_aff_lift(__isl_take isl_aff *aff) +{ + aff = isl_aff_cow(aff); + if (!aff) + return NULL; + + aff->ls = isl_local_space_lift(aff->ls); + if (!aff->ls) + return isl_aff_free(aff); + + return aff; +} + +/* Lift "maff" to a space with extra dimensions such that the result + * has no more existentially quantified variables. + * If "ls" is not NULL, then *ls is assigned the local space that lies + * at the basis of the lifting applied to "maff". + */ +__isl_give isl_multi_aff *isl_multi_aff_lift(__isl_take isl_multi_aff *maff, + __isl_give isl_local_space **ls) +{ + int i; + isl_space *space; + unsigned n_div; + + if (ls) + *ls = NULL; + + if (!maff) + return NULL; + + if (maff->n == 0) { + if (ls) { + isl_space *space = isl_multi_aff_get_domain_space(maff); + *ls = isl_local_space_from_space(space); + if (!*ls) + return isl_multi_aff_free(maff); + } + return maff; + } + + maff = isl_multi_aff_cow(maff); + maff = isl_multi_aff_align_divs(maff); + if (!maff) + return NULL; + + n_div = isl_aff_dim(maff->p[0], isl_dim_div); + space = isl_multi_aff_get_space(maff); + space = isl_space_lift(isl_space_domain(space), n_div); + space = isl_space_extend_domain_with_range(space, + isl_multi_aff_get_space(maff)); + if (!space) + return isl_multi_aff_free(maff); + isl_space_free(maff->space); + maff->space = space; + + if (ls) { + *ls = isl_aff_get_domain_local_space(maff->p[0]); + if (!*ls) + return isl_multi_aff_free(maff); + } + + for (i = 0; i < maff->n; ++i) { + maff->p[i] = isl_aff_lift(maff->p[i]); + if (!maff->p[i]) + goto error; + } + + return maff; +error: + if (ls) + isl_local_space_free(*ls); + return isl_multi_aff_free(maff); +} + + +/* Extract an isl_pw_aff corresponding to output dimension "pos" of "pma". + */ +__isl_give isl_pw_aff *isl_pw_multi_aff_get_pw_aff( + __isl_keep isl_pw_multi_aff *pma, int pos) +{ + int i; + int n_out; + isl_space *space; + isl_pw_aff *pa; + + if (!pma) + return NULL; + + n_out = isl_pw_multi_aff_dim(pma, isl_dim_out); + if (pos < 0 || pos >= n_out) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "index out of bounds", return NULL); + + space = isl_pw_multi_aff_get_space(pma); + space = isl_space_drop_dims(space, isl_dim_out, + pos + 1, n_out - pos - 1); + space = isl_space_drop_dims(space, isl_dim_out, 0, pos); + + pa = isl_pw_aff_alloc_size(space, pma->n); + for (i = 0; i < pma->n; ++i) { + isl_aff *aff; + aff = isl_multi_aff_get_aff(pma->p[i].maff, pos); + pa = isl_pw_aff_add_piece(pa, isl_set_copy(pma->p[i].set), aff); + } + + return pa; +} + +/* Return an isl_pw_multi_aff with the given "set" as domain and + * an unnamed zero-dimensional range. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_domain( + __isl_take isl_set *set) +{ + isl_multi_aff *ma; + isl_space *space; + + space = isl_set_get_space(set); + space = isl_space_from_domain(space); + ma = isl_multi_aff_zero(space); + return isl_pw_multi_aff_alloc(set, ma); +} + +/* Add an isl_pw_multi_aff with the given "set" as domain and + * an unnamed zero-dimensional range to *user. + */ +static isl_stat add_pw_multi_aff_from_domain(__isl_take isl_set *set, + void *user) +{ + isl_union_pw_multi_aff **upma = user; + isl_pw_multi_aff *pma; + + pma = isl_pw_multi_aff_from_domain(set); + *upma = isl_union_pw_multi_aff_add_pw_multi_aff(*upma, pma); + + return isl_stat_ok; +} + +/* Return an isl_union_pw_multi_aff with the given "uset" as domain and + * an unnamed zero-dimensional range. + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_domain( + __isl_take isl_union_set *uset) +{ + isl_space *space; + isl_union_pw_multi_aff *upma; + + if (!uset) + return NULL; + + space = isl_union_set_get_space(uset); + upma = isl_union_pw_multi_aff_empty(space); + + if (isl_union_set_foreach_set(uset, + &add_pw_multi_aff_from_domain, &upma) < 0) + goto error; + + isl_union_set_free(uset); + return upma; +error: + isl_union_set_free(uset); + isl_union_pw_multi_aff_free(upma); + return NULL; +} + +/* Convert "pma" to an isl_map and add it to *umap. + */ +static isl_stat map_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma, + void *user) +{ + isl_union_map **umap = user; + isl_map *map; + + map = isl_map_from_pw_multi_aff(pma); + *umap = isl_union_map_add_map(*umap, map); + + return isl_stat_ok; +} + +/* Construct a union map mapping the domain of the union + * piecewise multi-affine expression to its range, with each dimension + * in the range equated to the corresponding affine expression on its cell. + */ +__isl_give isl_union_map *isl_union_map_from_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma) +{ + isl_space *space; + isl_union_map *umap; + + if (!upma) + return NULL; + + space = isl_union_pw_multi_aff_get_space(upma); + umap = isl_union_map_empty(space); + + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, + &map_from_pw_multi_aff, &umap) < 0) + goto error; + + isl_union_pw_multi_aff_free(upma); + return umap; +error: + isl_union_pw_multi_aff_free(upma); + isl_union_map_free(umap); + return NULL; +} + +/* Local data for bin_entry and the callback "fn". + */ +struct isl_union_pw_multi_aff_bin_data { + isl_union_pw_multi_aff *upma2; + isl_union_pw_multi_aff *res; + isl_pw_multi_aff *pma; + isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, void *user); +}; + +/* Given an isl_pw_multi_aff from upma1, store it in data->pma + * and call data->fn for each isl_pw_multi_aff in data->upma2. + */ +static isl_stat bin_entry(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_union_pw_multi_aff_bin_data *data = user; + isl_stat r; + + data->pma = pma; + r = isl_union_pw_multi_aff_foreach_pw_multi_aff(data->upma2, + data->fn, data); + isl_pw_multi_aff_free(pma); + + return r; +} + +/* Call "fn" on each pair of isl_pw_multi_affs in "upma1" and "upma2". + * The isl_pw_multi_aff from upma1 is stored in data->pma (where data is + * passed as user field) and the isl_pw_multi_aff from upma2 is available + * as *entry. The callback should adjust data->res if desired. + */ +static __isl_give isl_union_pw_multi_aff *bin_op( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2, + isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, void *user)) +{ + isl_space *space; + struct isl_union_pw_multi_aff_bin_data data = { NULL, NULL, NULL, fn }; + + space = isl_union_pw_multi_aff_get_space(upma2); + upma1 = isl_union_pw_multi_aff_align_params(upma1, space); + space = isl_union_pw_multi_aff_get_space(upma1); + upma2 = isl_union_pw_multi_aff_align_params(upma2, space); + + if (!upma1 || !upma2) + goto error; + + data.upma2 = upma2; + data.res = isl_union_pw_multi_aff_alloc_same_size(upma1); + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma1, + &bin_entry, &data) < 0) + goto error; + + isl_union_pw_multi_aff_free(upma1); + isl_union_pw_multi_aff_free(upma2); + return data.res; +error: + isl_union_pw_multi_aff_free(upma1); + isl_union_pw_multi_aff_free(upma2); + isl_union_pw_multi_aff_free(data.res); + return NULL; +} + +/* Given two aligned isl_pw_multi_affs A -> B and C -> D, + * construct an isl_pw_multi_aff (A * C) -> [B -> D]. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_range_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + isl_space *space; + + space = isl_space_range_product(isl_pw_multi_aff_get_space(pma1), + isl_pw_multi_aff_get_space(pma2)); + return isl_pw_multi_aff_on_shared_domain_in(pma1, pma2, space, + &isl_multi_aff_range_product); +} + +/* Given two isl_pw_multi_affs A -> B and C -> D, + * construct an isl_pw_multi_aff (A * C) -> [B -> D]. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_range_product); +} + +/* Given two aligned isl_pw_multi_affs A -> B and C -> D, + * construct an isl_pw_multi_aff (A * C) -> (B, D). + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_flat_range_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + isl_space *space; + + space = isl_space_range_product(isl_pw_multi_aff_get_space(pma1), + isl_pw_multi_aff_get_space(pma2)); + space = isl_space_flatten_range(space); + return isl_pw_multi_aff_on_shared_domain_in(pma1, pma2, space, + &isl_multi_aff_flat_range_product); +} + +/* Given two isl_pw_multi_affs A -> B and C -> D, + * construct an isl_pw_multi_aff (A * C) -> (B, D). + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_flat_range_product( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2) +{ + return isl_pw_multi_aff_align_params_pw_pw_and(pma1, pma2, + &pw_multi_aff_flat_range_product); +} + +/* If data->pma and "pma2" have the same domain space, then compute + * their flat range product and the result to data->res. + */ +static isl_stat flat_range_product_entry(__isl_take isl_pw_multi_aff *pma2, + void *user) +{ + struct isl_union_pw_multi_aff_bin_data *data = user; + + if (!isl_space_tuple_is_equal(data->pma->dim, isl_dim_in, + pma2->dim, isl_dim_in)) { + isl_pw_multi_aff_free(pma2); + return isl_stat_ok; + } + + pma2 = isl_pw_multi_aff_flat_range_product( + isl_pw_multi_aff_copy(data->pma), pma2); + + data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma2); + + return isl_stat_ok; +} + +/* Given two isl_union_pw_multi_affs A -> B and C -> D, + * construct an isl_union_pw_multi_aff (A * C) -> (B, D). + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_flat_range_product( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2) +{ + return bin_op(upma1, upma2, &flat_range_product_entry); +} + +/* Replace the affine expressions at position "pos" in "pma" by "pa". + * The parameters are assumed to have been aligned. + * + * The implementation essentially performs an isl_pw_*_on_shared_domain, + * except that it works on two different isl_pw_* types. + */ +static __isl_give isl_pw_multi_aff *pw_multi_aff_set_pw_aff( + __isl_take isl_pw_multi_aff *pma, unsigned pos, + __isl_take isl_pw_aff *pa) +{ + int i, j, n; + isl_pw_multi_aff *res = NULL; + + if (!pma || !pa) + goto error; + + if (!isl_space_tuple_is_equal(pma->dim, isl_dim_in, + pa->dim, isl_dim_in)) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "domains don't match", goto error); + if (pos >= isl_pw_multi_aff_dim(pma, isl_dim_out)) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "index out of bounds", goto error); + + n = pma->n * pa->n; + res = isl_pw_multi_aff_alloc_size(isl_pw_multi_aff_get_space(pma), n); + + for (i = 0; i < pma->n; ++i) { + for (j = 0; j < pa->n; ++j) { + isl_set *common; + isl_multi_aff *res_ij; + int empty; + + common = isl_set_intersect(isl_set_copy(pma->p[i].set), + isl_set_copy(pa->p[j].set)); + empty = isl_set_plain_is_empty(common); + if (empty < 0 || empty) { + isl_set_free(common); + if (empty < 0) + goto error; + continue; + } + + res_ij = isl_multi_aff_set_aff( + isl_multi_aff_copy(pma->p[i].maff), pos, + isl_aff_copy(pa->p[j].aff)); + res_ij = isl_multi_aff_gist(res_ij, + isl_set_copy(common)); + + res = isl_pw_multi_aff_add_piece(res, common, res_ij); + } + } + + isl_pw_multi_aff_free(pma); + isl_pw_aff_free(pa); + return res; +error: + isl_pw_multi_aff_free(pma); + isl_pw_aff_free(pa); + return isl_pw_multi_aff_free(res); +} + +/* Replace the affine expressions at position "pos" in "pma" by "pa". + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_pw_aff( + __isl_take isl_pw_multi_aff *pma, unsigned pos, + __isl_take isl_pw_aff *pa) +{ + if (!pma || !pa) + goto error; + if (isl_space_match(pma->dim, isl_dim_param, pa->dim, isl_dim_param)) + return pw_multi_aff_set_pw_aff(pma, pos, pa); + if (!isl_space_has_named_params(pma->dim) || + !isl_space_has_named_params(pa->dim)) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "unaligned unnamed parameters", goto error); + pma = isl_pw_multi_aff_align_params(pma, isl_pw_aff_get_space(pa)); + pa = isl_pw_aff_align_params(pa, isl_pw_multi_aff_get_space(pma)); + return pw_multi_aff_set_pw_aff(pma, pos, pa); +error: + isl_pw_multi_aff_free(pma); + isl_pw_aff_free(pa); + return NULL; +} + +/* Do the parameters of "pa" match those of "space"? + */ +int isl_pw_aff_matching_params(__isl_keep isl_pw_aff *pa, + __isl_keep isl_space *space) +{ + isl_space *pa_space; + int match; + + if (!pa || !space) + return -1; + + pa_space = isl_pw_aff_get_space(pa); + + match = isl_space_match(space, isl_dim_param, pa_space, isl_dim_param); + + isl_space_free(pa_space); + return match; +} + +/* Check that the domain space of "pa" matches "space". + * + * Return 0 on success and -1 on error. + */ +int isl_pw_aff_check_match_domain_space(__isl_keep isl_pw_aff *pa, + __isl_keep isl_space *space) +{ + isl_space *pa_space; + int match; + + if (!pa || !space) + return -1; + + pa_space = isl_pw_aff_get_space(pa); + + match = isl_space_match(space, isl_dim_param, pa_space, isl_dim_param); + if (match < 0) + goto error; + if (!match) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "parameters don't match", goto error); + match = isl_space_tuple_is_equal(space, isl_dim_in, + pa_space, isl_dim_in); + if (match < 0) + goto error; + if (!match) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "domains don't match", goto error); + isl_space_free(pa_space); + return 0; +error: + isl_space_free(pa_space); + return -1; +} + +#undef BASE +#define BASE pw_aff +#undef DOMBASE +#define DOMBASE set + +#include +#include +#include +#include +#include +#include + +/* Scale the elements of "pma" by the corresponding elements of "mv". + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_multi_val( + __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_val *mv) +{ + int i; + + pma = isl_pw_multi_aff_cow(pma); + if (!pma || !mv) + goto error; + if (!isl_space_tuple_is_equal(pma->dim, isl_dim_out, + mv->space, isl_dim_set)) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "spaces don't match", goto error); + if (!isl_space_match(pma->dim, isl_dim_param, + mv->space, isl_dim_param)) { + pma = isl_pw_multi_aff_align_params(pma, + isl_multi_val_get_space(mv)); + mv = isl_multi_val_align_params(mv, + isl_pw_multi_aff_get_space(pma)); + if (!pma || !mv) + goto error; + } + + for (i = 0; i < pma->n; ++i) { + pma->p[i].maff = isl_multi_aff_scale_multi_val(pma->p[i].maff, + isl_multi_val_copy(mv)); + if (!pma->p[i].maff) + goto error; + } + + isl_multi_val_free(mv); + return pma; +error: + isl_multi_val_free(mv); + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* This function is called for each entry of an isl_union_pw_multi_aff. + * If the space of the entry matches that of data->mv, + * then apply isl_pw_multi_aff_scale_multi_val and return the result. + * Otherwise, return an empty isl_pw_multi_aff. + */ +static __isl_give isl_pw_multi_aff *union_pw_multi_aff_scale_multi_val_entry( + __isl_take isl_pw_multi_aff *pma, void *user) +{ + isl_multi_val *mv = user; + + if (!pma) + return NULL; + if (!isl_space_tuple_is_equal(pma->dim, isl_dim_out, + mv->space, isl_dim_set)) { + isl_space *space = isl_pw_multi_aff_get_space(pma); + isl_pw_multi_aff_free(pma); + return isl_pw_multi_aff_empty(space); + } + + return isl_pw_multi_aff_scale_multi_val(pma, isl_multi_val_copy(mv)); +} + +/* Scale the elements of "upma" by the corresponding elements of "mv", + * for those entries that match the space of "mv". + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_multi_val( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_multi_val *mv) +{ + upma = isl_union_pw_multi_aff_align_params(upma, + isl_multi_val_get_space(mv)); + mv = isl_multi_val_align_params(mv, + isl_union_pw_multi_aff_get_space(upma)); + if (!upma || !mv) + goto error; + + return isl_union_pw_multi_aff_transform(upma, + &union_pw_multi_aff_scale_multi_val_entry, mv); + + isl_multi_val_free(mv); + return upma; +error: + isl_multi_val_free(mv); + isl_union_pw_multi_aff_free(upma); + return NULL; +} + +/* Construct and return a piecewise multi affine expression + * in the given space with value zero in each of the output dimensions and + * a universe domain. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_zero(__isl_take isl_space *space) +{ + return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_zero(space)); +} + +/* Construct and return a piecewise multi affine expression + * that is equal to the given piecewise affine expression. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_pw_aff( + __isl_take isl_pw_aff *pa) +{ + int i; + isl_space *space; + isl_pw_multi_aff *pma; + + if (!pa) + return NULL; + + space = isl_pw_aff_get_space(pa); + pma = isl_pw_multi_aff_alloc_size(space, pa->n); + + for (i = 0; i < pa->n; ++i) { + isl_set *set; + isl_multi_aff *ma; + + set = isl_set_copy(pa->p[i].set); + ma = isl_multi_aff_from_aff(isl_aff_copy(pa->p[i].aff)); + pma = isl_pw_multi_aff_add_piece(pma, set, ma); + } + + isl_pw_aff_free(pa); + return pma; +} + +/* Construct a set or map mapping the shared (parameter) domain + * of the piecewise affine expressions to the range of "mpa" + * with each dimension in the range equated to the + * corresponding piecewise affine expression. + */ +static __isl_give isl_map *map_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa) +{ + int i; + isl_space *space; + isl_map *map; + + if (!mpa) + return NULL; + + if (isl_space_dim(mpa->space, isl_dim_out) != mpa->n) + isl_die(isl_multi_pw_aff_get_ctx(mpa), isl_error_internal, + "invalid space", goto error); + + space = isl_multi_pw_aff_get_domain_space(mpa); + map = isl_map_universe(isl_space_from_domain(space)); + + for (i = 0; i < mpa->n; ++i) { + isl_pw_aff *pa; + isl_map *map_i; + + pa = isl_pw_aff_copy(mpa->p[i]); + map_i = map_from_pw_aff(pa); + + map = isl_map_flat_range_product(map, map_i); + } + + map = isl_map_reset_space(map, isl_multi_pw_aff_get_space(mpa)); + + isl_multi_pw_aff_free(mpa); + return map; +error: + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Construct a map mapping the shared domain + * of the piecewise affine expressions to the range of "mpa" + * with each dimension in the range equated to the + * corresponding piecewise affine expression. + */ +__isl_give isl_map *isl_map_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa) +{ + if (!mpa) + return NULL; + if (isl_space_is_set(mpa->space)) + isl_die(isl_multi_pw_aff_get_ctx(mpa), isl_error_internal, + "space of input is not a map", goto error); + + return map_from_multi_pw_aff(mpa); +error: + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Construct a set mapping the shared parameter domain + * of the piecewise affine expressions to the space of "mpa" + * with each dimension in the range equated to the + * corresponding piecewise affine expression. + */ +__isl_give isl_set *isl_set_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa) +{ + if (!mpa) + return NULL; + if (!isl_space_is_set(mpa->space)) + isl_die(isl_multi_pw_aff_get_ctx(mpa), isl_error_internal, + "space of input is not a set", goto error); + + return map_from_multi_pw_aff(mpa); +error: + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Construct and return a piecewise multi affine expression + * that is equal to the given multi piecewise affine expression + * on the shared domain of the piecewise affine expressions. + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa) +{ + int i; + isl_space *space; + isl_pw_aff *pa; + isl_pw_multi_aff *pma; + + if (!mpa) + return NULL; + + space = isl_multi_pw_aff_get_space(mpa); + + if (mpa->n == 0) { + isl_multi_pw_aff_free(mpa); + return isl_pw_multi_aff_zero(space); + } + + pa = isl_multi_pw_aff_get_pw_aff(mpa, 0); + pma = isl_pw_multi_aff_from_pw_aff(pa); + + for (i = 1; i < mpa->n; ++i) { + isl_pw_multi_aff *pma_i; + + pa = isl_multi_pw_aff_get_pw_aff(mpa, i); + pma_i = isl_pw_multi_aff_from_pw_aff(pa); + pma = isl_pw_multi_aff_range_product(pma, pma_i); + } + + pma = isl_pw_multi_aff_reset_space(pma, space); + + isl_multi_pw_aff_free(mpa); + return pma; +} + +/* Construct and return a multi piecewise affine expression + * that is equal to the given multi affine expression. + */ +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_multi_aff( + __isl_take isl_multi_aff *ma) +{ + int i, n; + isl_multi_pw_aff *mpa; + + if (!ma) + return NULL; + + n = isl_multi_aff_dim(ma, isl_dim_out); + mpa = isl_multi_pw_aff_alloc(isl_multi_aff_get_space(ma)); + + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + + pa = isl_pw_aff_from_aff(isl_multi_aff_get_aff(ma, i)); + mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa); + } + + isl_multi_aff_free(ma); + return mpa; +} + +/* Construct and return a multi piecewise affine expression + * that is equal to the given piecewise multi affine expression. + */ +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_multi_aff( + __isl_take isl_pw_multi_aff *pma) +{ + int i, n; + isl_space *space; + isl_multi_pw_aff *mpa; + + if (!pma) + return NULL; + + n = isl_pw_multi_aff_dim(pma, isl_dim_out); + space = isl_pw_multi_aff_get_space(pma); + mpa = isl_multi_pw_aff_alloc(space); + + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + + pa = isl_pw_multi_aff_get_pw_aff(pma, i); + mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa); + } + + isl_pw_multi_aff_free(pma); + return mpa; +} + +/* Do "pa1" and "pa2" represent the same function? + * + * We first check if they are obviously equal. + * If not, we convert them to maps and check if those are equal. + * + * If "pa1" or "pa2" contain any NaNs, then they are considered + * not to be the same. A NaN is not equal to anything, not even + * to another NaN. + */ +int isl_pw_aff_is_equal(__isl_keep isl_pw_aff *pa1, __isl_keep isl_pw_aff *pa2) +{ + int equal; + isl_bool has_nan; + isl_map *map1, *map2; + + if (!pa1 || !pa2) + return -1; + + equal = isl_pw_aff_plain_is_equal(pa1, pa2); + if (equal < 0 || equal) + return equal; + has_nan = isl_pw_aff_involves_nan(pa1); + if (has_nan >= 0 && !has_nan) + has_nan = isl_pw_aff_involves_nan(pa2); + if (has_nan < 0) + return -1; + if (has_nan) + return 0; + + map1 = map_from_pw_aff(isl_pw_aff_copy(pa1)); + map2 = map_from_pw_aff(isl_pw_aff_copy(pa2)); + equal = isl_map_is_equal(map1, map2); + isl_map_free(map1); + isl_map_free(map2); + + return equal; +} + +/* Do "mpa1" and "mpa2" represent the same function? + * + * Note that we cannot convert the entire isl_multi_pw_aff + * to a map because the domains of the piecewise affine expressions + * may not be the same. + */ +isl_bool isl_multi_pw_aff_is_equal(__isl_keep isl_multi_pw_aff *mpa1, + __isl_keep isl_multi_pw_aff *mpa2) +{ + int i; + isl_bool equal; + + if (!mpa1 || !mpa2) + return isl_bool_error; + + if (!isl_space_match(mpa1->space, isl_dim_param, + mpa2->space, isl_dim_param)) { + if (!isl_space_has_named_params(mpa1->space)) + return isl_bool_false; + if (!isl_space_has_named_params(mpa2->space)) + return isl_bool_false; + mpa1 = isl_multi_pw_aff_copy(mpa1); + mpa2 = isl_multi_pw_aff_copy(mpa2); + mpa1 = isl_multi_pw_aff_align_params(mpa1, + isl_multi_pw_aff_get_space(mpa2)); + mpa2 = isl_multi_pw_aff_align_params(mpa2, + isl_multi_pw_aff_get_space(mpa1)); + equal = isl_multi_pw_aff_is_equal(mpa1, mpa2); + isl_multi_pw_aff_free(mpa1); + isl_multi_pw_aff_free(mpa2); + return equal; + } + + equal = isl_space_is_equal(mpa1->space, mpa2->space); + if (equal < 0 || !equal) + return equal; + + for (i = 0; i < mpa1->n; ++i) { + equal = isl_pw_aff_is_equal(mpa1->p[i], mpa2->p[i]); + if (equal < 0 || !equal) + return equal; + } + + return isl_bool_true; +} + +/* Compute the pullback of "mpa" by the function represented by "ma". + * In other words, plug in "ma" in "mpa". + * + * The parameters of "mpa" and "ma" are assumed to have been aligned. + */ +static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff_aligned( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma) +{ + int i; + isl_space *space = NULL; + + mpa = isl_multi_pw_aff_cow(mpa); + if (!mpa || !ma) + goto error; + + space = isl_space_join(isl_multi_aff_get_space(ma), + isl_multi_pw_aff_get_space(mpa)); + if (!space) + goto error; + + for (i = 0; i < mpa->n; ++i) { + mpa->p[i] = isl_pw_aff_pullback_multi_aff(mpa->p[i], + isl_multi_aff_copy(ma)); + if (!mpa->p[i]) + goto error; + } + + isl_multi_aff_free(ma); + isl_space_free(mpa->space); + mpa->space = space; + return mpa; +error: + isl_space_free(space); + isl_multi_pw_aff_free(mpa); + isl_multi_aff_free(ma); + return NULL; +} + +/* Compute the pullback of "mpa" by the function represented by "ma". + * In other words, plug in "ma" in "mpa". + */ +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma) +{ + if (!mpa || !ma) + goto error; + if (isl_space_match(mpa->space, isl_dim_param, + ma->space, isl_dim_param)) + return isl_multi_pw_aff_pullback_multi_aff_aligned(mpa, ma); + mpa = isl_multi_pw_aff_align_params(mpa, isl_multi_aff_get_space(ma)); + ma = isl_multi_aff_align_params(ma, isl_multi_pw_aff_get_space(mpa)); + return isl_multi_pw_aff_pullback_multi_aff_aligned(mpa, ma); +error: + isl_multi_pw_aff_free(mpa); + isl_multi_aff_free(ma); + return NULL; +} + +/* Compute the pullback of "mpa" by the function represented by "pma". + * In other words, plug in "pma" in "mpa". + * + * The parameters of "mpa" and "mpa" are assumed to have been aligned. + */ +static __isl_give isl_multi_pw_aff * +isl_multi_pw_aff_pullback_pw_multi_aff_aligned( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_multi_aff *pma) +{ + int i; + isl_space *space = NULL; + + mpa = isl_multi_pw_aff_cow(mpa); + if (!mpa || !pma) + goto error; + + space = isl_space_join(isl_pw_multi_aff_get_space(pma), + isl_multi_pw_aff_get_space(mpa)); + + for (i = 0; i < mpa->n; ++i) { + mpa->p[i] = isl_pw_aff_pullback_pw_multi_aff_aligned(mpa->p[i], + isl_pw_multi_aff_copy(pma)); + if (!mpa->p[i]) + goto error; + } + + isl_pw_multi_aff_free(pma); + isl_space_free(mpa->space); + mpa->space = space; + return mpa; +error: + isl_space_free(space); + isl_multi_pw_aff_free(mpa); + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Compute the pullback of "mpa" by the function represented by "pma". + * In other words, plug in "pma" in "mpa". + */ +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_pw_multi_aff( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_multi_aff *pma) +{ + if (!mpa || !pma) + goto error; + if (isl_space_match(mpa->space, isl_dim_param, pma->dim, isl_dim_param)) + return isl_multi_pw_aff_pullback_pw_multi_aff_aligned(mpa, pma); + mpa = isl_multi_pw_aff_align_params(mpa, + isl_pw_multi_aff_get_space(pma)); + pma = isl_pw_multi_aff_align_params(pma, + isl_multi_pw_aff_get_space(mpa)); + return isl_multi_pw_aff_pullback_pw_multi_aff_aligned(mpa, pma); +error: + isl_multi_pw_aff_free(mpa); + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Apply "aff" to "mpa". The range of "mpa" needs to be compatible + * with the domain of "aff". The domain of the result is the same + * as that of "mpa". + * "mpa" and "aff" are assumed to have been aligned. + * + * We first extract the parametric constant from "aff", defined + * over the correct domain. + * Then we add the appropriate combinations of the members of "mpa". + * Finally, we add the integer divisions through recursive calls. + */ +static __isl_give isl_pw_aff *isl_multi_pw_aff_apply_aff_aligned( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_aff *aff) +{ + int i, n_in, n_div; + isl_space *space; + isl_val *v; + isl_pw_aff *pa; + isl_aff *tmp; + + n_in = isl_aff_dim(aff, isl_dim_in); + n_div = isl_aff_dim(aff, isl_dim_div); + + space = isl_space_domain(isl_multi_pw_aff_get_space(mpa)); + tmp = isl_aff_copy(aff); + tmp = isl_aff_drop_dims(tmp, isl_dim_div, 0, n_div); + tmp = isl_aff_drop_dims(tmp, isl_dim_in, 0, n_in); + tmp = isl_aff_add_dims(tmp, isl_dim_in, + isl_space_dim(space, isl_dim_set)); + tmp = isl_aff_reset_domain_space(tmp, space); + pa = isl_pw_aff_from_aff(tmp); + + for (i = 0; i < n_in; ++i) { + isl_pw_aff *pa_i; + + if (!isl_aff_involves_dims(aff, isl_dim_in, i, 1)) + continue; + v = isl_aff_get_coefficient_val(aff, isl_dim_in, i); + pa_i = isl_multi_pw_aff_get_pw_aff(mpa, i); + pa_i = isl_pw_aff_scale_val(pa_i, v); + pa = isl_pw_aff_add(pa, pa_i); + } + + for (i = 0; i < n_div; ++i) { + isl_aff *div; + isl_pw_aff *pa_i; + + if (!isl_aff_involves_dims(aff, isl_dim_div, i, 1)) + continue; + div = isl_aff_get_div(aff, i); + pa_i = isl_multi_pw_aff_apply_aff_aligned( + isl_multi_pw_aff_copy(mpa), div); + pa_i = isl_pw_aff_floor(pa_i); + v = isl_aff_get_coefficient_val(aff, isl_dim_div, i); + pa_i = isl_pw_aff_scale_val(pa_i, v); + pa = isl_pw_aff_add(pa, pa_i); + } + + isl_multi_pw_aff_free(mpa); + isl_aff_free(aff); + + return pa; +} + +/* Apply "aff" to "mpa". The range of "mpa" needs to be compatible + * with the domain of "aff". The domain of the result is the same + * as that of "mpa". + */ +__isl_give isl_pw_aff *isl_multi_pw_aff_apply_aff( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_aff *aff) +{ + if (!aff || !mpa) + goto error; + if (isl_space_match(aff->ls->dim, isl_dim_param, + mpa->space, isl_dim_param)) + return isl_multi_pw_aff_apply_aff_aligned(mpa, aff); + + aff = isl_aff_align_params(aff, isl_multi_pw_aff_get_space(mpa)); + mpa = isl_multi_pw_aff_align_params(mpa, isl_aff_get_space(aff)); + + return isl_multi_pw_aff_apply_aff_aligned(mpa, aff); +error: + isl_aff_free(aff); + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Apply "pa" to "mpa". The range of "mpa" needs to be compatible + * with the domain of "pa". The domain of the result is the same + * as that of "mpa". + * "mpa" and "pa" are assumed to have been aligned. + * + * We consider each piece in turn. Note that the domains of the + * pieces are assumed to be disjoint and they remain disjoint + * after taking the preimage (over the same function). + */ +static __isl_give isl_pw_aff *isl_multi_pw_aff_apply_pw_aff_aligned( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_aff *pa) +{ + isl_space *space; + isl_pw_aff *res; + int i; + + if (!mpa || !pa) + goto error; + + space = isl_space_join(isl_multi_pw_aff_get_space(mpa), + isl_pw_aff_get_space(pa)); + res = isl_pw_aff_empty(space); + + for (i = 0; i < pa->n; ++i) { + isl_pw_aff *pa_i; + isl_set *domain; + + pa_i = isl_multi_pw_aff_apply_aff_aligned( + isl_multi_pw_aff_copy(mpa), + isl_aff_copy(pa->p[i].aff)); + domain = isl_set_copy(pa->p[i].set); + domain = isl_set_preimage_multi_pw_aff(domain, + isl_multi_pw_aff_copy(mpa)); + pa_i = isl_pw_aff_intersect_domain(pa_i, domain); + res = isl_pw_aff_add_disjoint(res, pa_i); + } + + isl_pw_aff_free(pa); + isl_multi_pw_aff_free(mpa); + return res; +error: + isl_pw_aff_free(pa); + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Apply "pa" to "mpa". The range of "mpa" needs to be compatible + * with the domain of "pa". The domain of the result is the same + * as that of "mpa". + */ +__isl_give isl_pw_aff *isl_multi_pw_aff_apply_pw_aff( + __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_aff *pa) +{ + if (!pa || !mpa) + goto error; + if (isl_space_match(pa->dim, isl_dim_param, mpa->space, isl_dim_param)) + return isl_multi_pw_aff_apply_pw_aff_aligned(mpa, pa); + + pa = isl_pw_aff_align_params(pa, isl_multi_pw_aff_get_space(mpa)); + mpa = isl_multi_pw_aff_align_params(mpa, isl_pw_aff_get_space(pa)); + + return isl_multi_pw_aff_apply_pw_aff_aligned(mpa, pa); +error: + isl_pw_aff_free(pa); + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Compute the pullback of "pa" by the function represented by "mpa". + * In other words, plug in "mpa" in "pa". + * "pa" and "mpa" are assumed to have been aligned. + * + * The pullback is computed by applying "pa" to "mpa". + */ +static __isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff_aligned( + __isl_take isl_pw_aff *pa, __isl_take isl_multi_pw_aff *mpa) +{ + return isl_multi_pw_aff_apply_pw_aff_aligned(mpa, pa); +} + +/* Compute the pullback of "pa" by the function represented by "mpa". + * In other words, plug in "mpa" in "pa". + * + * The pullback is computed by applying "pa" to "mpa". + */ +__isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff( + __isl_take isl_pw_aff *pa, __isl_take isl_multi_pw_aff *mpa) +{ + return isl_multi_pw_aff_apply_pw_aff(mpa, pa); +} + +/* Compute the pullback of "mpa1" by the function represented by "mpa2". + * In other words, plug in "mpa2" in "mpa1". + * + * The parameters of "mpa1" and "mpa2" are assumed to have been aligned. + * + * We pullback each member of "mpa1" in turn. + */ +static __isl_give isl_multi_pw_aff * +isl_multi_pw_aff_pullback_multi_pw_aff_aligned( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2) +{ + int i; + isl_space *space = NULL; + + mpa1 = isl_multi_pw_aff_cow(mpa1); + if (!mpa1 || !mpa2) + goto error; + + space = isl_space_join(isl_multi_pw_aff_get_space(mpa2), + isl_multi_pw_aff_get_space(mpa1)); + + for (i = 0; i < mpa1->n; ++i) { + mpa1->p[i] = isl_pw_aff_pullback_multi_pw_aff_aligned( + mpa1->p[i], isl_multi_pw_aff_copy(mpa2)); + if (!mpa1->p[i]) + goto error; + } + + mpa1 = isl_multi_pw_aff_reset_space(mpa1, space); + + isl_multi_pw_aff_free(mpa2); + return mpa1; +error: + isl_space_free(space); + isl_multi_pw_aff_free(mpa1); + isl_multi_pw_aff_free(mpa2); + return NULL; +} + +/* Compute the pullback of "mpa1" by the function represented by "mpa2". + * In other words, plug in "mpa2" in "mpa1". + */ +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2) +{ + return isl_multi_pw_aff_align_params_multi_multi_and(mpa1, mpa2, + &isl_multi_pw_aff_pullback_multi_pw_aff_aligned); +} + +/* Align the parameters of "mpa1" and "mpa2", check that the ranges + * of "mpa1" and "mpa2" live in the same space, construct map space + * between the domain spaces of "mpa1" and "mpa2" and call "order" + * with this map space as extract argument. + */ +static __isl_give isl_map *isl_multi_pw_aff_order_map( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2, + __isl_give isl_map *(*order)(__isl_keep isl_multi_pw_aff *mpa1, + __isl_keep isl_multi_pw_aff *mpa2, __isl_take isl_space *space)) +{ + int match; + isl_space *space1, *space2; + isl_map *res; + + mpa1 = isl_multi_pw_aff_align_params(mpa1, + isl_multi_pw_aff_get_space(mpa2)); + mpa2 = isl_multi_pw_aff_align_params(mpa2, + isl_multi_pw_aff_get_space(mpa1)); + if (!mpa1 || !mpa2) + goto error; + match = isl_space_tuple_is_equal(mpa1->space, isl_dim_out, + mpa2->space, isl_dim_out); + if (match < 0) + goto error; + if (!match) + isl_die(isl_multi_pw_aff_get_ctx(mpa1), isl_error_invalid, + "range spaces don't match", goto error); + space1 = isl_space_domain(isl_multi_pw_aff_get_space(mpa1)); + space2 = isl_space_domain(isl_multi_pw_aff_get_space(mpa2)); + space1 = isl_space_map_from_domain_and_range(space1, space2); + + res = order(mpa1, mpa2, space1); + isl_multi_pw_aff_free(mpa1); + isl_multi_pw_aff_free(mpa2); + return res; +error: + isl_multi_pw_aff_free(mpa1); + isl_multi_pw_aff_free(mpa2); + return NULL; +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function values are equal. "space" is the space of the result. + * The parameters of "mpa1" and "mpa2" are assumed to have been aligned. + * + * "mpa1" and "mpa2" are equal when each of the pairs of elements + * in the sequences are equal. + */ +static __isl_give isl_map *isl_multi_pw_aff_eq_map_on_space( + __isl_keep isl_multi_pw_aff *mpa1, __isl_keep isl_multi_pw_aff *mpa2, + __isl_take isl_space *space) +{ + int i, n; + isl_map *res; + + res = isl_map_universe(space); + + n = isl_multi_pw_aff_dim(mpa1, isl_dim_out); + for (i = 0; i < n; ++i) { + isl_pw_aff *pa1, *pa2; + isl_map *map; + + pa1 = isl_multi_pw_aff_get_pw_aff(mpa1, i); + pa2 = isl_multi_pw_aff_get_pw_aff(mpa2, i); + map = isl_pw_aff_eq_map(pa1, pa2); + res = isl_map_intersect(res, map); + } + + return res; +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function values are equal. + */ +__isl_give isl_map *isl_multi_pw_aff_eq_map(__isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2) +{ + return isl_multi_pw_aff_order_map(mpa1, mpa2, + &isl_multi_pw_aff_eq_map_on_space); +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function values of "mpa1" is lexicographically satisfies "base" + * compared to that of "mpa2". "space" is the space of the result. + * The parameters of "mpa1" and "mpa2" are assumed to have been aligned. + * + * "mpa1" lexicographically satisfies "base" compared to "mpa2" + * if its i-th element satisfies "base" when compared to + * the i-th element of "mpa2" while all previous elements are + * pairwise equal. + */ +static __isl_give isl_map *isl_multi_pw_aff_lex_map_on_space( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2, + __isl_give isl_map *(*base)(__isl_take isl_pw_aff *pa1, + __isl_take isl_pw_aff *pa2), + __isl_take isl_space *space) +{ + int i, n; + isl_map *res, *rest; + + res = isl_map_empty(isl_space_copy(space)); + rest = isl_map_universe(space); + + n = isl_multi_pw_aff_dim(mpa1, isl_dim_out); + for (i = 0; i < n; ++i) { + isl_pw_aff *pa1, *pa2; + isl_map *map; + + pa1 = isl_multi_pw_aff_get_pw_aff(mpa1, i); + pa2 = isl_multi_pw_aff_get_pw_aff(mpa2, i); + map = base(pa1, pa2); + map = isl_map_intersect(map, isl_map_copy(rest)); + res = isl_map_union(res, map); + + if (i == n - 1) + continue; + + pa1 = isl_multi_pw_aff_get_pw_aff(mpa1, i); + pa2 = isl_multi_pw_aff_get_pw_aff(mpa2, i); + map = isl_pw_aff_eq_map(pa1, pa2); + rest = isl_map_intersect(rest, map); + } + + isl_map_free(rest); + return res; +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function value of "mpa1" is lexicographically less than that + * of "mpa2". "space" is the space of the result. + * The parameters of "mpa1" and "mpa2" are assumed to have been aligned. + * + * "mpa1" is less than "mpa2" if its i-th element is smaller + * than the i-th element of "mpa2" while all previous elements are + * pairwise equal. + */ +__isl_give isl_map *isl_multi_pw_aff_lex_lt_map_on_space( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2, + __isl_take isl_space *space) +{ + return isl_multi_pw_aff_lex_map_on_space(mpa1, mpa2, + &isl_pw_aff_lt_map, space); +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function value of "mpa1" is lexicographically less than that + * of "mpa2". + */ +__isl_give isl_map *isl_multi_pw_aff_lex_lt_map( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2) +{ + return isl_multi_pw_aff_order_map(mpa1, mpa2, + &isl_multi_pw_aff_lex_lt_map_on_space); +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function value of "mpa1" is lexicographically greater than that + * of "mpa2". "space" is the space of the result. + * The parameters of "mpa1" and "mpa2" are assumed to have been aligned. + * + * "mpa1" is greater than "mpa2" if its i-th element is greater + * than the i-th element of "mpa2" while all previous elements are + * pairwise equal. + */ +__isl_give isl_map *isl_multi_pw_aff_lex_gt_map_on_space( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2, + __isl_take isl_space *space) +{ + return isl_multi_pw_aff_lex_map_on_space(mpa1, mpa2, + &isl_pw_aff_gt_map, space); +} + +/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2" + * where the function value of "mpa1" is lexicographically greater than that + * of "mpa2". + */ +__isl_give isl_map *isl_multi_pw_aff_lex_gt_map( + __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2) +{ + return isl_multi_pw_aff_order_map(mpa1, mpa2, + &isl_multi_pw_aff_lex_gt_map_on_space); +} + +/* Compare two isl_affs. + * + * Return -1 if "aff1" is "smaller" than "aff2", 1 if "aff1" is "greater" + * than "aff2" and 0 if they are equal. + * + * The order is fairly arbitrary. We do consider expressions that only involve + * earlier dimensions as "smaller". + */ +int isl_aff_plain_cmp(__isl_keep isl_aff *aff1, __isl_keep isl_aff *aff2) +{ + int cmp; + int last1, last2; + + if (aff1 == aff2) + return 0; + + if (!aff1) + return -1; + if (!aff2) + return 1; + + cmp = isl_local_space_cmp(aff1->ls, aff2->ls); + if (cmp != 0) + return cmp; + + last1 = isl_seq_last_non_zero(aff1->v->el + 1, aff1->v->size - 1); + last2 = isl_seq_last_non_zero(aff2->v->el + 1, aff1->v->size - 1); + if (last1 != last2) + return last1 - last2; + + return isl_seq_cmp(aff1->v->el, aff2->v->el, aff1->v->size); +} + +/* Compare two isl_pw_affs. + * + * Return -1 if "pa1" is "smaller" than "pa2", 1 if "pa1" is "greater" + * than "pa2" and 0 if they are equal. + * + * The order is fairly arbitrary. We do consider expressions that only involve + * earlier dimensions as "smaller". + */ +int isl_pw_aff_plain_cmp(__isl_keep isl_pw_aff *pa1, + __isl_keep isl_pw_aff *pa2) +{ + int i; + int cmp; + + if (pa1 == pa2) + return 0; + + if (!pa1) + return -1; + if (!pa2) + return 1; + + cmp = isl_space_cmp(pa1->dim, pa2->dim); + if (cmp != 0) + return cmp; + + if (pa1->n != pa2->n) + return pa1->n - pa2->n; + + for (i = 0; i < pa1->n; ++i) { + cmp = isl_set_plain_cmp(pa1->p[i].set, pa2->p[i].set); + if (cmp != 0) + return cmp; + cmp = isl_aff_plain_cmp(pa1->p[i].aff, pa2->p[i].aff); + if (cmp != 0) + return cmp; + } + + return 0; +} + +/* Return a piecewise affine expression that is equal to "v" on "domain". + */ +__isl_give isl_pw_aff *isl_pw_aff_val_on_domain(__isl_take isl_set *domain, + __isl_take isl_val *v) +{ + isl_space *space; + isl_local_space *ls; + isl_aff *aff; + + space = isl_set_get_space(domain); + ls = isl_local_space_from_space(space); + aff = isl_aff_val_on_domain(ls, v); + + return isl_pw_aff_alloc(domain, aff); +} + +/* Return a multi affine expression that is equal to "mv" on domain + * space "space". + */ +__isl_give isl_multi_aff *isl_multi_aff_multi_val_on_space( + __isl_take isl_space *space, __isl_take isl_multi_val *mv) +{ + int i, n; + isl_space *space2; + isl_local_space *ls; + isl_multi_aff *ma; + + if (!space || !mv) + goto error; + + n = isl_multi_val_dim(mv, isl_dim_set); + space2 = isl_multi_val_get_space(mv); + space2 = isl_space_align_params(space2, isl_space_copy(space)); + space = isl_space_align_params(space, isl_space_copy(space2)); + space = isl_space_map_from_domain_and_range(space, space2); + ma = isl_multi_aff_alloc(isl_space_copy(space)); + ls = isl_local_space_from_space(isl_space_domain(space)); + for (i = 0; i < n; ++i) { + isl_val *v; + isl_aff *aff; + + v = isl_multi_val_get_val(mv, i); + aff = isl_aff_val_on_domain(isl_local_space_copy(ls), v); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + isl_local_space_free(ls); + + isl_multi_val_free(mv); + return ma; +error: + isl_space_free(space); + isl_multi_val_free(mv); + return NULL; +} + +/* Return a piecewise multi-affine expression + * that is equal to "mv" on "domain". + */ +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_multi_val_on_domain( + __isl_take isl_set *domain, __isl_take isl_multi_val *mv) +{ + isl_space *space; + isl_multi_aff *ma; + + space = isl_set_get_space(domain); + ma = isl_multi_aff_multi_val_on_space(space, mv); + + return isl_pw_multi_aff_alloc(domain, ma); +} + +/* Internal data structure for isl_union_pw_multi_aff_multi_val_on_domain. + * mv is the value that should be attained on each domain set + * res collects the results + */ +struct isl_union_pw_multi_aff_multi_val_on_domain_data { + isl_multi_val *mv; + isl_union_pw_multi_aff *res; +}; + +/* Create an isl_pw_multi_aff equal to data->mv on "domain" + * and add it to data->res. + */ +static isl_stat pw_multi_aff_multi_val_on_domain(__isl_take isl_set *domain, + void *user) +{ + struct isl_union_pw_multi_aff_multi_val_on_domain_data *data = user; + isl_pw_multi_aff *pma; + isl_multi_val *mv; + + mv = isl_multi_val_copy(data->mv); + pma = isl_pw_multi_aff_multi_val_on_domain(domain, mv); + data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Return a union piecewise multi-affine expression + * that is equal to "mv" on "domain". + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_multi_val_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv) +{ + struct isl_union_pw_multi_aff_multi_val_on_domain_data data; + isl_space *space; + + space = isl_union_set_get_space(domain); + data.res = isl_union_pw_multi_aff_empty(space); + data.mv = mv; + if (isl_union_set_foreach_set(domain, + &pw_multi_aff_multi_val_on_domain, &data) < 0) + data.res = isl_union_pw_multi_aff_free(data.res); + isl_union_set_free(domain); + isl_multi_val_free(mv); + return data.res; +} + +/* Compute the pullback of data->pma by the function represented by "pma2", + * provided the spaces match, and add the results to data->res. + */ +static isl_stat pullback_entry(__isl_take isl_pw_multi_aff *pma2, void *user) +{ + struct isl_union_pw_multi_aff_bin_data *data = user; + + if (!isl_space_tuple_is_equal(data->pma->dim, isl_dim_in, + pma2->dim, isl_dim_out)) { + isl_pw_multi_aff_free(pma2); + return isl_stat_ok; + } + + pma2 = isl_pw_multi_aff_pullback_pw_multi_aff( + isl_pw_multi_aff_copy(data->pma), pma2); + + data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma2); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Compute the pullback of "upma1" by the function represented by "upma2". + */ +__isl_give isl_union_pw_multi_aff * +isl_union_pw_multi_aff_pullback_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2) +{ + return bin_op(upma1, upma2, &pullback_entry); +} + +/* Check that the domain space of "upa" matches "space". + * + * Return 0 on success and -1 on error. + * + * This function is called from isl_multi_union_pw_aff_set_union_pw_aff and + * can in principle never fail since the space "space" is that + * of the isl_multi_union_pw_aff and is a set space such that + * there is no domain space to match. + * + * We check the parameters and double-check that "space" is + * indeed that of a set. + */ +static int isl_union_pw_aff_check_match_domain_space( + __isl_keep isl_union_pw_aff *upa, __isl_keep isl_space *space) +{ + isl_space *upa_space; + int match; + + if (!upa || !space) + return -1; + + match = isl_space_is_set(space); + if (match < 0) + return -1; + if (!match) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "expecting set space", return -1); + + upa_space = isl_union_pw_aff_get_space(upa); + match = isl_space_match(space, isl_dim_param, upa_space, isl_dim_param); + if (match < 0) + goto error; + if (!match) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "parameters don't match", goto error); + + isl_space_free(upa_space); + return 0; +error: + isl_space_free(upa_space); + return -1; +} + +/* Do the parameters of "upa" match those of "space"? + */ +static int isl_union_pw_aff_matching_params(__isl_keep isl_union_pw_aff *upa, + __isl_keep isl_space *space) +{ + isl_space *upa_space; + int match; + + if (!upa || !space) + return -1; + + upa_space = isl_union_pw_aff_get_space(upa); + + match = isl_space_match(space, isl_dim_param, upa_space, isl_dim_param); + + isl_space_free(upa_space); + return match; +} + +/* Internal data structure for isl_union_pw_aff_reset_domain_space. + * space represents the new parameters. + * res collects the results. + */ +struct isl_union_pw_aff_reset_params_data { + isl_space *space; + isl_union_pw_aff *res; +}; + +/* Replace the parameters of "pa" by data->space and + * add the result to data->res. + */ +static isl_stat reset_params(__isl_take isl_pw_aff *pa, void *user) +{ + struct isl_union_pw_aff_reset_params_data *data = user; + isl_space *space; + + space = isl_pw_aff_get_space(pa); + space = isl_space_replace(space, isl_dim_param, data->space); + pa = isl_pw_aff_reset_space(pa, space); + data->res = isl_union_pw_aff_add_pw_aff(data->res, pa); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Replace the domain space of "upa" by "space". + * Since a union expression does not have a (single) domain space, + * "space" is necessarily a parameter space. + * + * Since the order and the names of the parameters determine + * the hash value, we need to create a new hash table. + */ +static __isl_give isl_union_pw_aff *isl_union_pw_aff_reset_domain_space( + __isl_take isl_union_pw_aff *upa, __isl_take isl_space *space) +{ + struct isl_union_pw_aff_reset_params_data data = { space }; + int match; + + match = isl_union_pw_aff_matching_params(upa, space); + if (match < 0) + upa = isl_union_pw_aff_free(upa); + else if (match) { + isl_space_free(space); + return upa; + } + + data.res = isl_union_pw_aff_empty(isl_space_copy(space)); + if (isl_union_pw_aff_foreach_pw_aff(upa, &reset_params, &data) < 0) + data.res = isl_union_pw_aff_free(data.res); + + isl_union_pw_aff_free(upa); + isl_space_free(space); + return data.res; +} + +/* Return the floor of "pa". + */ +static __isl_give isl_pw_aff *floor_entry(__isl_take isl_pw_aff *pa, void *user) +{ + return isl_pw_aff_floor(pa); +} + +/* Given f, return floor(f). + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_floor( + __isl_take isl_union_pw_aff *upa) +{ + return isl_union_pw_aff_transform_inplace(upa, &floor_entry, NULL); +} + +/* Compute + * + * upa mod m = upa - m * floor(upa/m) + * + * with m an integer value. + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_mod_val( + __isl_take isl_union_pw_aff *upa, __isl_take isl_val *m) +{ + isl_union_pw_aff *res; + + if (!upa || !m) + goto error; + + if (!isl_val_is_int(m)) + isl_die(isl_val_get_ctx(m), isl_error_invalid, + "expecting integer modulo", goto error); + if (!isl_val_is_pos(m)) + isl_die(isl_val_get_ctx(m), isl_error_invalid, + "expecting positive modulo", goto error); + + res = isl_union_pw_aff_copy(upa); + upa = isl_union_pw_aff_scale_down_val(upa, isl_val_copy(m)); + upa = isl_union_pw_aff_floor(upa); + upa = isl_union_pw_aff_scale_val(upa, m); + res = isl_union_pw_aff_sub(res, upa); + + return res; +error: + isl_val_free(m); + isl_union_pw_aff_free(upa); + return NULL; +} + +/* Internal data structure for isl_union_pw_aff_aff_on_domain. + * "aff" is the symbolic value that the resulting isl_union_pw_aff + * needs to attain. + * "res" collects the results. + */ +struct isl_union_pw_aff_aff_on_domain_data { + isl_aff *aff; + isl_union_pw_aff *res; +}; + +/* Construct a piecewise affine expression that is equal to data->aff + * on "domain" and add the result to data->res. + */ +static isl_stat pw_aff_aff_on_domain(__isl_take isl_set *domain, void *user) +{ + struct isl_union_pw_aff_aff_on_domain_data *data = user; + isl_pw_aff *pa; + isl_aff *aff; + int dim; + + aff = isl_aff_copy(data->aff); + dim = isl_set_dim(domain, isl_dim_set); + aff = isl_aff_add_dims(aff, isl_dim_in, dim); + aff = isl_aff_reset_domain_space(aff, isl_set_get_space(domain)); + pa = isl_pw_aff_alloc(domain, aff); + data->res = isl_union_pw_aff_add_pw_aff(data->res, pa); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Internal data structure for isl_union_pw_multi_aff_get_union_pw_aff. + * pos is the output position that needs to be extracted. + * res collects the results. + */ +struct isl_union_pw_multi_aff_get_union_pw_aff_data { + int pos; + isl_union_pw_aff *res; +}; + +/* Extract an isl_pw_aff corresponding to output dimension "pos" of "pma" + * (assuming it has such a dimension) and add it to data->res. + */ +static isl_stat get_union_pw_aff(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_union_pw_multi_aff_get_union_pw_aff_data *data = user; + int n_out; + isl_pw_aff *pa; + + if (!pma) + return isl_stat_error; + + n_out = isl_pw_multi_aff_dim(pma, isl_dim_out); + if (data->pos >= n_out) { + isl_pw_multi_aff_free(pma); + return isl_stat_ok; + } + + pa = isl_pw_multi_aff_get_pw_aff(pma, data->pos); + isl_pw_multi_aff_free(pma); + + data->res = isl_union_pw_aff_add_pw_aff(data->res, pa); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Extract an isl_union_pw_aff corresponding to + * output dimension "pos" of "upma". + */ +__isl_give isl_union_pw_aff *isl_union_pw_multi_aff_get_union_pw_aff( + __isl_keep isl_union_pw_multi_aff *upma, int pos) +{ + struct isl_union_pw_multi_aff_get_union_pw_aff_data data; + isl_space *space; + + if (!upma) + return NULL; + + if (pos < 0) + isl_die(isl_union_pw_multi_aff_get_ctx(upma), isl_error_invalid, + "cannot extract at negative position", return NULL); + + space = isl_union_pw_multi_aff_get_space(upma); + data.res = isl_union_pw_aff_empty(space); + data.pos = pos; + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, + &get_union_pw_aff, &data) < 0) + data.res = isl_union_pw_aff_free(data.res); + + return data.res; +} + +/* Return a union piecewise affine expression + * that is equal to "aff" on "domain". + * + * Construct an isl_pw_aff on each of the sets in "domain" and + * collect the results. + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_aff_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_aff *aff) +{ + struct isl_union_pw_aff_aff_on_domain_data data; + isl_space *space; + + if (!domain || !aff) + goto error; + if (!isl_local_space_is_params(aff->ls)) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "expecting parametric expression", goto error); + + space = isl_union_set_get_space(domain); + data.res = isl_union_pw_aff_empty(space); + data.aff = aff; + if (isl_union_set_foreach_set(domain, &pw_aff_aff_on_domain, &data) < 0) + data.res = isl_union_pw_aff_free(data.res); + isl_union_set_free(domain); + isl_aff_free(aff); + return data.res; +error: + isl_union_set_free(domain); + isl_aff_free(aff); + return NULL; +} + +/* Internal data structure for isl_union_pw_aff_val_on_domain. + * "v" is the value that the resulting isl_union_pw_aff needs to attain. + * "res" collects the results. + */ +struct isl_union_pw_aff_val_on_domain_data { + isl_val *v; + isl_union_pw_aff *res; +}; + +/* Construct a piecewise affine expression that is equal to data->v + * on "domain" and add the result to data->res. + */ +static isl_stat pw_aff_val_on_domain(__isl_take isl_set *domain, void *user) +{ + struct isl_union_pw_aff_val_on_domain_data *data = user; + isl_pw_aff *pa; + isl_val *v; + + v = isl_val_copy(data->v); + pa = isl_pw_aff_val_on_domain(domain, v); + data->res = isl_union_pw_aff_add_pw_aff(data->res, pa); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Return a union piecewise affine expression + * that is equal to "v" on "domain". + * + * Construct an isl_pw_aff on each of the sets in "domain" and + * collect the results. + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_val_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_val *v) +{ + struct isl_union_pw_aff_val_on_domain_data data; + isl_space *space; + + space = isl_union_set_get_space(domain); + data.res = isl_union_pw_aff_empty(space); + data.v = v; + if (isl_union_set_foreach_set(domain, &pw_aff_val_on_domain, &data) < 0) + data.res = isl_union_pw_aff_free(data.res); + isl_union_set_free(domain); + isl_val_free(v); + return data.res; +} + +/* Construct a piecewise multi affine expression + * that is equal to "pa" and add it to upma. + */ +static isl_stat pw_multi_aff_from_pw_aff_entry(__isl_take isl_pw_aff *pa, + void *user) +{ + isl_union_pw_multi_aff **upma = user; + isl_pw_multi_aff *pma; + + pma = isl_pw_multi_aff_from_pw_aff(pa); + *upma = isl_union_pw_multi_aff_add_pw_multi_aff(*upma, pma); + + return *upma ? isl_stat_ok : isl_stat_error; +} + +/* Construct and return a union piecewise multi affine expression + * that is equal to the given union piecewise affine expression. + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa) +{ + isl_space *space; + isl_union_pw_multi_aff *upma; + + if (!upa) + return NULL; + + space = isl_union_pw_aff_get_space(upa); + upma = isl_union_pw_multi_aff_empty(space); + + if (isl_union_pw_aff_foreach_pw_aff(upa, + &pw_multi_aff_from_pw_aff_entry, &upma) < 0) + upma = isl_union_pw_multi_aff_free(upma); + + isl_union_pw_aff_free(upa); + return upma; +} + +/* Compute the set of elements in the domain of "pa" where it is zero and + * add this set to "uset". + */ +static isl_stat zero_union_set(__isl_take isl_pw_aff *pa, void *user) +{ + isl_union_set **uset = (isl_union_set **)user; + + *uset = isl_union_set_add_set(*uset, isl_pw_aff_zero_set(pa)); + + return *uset ? isl_stat_ok : isl_stat_error; +} + +/* Return a union set containing those elements in the domain + * of "upa" where it is zero. + */ +__isl_give isl_union_set *isl_union_pw_aff_zero_union_set( + __isl_take isl_union_pw_aff *upa) +{ + isl_union_set *zero; + + zero = isl_union_set_empty(isl_union_pw_aff_get_space(upa)); + if (isl_union_pw_aff_foreach_pw_aff(upa, &zero_union_set, &zero) < 0) + zero = isl_union_set_free(zero); + + isl_union_pw_aff_free(upa); + return zero; +} + +/* Convert "pa" to an isl_map and add it to *umap. + */ +static isl_stat map_from_pw_aff_entry(__isl_take isl_pw_aff *pa, void *user) +{ + isl_union_map **umap = user; + isl_map *map; + + map = isl_map_from_pw_aff(pa); + *umap = isl_union_map_add_map(*umap, map); + + return *umap ? isl_stat_ok : isl_stat_error; +} + +/* Construct a union map mapping the domain of the union + * piecewise affine expression to its range, with the single output dimension + * equated to the corresponding affine expressions on their cells. + */ +__isl_give isl_union_map *isl_union_map_from_union_pw_aff( + __isl_take isl_union_pw_aff *upa) +{ + isl_space *space; + isl_union_map *umap; + + if (!upa) + return NULL; + + space = isl_union_pw_aff_get_space(upa); + umap = isl_union_map_empty(space); + + if (isl_union_pw_aff_foreach_pw_aff(upa, &map_from_pw_aff_entry, + &umap) < 0) + umap = isl_union_map_free(umap); + + isl_union_pw_aff_free(upa); + return umap; +} + +/* Internal data structure for isl_union_pw_aff_pullback_union_pw_multi_aff. + * upma is the function that is plugged in. + * pa is the current part of the function in which upma is plugged in. + * res collects the results. + */ +struct isl_union_pw_aff_pullback_upma_data { + isl_union_pw_multi_aff *upma; + isl_pw_aff *pa; + isl_union_pw_aff *res; +}; + +/* Check if "pma" can be plugged into data->pa. + * If so, perform the pullback and add the result to data->res. + */ +static isl_stat pa_pb_pma(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_union_pw_aff_pullback_upma_data *data = user; + isl_pw_aff *pa; + + if (!isl_space_tuple_is_equal(data->pa->dim, isl_dim_in, + pma->dim, isl_dim_out)) { + isl_pw_multi_aff_free(pma); + return isl_stat_ok; + } + + pa = isl_pw_aff_copy(data->pa); + pa = isl_pw_aff_pullback_pw_multi_aff(pa, pma); + + data->res = isl_union_pw_aff_add_pw_aff(data->res, pa); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Check if any of the elements of data->upma can be plugged into pa, + * add if so add the result to data->res. + */ +static isl_stat upa_pb_upma(__isl_take isl_pw_aff *pa, void *user) +{ + struct isl_union_pw_aff_pullback_upma_data *data = user; + isl_stat r; + + data->pa = pa; + r = isl_union_pw_multi_aff_foreach_pw_multi_aff(data->upma, + &pa_pb_pma, data); + isl_pw_aff_free(pa); + + return r; +} + +/* Compute the pullback of "upa" by the function represented by "upma". + * In other words, plug in "upma" in "upa". The result contains + * expressions defined over the domain space of "upma". + * + * Run over all pairs of elements in "upa" and "upma", perform + * the pullback when appropriate and collect the results. + * If the hash value were based on the domain space rather than + * the function space, then we could run through all elements + * of "upma" and directly pick out the corresponding element of "upa". + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_pullback_union_pw_multi_aff( + __isl_take isl_union_pw_aff *upa, + __isl_take isl_union_pw_multi_aff *upma) +{ + struct isl_union_pw_aff_pullback_upma_data data = { NULL, NULL }; + isl_space *space; + + space = isl_union_pw_multi_aff_get_space(upma); + upa = isl_union_pw_aff_align_params(upa, space); + space = isl_union_pw_aff_get_space(upa); + upma = isl_union_pw_multi_aff_align_params(upma, space); + + if (!upa || !upma) + goto error; + + data.upma = upma; + data.res = isl_union_pw_aff_alloc_same_size(upa); + if (isl_union_pw_aff_foreach_pw_aff(upa, &upa_pb_upma, &data) < 0) + data.res = isl_union_pw_aff_free(data.res); + + isl_union_pw_aff_free(upa); + isl_union_pw_multi_aff_free(upma); + return data.res; +error: + isl_union_pw_aff_free(upa); + isl_union_pw_multi_aff_free(upma); + return NULL; +} + +#undef BASE +#define BASE union_pw_aff +#undef DOMBASE +#define DOMBASE union_set + +#define NO_MOVE_DIMS +#define NO_DIMS +#define NO_DOMAIN +#define NO_PRODUCT +#define NO_SPLICE +#define NO_ZERO +#define NO_IDENTITY +#define NO_GIST + +#include +#include +#include +#include +#include +#include +#include + +/* Construct a multiple union piecewise affine expression + * in the given space with value zero in each of the output dimensions. + * + * Since there is no canonical zero value for + * a union piecewise affine expression, we can only construct + * zero-dimensional "zero" value. + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_zero( + __isl_take isl_space *space) +{ + if (!space) + return NULL; + + if (!isl_space_is_set(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "expecting set space", goto error); + if (isl_space_dim(space , isl_dim_out) != 0) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "expecting 0D space", goto error); + + return isl_multi_union_pw_aff_alloc(space); +error: + isl_space_free(space); + return NULL; +} + +/* Compute the sum of "mupa1" and "mupa2" on the union of their domains, + * with the actual sum on the shared domain and + * the defined expression on the symmetric difference of the domains. + * + * We simply iterate over the elements in both arguments and + * call isl_union_pw_aff_union_add on each of them. + */ +static __isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_union_add_aligned( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2) +{ + return isl_multi_union_pw_aff_bin_op(mupa1, mupa2, + &isl_union_pw_aff_union_add); +} + +/* Compute the sum of "mupa1" and "mupa2" on the union of their domains, + * with the actual sum on the shared domain and + * the defined expression on the symmetric difference of the domains. + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_union_add( + __isl_take isl_multi_union_pw_aff *mupa1, + __isl_take isl_multi_union_pw_aff *mupa2) +{ + return isl_multi_union_pw_aff_align_params_multi_multi_and(mupa1, mupa2, + &isl_multi_union_pw_aff_union_add_aligned); +} + +/* Construct and return a multi union piecewise affine expression + * that is equal to the given multi affine expression. + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_aff( + __isl_take isl_multi_aff *ma) +{ + isl_multi_pw_aff *mpa; + + mpa = isl_multi_pw_aff_from_multi_aff(ma); + return isl_multi_union_pw_aff_from_multi_pw_aff(mpa); +} + +/* Construct and return a multi union piecewise affine expression + * that is equal to the given multi piecewise affine expression. + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_pw_aff( + __isl_take isl_multi_pw_aff *mpa) +{ + int i, n; + isl_space *space; + isl_multi_union_pw_aff *mupa; + + if (!mpa) + return NULL; + + space = isl_multi_pw_aff_get_space(mpa); + space = isl_space_range(space); + mupa = isl_multi_union_pw_aff_alloc(space); + + n = isl_multi_pw_aff_dim(mpa, isl_dim_out); + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + isl_union_pw_aff *upa; + + pa = isl_multi_pw_aff_get_pw_aff(mpa, i); + upa = isl_union_pw_aff_from_pw_aff(pa); + mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa); + } + + isl_multi_pw_aff_free(mpa); + + return mupa; +} + +/* Extract the range space of "pma" and assign it to *space. + * If *space has already been set (through a previous call to this function), + * then check that the range space is the same. + */ +static isl_stat extract_space(__isl_take isl_pw_multi_aff *pma, void *user) +{ + isl_space **space = user; + isl_space *pma_space; + isl_bool equal; + + pma_space = isl_space_range(isl_pw_multi_aff_get_space(pma)); + isl_pw_multi_aff_free(pma); + + if (!pma_space) + return isl_stat_error; + if (!*space) { + *space = pma_space; + return isl_stat_ok; + } + + equal = isl_space_is_equal(pma_space, *space); + isl_space_free(pma_space); + + if (equal < 0) + return isl_stat_error; + if (!equal) + isl_die(isl_space_get_ctx(*space), isl_error_invalid, + "range spaces not the same", return isl_stat_error); + return isl_stat_ok; +} + +/* Construct and return a multi union piecewise affine expression + * that is equal to the given union piecewise multi affine expression. + * + * In order to be able to perform the conversion, the input + * needs to be non-empty and may only involve a single range space. + */ +__isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_from_union_pw_multi_aff( + __isl_take isl_union_pw_multi_aff *upma) +{ + isl_space *space = NULL; + isl_multi_union_pw_aff *mupa; + int i, n; + + if (!upma) + return NULL; + if (isl_union_pw_multi_aff_n_pw_multi_aff(upma) == 0) + isl_die(isl_union_pw_multi_aff_get_ctx(upma), isl_error_invalid, + "cannot extract range space from empty input", + goto error); + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, &extract_space, + &space) < 0) + goto error; + + if (!space) + goto error; + + n = isl_space_dim(space, isl_dim_set); + mupa = isl_multi_union_pw_aff_alloc(space); + + for (i = 0; i < n; ++i) { + isl_union_pw_aff *upa; + + upa = isl_union_pw_multi_aff_get_union_pw_aff(upma, i); + mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa); + } + + isl_union_pw_multi_aff_free(upma); + return mupa; +error: + isl_space_free(space); + isl_union_pw_multi_aff_free(upma); + return NULL; +} + +/* Try and create an isl_multi_union_pw_aff that is equivalent + * to the given isl_union_map. + * The isl_union_map is required to be single-valued in each space. + * Moreover, it cannot be empty and all range spaces need to be the same. + * Otherwise, an error is produced. + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_union_map( + __isl_take isl_union_map *umap) +{ + isl_union_pw_multi_aff *upma; + + upma = isl_union_pw_multi_aff_from_union_map(umap); + return isl_multi_union_pw_aff_from_union_pw_multi_aff(upma); +} + +/* Return a multiple union piecewise affine expression + * that is equal to "mv" on "domain", assuming "domain" and "mv" + * have been aligned. + */ +static __isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_multi_val_on_domain_aligned( + __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv) +{ + int i, n; + isl_space *space; + isl_multi_union_pw_aff *mupa; + + if (!domain || !mv) + goto error; + + n = isl_multi_val_dim(mv, isl_dim_set); + space = isl_multi_val_get_space(mv); + mupa = isl_multi_union_pw_aff_alloc(space); + for (i = 0; i < n; ++i) { + isl_val *v; + isl_union_pw_aff *upa; + + v = isl_multi_val_get_val(mv, i); + upa = isl_union_pw_aff_val_on_domain(isl_union_set_copy(domain), + v); + mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa); + } + + isl_union_set_free(domain); + isl_multi_val_free(mv); + return mupa; +error: + isl_union_set_free(domain); + isl_multi_val_free(mv); + return NULL; +} + +/* Return a multiple union piecewise affine expression + * that is equal to "mv" on "domain". + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_val_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv) +{ + if (!domain || !mv) + goto error; + if (isl_space_match(domain->dim, isl_dim_param, + mv->space, isl_dim_param)) + return isl_multi_union_pw_aff_multi_val_on_domain_aligned( + domain, mv); + domain = isl_union_set_align_params(domain, + isl_multi_val_get_space(mv)); + mv = isl_multi_val_align_params(mv, isl_union_set_get_space(domain)); + return isl_multi_union_pw_aff_multi_val_on_domain_aligned(domain, mv); +error: + isl_union_set_free(domain); + isl_multi_val_free(mv); + return NULL; +} + +/* Return a multiple union piecewise affine expression + * that is equal to "ma" on "domain", assuming "domain" and "ma" + * have been aligned. + */ +static __isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_multi_aff_on_domain_aligned( + __isl_take isl_union_set *domain, __isl_take isl_multi_aff *ma) +{ + int i, n; + isl_space *space; + isl_multi_union_pw_aff *mupa; + + if (!domain || !ma) + goto error; + + n = isl_multi_aff_dim(ma, isl_dim_set); + space = isl_multi_aff_get_space(ma); + mupa = isl_multi_union_pw_aff_alloc(space); + for (i = 0; i < n; ++i) { + isl_aff *aff; + isl_union_pw_aff *upa; + + aff = isl_multi_aff_get_aff(ma, i); + upa = isl_union_pw_aff_aff_on_domain(isl_union_set_copy(domain), + aff); + mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa); + } + + isl_union_set_free(domain); + isl_multi_aff_free(ma); + return mupa; +error: + isl_union_set_free(domain); + isl_multi_aff_free(ma); + return NULL; +} + +/* Return a multiple union piecewise affine expression + * that is equal to "ma" on "domain". + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_aff_on_domain( + __isl_take isl_union_set *domain, __isl_take isl_multi_aff *ma) +{ + if (!domain || !ma) + goto error; + if (isl_space_match(domain->dim, isl_dim_param, + ma->space, isl_dim_param)) + return isl_multi_union_pw_aff_multi_aff_on_domain_aligned( + domain, ma); + domain = isl_union_set_align_params(domain, + isl_multi_aff_get_space(ma)); + ma = isl_multi_aff_align_params(ma, isl_union_set_get_space(domain)); + return isl_multi_union_pw_aff_multi_aff_on_domain_aligned(domain, ma); +error: + isl_union_set_free(domain); + isl_multi_aff_free(ma); + return NULL; +} + +/* Return a union set containing those elements in the domains + * of the elements of "mupa" where they are all zero. + */ +__isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set( + __isl_take isl_multi_union_pw_aff *mupa) +{ + int i, n; + isl_union_pw_aff *upa; + isl_union_set *zero; + + if (!mupa) + return NULL; + + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + if (n == 0) + isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid, + "cannot determine zero set " + "of zero-dimensional function", goto error); + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0); + zero = isl_union_pw_aff_zero_union_set(upa); + + for (i = 1; i < n; ++i) { + isl_union_set *zero_i; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + zero_i = isl_union_pw_aff_zero_union_set(upa); + + zero = isl_union_set_intersect(zero, zero_i); + } + + isl_multi_union_pw_aff_free(mupa); + return zero; +error: + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Construct a union map mapping the shared domain + * of the union piecewise affine expressions to the range of "mupa" + * with each dimension in the range equated to the + * corresponding union piecewise affine expression. + * + * The input cannot be zero-dimensional as there is + * no way to extract a domain from a zero-dimensional isl_multi_union_pw_aff. + */ +__isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa) +{ + int i, n; + isl_space *space; + isl_union_map *umap; + isl_union_pw_aff *upa; + + if (!mupa) + return NULL; + + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + if (n == 0) + isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid, + "cannot determine domain of zero-dimensional " + "isl_multi_union_pw_aff", goto error); + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0); + umap = isl_union_map_from_union_pw_aff(upa); + + for (i = 1; i < n; ++i) { + isl_union_map *umap_i; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + umap_i = isl_union_map_from_union_pw_aff(upa); + umap = isl_union_map_flat_range_product(umap, umap_i); + } + + space = isl_multi_union_pw_aff_get_space(mupa); + umap = isl_union_map_reset_range_space(umap, space); + + isl_multi_union_pw_aff_free(mupa); + return umap; +error: + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Internal data structure for isl_union_pw_multi_aff_reset_range_space. + * "range" is the space from which to set the range space. + * "res" collects the results. + */ +struct isl_union_pw_multi_aff_reset_range_space_data { + isl_space *range; + isl_union_pw_multi_aff *res; +}; + +/* Replace the range space of "pma" by the range space of data->range and + * add the result to data->res. + */ +static isl_stat reset_range_space(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_union_pw_multi_aff_reset_range_space_data *data = user; + isl_space *space; + + space = isl_pw_multi_aff_get_space(pma); + space = isl_space_domain(space); + space = isl_space_extend_domain_with_range(space, + isl_space_copy(data->range)); + pma = isl_pw_multi_aff_reset_space(pma, space); + data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Replace the range space of all the piecewise affine expressions in "upma" by + * the range space of "space". + * + * This assumes that all these expressions have the same output dimension. + * + * Since the spaces of the expressions change, so do their hash values. + * We therefore need to create a new isl_union_pw_multi_aff. + * Note that the hash value is currently computed based on the entire + * space even though there can only be a single expression with a given + * domain space. + */ +static __isl_give isl_union_pw_multi_aff * +isl_union_pw_multi_aff_reset_range_space( + __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_space *space) +{ + struct isl_union_pw_multi_aff_reset_range_space_data data = { space }; + isl_space *space_upma; + + space_upma = isl_union_pw_multi_aff_get_space(upma); + data.res = isl_union_pw_multi_aff_empty(space_upma); + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, + &reset_range_space, &data) < 0) + data.res = isl_union_pw_multi_aff_free(data.res); + + isl_space_free(space); + isl_union_pw_multi_aff_free(upma); + return data.res; +} + +/* Construct and return a union piecewise multi affine expression + * that is equal to the given multi union piecewise affine expression. + * + * In order to be able to perform the conversion, the input + * needs to have a least one output dimension. + */ +__isl_give isl_union_pw_multi_aff * +isl_union_pw_multi_aff_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa) +{ + int i, n; + isl_space *space; + isl_union_pw_multi_aff *upma; + isl_union_pw_aff *upa; + + if (!mupa) + return NULL; + + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + if (n == 0) + isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid, + "cannot determine domain of zero-dimensional " + "isl_multi_union_pw_aff", goto error); + + space = isl_multi_union_pw_aff_get_space(mupa); + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0); + upma = isl_union_pw_multi_aff_from_union_pw_aff(upa); + + for (i = 1; i < n; ++i) { + isl_union_pw_multi_aff *upma_i; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + upma_i = isl_union_pw_multi_aff_from_union_pw_aff(upa); + upma = isl_union_pw_multi_aff_flat_range_product(upma, upma_i); + } + + upma = isl_union_pw_multi_aff_reset_range_space(upma, space); + + isl_multi_union_pw_aff_free(mupa); + return upma; +error: + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Intersect the range of "mupa" with "range". + * That is, keep only those domain elements that have a function value + * in "range". + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_range( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *range) +{ + isl_union_pw_multi_aff *upma; + isl_union_set *domain; + isl_space *space; + int n; + int match; + + if (!mupa || !range) + goto error; + + space = isl_set_get_space(range); + match = isl_space_tuple_is_equal(mupa->space, isl_dim_set, + space, isl_dim_set); + isl_space_free(space); + if (match < 0) + goto error; + if (!match) + isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid, + "space don't match", goto error); + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + if (n == 0) + isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid, + "cannot intersect range of zero-dimensional " + "isl_multi_union_pw_aff", goto error); + + upma = isl_union_pw_multi_aff_from_multi_union_pw_aff( + isl_multi_union_pw_aff_copy(mupa)); + domain = isl_union_set_from_set(range); + domain = isl_union_set_preimage_union_pw_multi_aff(domain, upma); + mupa = isl_multi_union_pw_aff_intersect_domain(mupa, domain); + + return mupa; +error: + isl_multi_union_pw_aff_free(mupa); + isl_set_free(range); + return NULL; +} + +/* Return the shared domain of the elements of "mupa". + */ +__isl_give isl_union_set *isl_multi_union_pw_aff_domain( + __isl_take isl_multi_union_pw_aff *mupa) +{ + int i, n; + isl_union_pw_aff *upa; + isl_union_set *dom; + + if (!mupa) + return NULL; + + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + if (n == 0) + isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid, + "cannot determine domain", goto error); + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0); + dom = isl_union_pw_aff_domain(upa); + for (i = 1; i < n; ++i) { + isl_union_set *dom_i; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + dom_i = isl_union_pw_aff_domain(upa); + dom = isl_union_set_intersect(dom, dom_i); + } + + isl_multi_union_pw_aff_free(mupa); + return dom; +error: + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Apply "aff" to "mupa". The space of "mupa" is equal to the domain of "aff". + * In particular, the spaces have been aligned. + * The result is defined over the shared domain of the elements of "mupa" + * + * We first extract the parametric constant part of "aff" and + * define that over the shared domain. + * Then we iterate over all input dimensions of "aff" and add the corresponding + * multiples of the elements of "mupa". + * Finally, we consider the integer divisions, calling the function + * recursively to obtain an isl_union_pw_aff corresponding to the + * integer division argument. + */ +static __isl_give isl_union_pw_aff *multi_union_pw_aff_apply_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_aff *aff) +{ + int i, n_in, n_div; + isl_union_pw_aff *upa; + isl_union_set *uset; + isl_val *v; + isl_aff *cst; + + n_in = isl_aff_dim(aff, isl_dim_in); + n_div = isl_aff_dim(aff, isl_dim_div); + + uset = isl_multi_union_pw_aff_domain(isl_multi_union_pw_aff_copy(mupa)); + cst = isl_aff_copy(aff); + cst = isl_aff_drop_dims(cst, isl_dim_div, 0, n_div); + cst = isl_aff_drop_dims(cst, isl_dim_in, 0, n_in); + cst = isl_aff_project_domain_on_params(cst); + upa = isl_union_pw_aff_aff_on_domain(uset, cst); + + for (i = 0; i < n_in; ++i) { + isl_union_pw_aff *upa_i; + + if (!isl_aff_involves_dims(aff, isl_dim_in, i, 1)) + continue; + v = isl_aff_get_coefficient_val(aff, isl_dim_in, i); + upa_i = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + upa_i = isl_union_pw_aff_scale_val(upa_i, v); + upa = isl_union_pw_aff_add(upa, upa_i); + } + + for (i = 0; i < n_div; ++i) { + isl_aff *div; + isl_union_pw_aff *upa_i; + + if (!isl_aff_involves_dims(aff, isl_dim_div, i, 1)) + continue; + div = isl_aff_get_div(aff, i); + upa_i = multi_union_pw_aff_apply_aff( + isl_multi_union_pw_aff_copy(mupa), div); + upa_i = isl_union_pw_aff_floor(upa_i); + v = isl_aff_get_coefficient_val(aff, isl_dim_div, i); + upa_i = isl_union_pw_aff_scale_val(upa_i, v); + upa = isl_union_pw_aff_add(upa, upa_i); + } + + isl_multi_union_pw_aff_free(mupa); + isl_aff_free(aff); + + return upa; +} + +/* Apply "aff" to "mupa". The space of "mupa" needs to be compatible + * with the domain of "aff". + * Furthermore, the dimension of this space needs to be greater than zero. + * The result is defined over the shared domain of the elements of "mupa" + * + * We perform these checks and then hand over control to + * multi_union_pw_aff_apply_aff. + */ +__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_aff *aff) +{ + isl_space *space1, *space2; + int equal; + + mupa = isl_multi_union_pw_aff_align_params(mupa, + isl_aff_get_space(aff)); + aff = isl_aff_align_params(aff, isl_multi_union_pw_aff_get_space(mupa)); + if (!mupa || !aff) + goto error; + + space1 = isl_multi_union_pw_aff_get_space(mupa); + space2 = isl_aff_get_domain_space(aff); + equal = isl_space_is_equal(space1, space2); + isl_space_free(space1); + isl_space_free(space2); + if (equal < 0) + goto error; + if (!equal) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "spaces don't match", goto error); + if (isl_aff_dim(aff, isl_dim_in) == 0) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot determine domains", goto error); + + return multi_union_pw_aff_apply_aff(mupa, aff); +error: + isl_multi_union_pw_aff_free(mupa); + isl_aff_free(aff); + return NULL; +} + +/* Apply "ma" to "mupa". The space of "mupa" needs to be compatible + * with the domain of "ma". + * Furthermore, the dimension of this space needs to be greater than zero, + * unless the dimension of the target space of "ma" is also zero. + * The result is defined over the shared domain of the elements of "mupa" + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma) +{ + isl_space *space1, *space2; + isl_multi_union_pw_aff *res; + int equal; + int i, n_out; + + mupa = isl_multi_union_pw_aff_align_params(mupa, + isl_multi_aff_get_space(ma)); + ma = isl_multi_aff_align_params(ma, + isl_multi_union_pw_aff_get_space(mupa)); + if (!mupa || !ma) + goto error; + + space1 = isl_multi_union_pw_aff_get_space(mupa); + space2 = isl_multi_aff_get_domain_space(ma); + equal = isl_space_is_equal(space1, space2); + isl_space_free(space1); + isl_space_free(space2); + if (equal < 0) + goto error; + if (!equal) + isl_die(isl_multi_aff_get_ctx(ma), isl_error_invalid, + "spaces don't match", goto error); + n_out = isl_multi_aff_dim(ma, isl_dim_out); + if (isl_multi_aff_dim(ma, isl_dim_in) == 0 && n_out != 0) + isl_die(isl_multi_aff_get_ctx(ma), isl_error_invalid, + "cannot determine domains", goto error); + + space1 = isl_space_range(isl_multi_aff_get_space(ma)); + res = isl_multi_union_pw_aff_alloc(space1); + + for (i = 0; i < n_out; ++i) { + isl_aff *aff; + isl_union_pw_aff *upa; + + aff = isl_multi_aff_get_aff(ma, i); + upa = multi_union_pw_aff_apply_aff( + isl_multi_union_pw_aff_copy(mupa), aff); + res = isl_multi_union_pw_aff_set_union_pw_aff(res, i, upa); + } + + isl_multi_aff_free(ma); + isl_multi_union_pw_aff_free(mupa); + return res; +error: + isl_multi_union_pw_aff_free(mupa); + isl_multi_aff_free(ma); + return NULL; +} + +/* Apply "pa" to "mupa". The space of "mupa" needs to be compatible + * with the domain of "pa". + * Furthermore, the dimension of this space needs to be greater than zero. + * The result is defined over the shared domain of the elements of "mupa" + */ +__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_pw_aff *pa) +{ + int i; + int equal; + isl_space *space, *space2; + isl_union_pw_aff *upa; + + mupa = isl_multi_union_pw_aff_align_params(mupa, + isl_pw_aff_get_space(pa)); + pa = isl_pw_aff_align_params(pa, + isl_multi_union_pw_aff_get_space(mupa)); + if (!mupa || !pa) + goto error; + + space = isl_multi_union_pw_aff_get_space(mupa); + space2 = isl_pw_aff_get_domain_space(pa); + equal = isl_space_is_equal(space, space2); + isl_space_free(space); + isl_space_free(space2); + if (equal < 0) + goto error; + if (!equal) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "spaces don't match", goto error); + if (isl_pw_aff_dim(pa, isl_dim_in) == 0) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "cannot determine domains", goto error); + + space = isl_space_params(isl_multi_union_pw_aff_get_space(mupa)); + upa = isl_union_pw_aff_empty(space); + + for (i = 0; i < pa->n; ++i) { + isl_aff *aff; + isl_set *domain; + isl_multi_union_pw_aff *mupa_i; + isl_union_pw_aff *upa_i; + + mupa_i = isl_multi_union_pw_aff_copy(mupa); + domain = isl_set_copy(pa->p[i].set); + mupa_i = isl_multi_union_pw_aff_intersect_range(mupa_i, domain); + aff = isl_aff_copy(pa->p[i].aff); + upa_i = multi_union_pw_aff_apply_aff(mupa_i, aff); + upa = isl_union_pw_aff_union_add(upa, upa_i); + } + + isl_multi_union_pw_aff_free(mupa); + isl_pw_aff_free(pa); + return upa; +error: + isl_multi_union_pw_aff_free(mupa); + isl_pw_aff_free(pa); + return NULL; +} + +/* Apply "pma" to "mupa". The space of "mupa" needs to be compatible + * with the domain of "pma". + * Furthermore, the dimension of this space needs to be greater than zero, + * unless the dimension of the target space of "pma" is also zero. + * The result is defined over the shared domain of the elements of "mupa" + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_pw_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_pw_multi_aff *pma) +{ + isl_space *space1, *space2; + isl_multi_union_pw_aff *res; + int equal; + int i, n_out; + + mupa = isl_multi_union_pw_aff_align_params(mupa, + isl_pw_multi_aff_get_space(pma)); + pma = isl_pw_multi_aff_align_params(pma, + isl_multi_union_pw_aff_get_space(mupa)); + if (!mupa || !pma) + goto error; + + space1 = isl_multi_union_pw_aff_get_space(mupa); + space2 = isl_pw_multi_aff_get_domain_space(pma); + equal = isl_space_is_equal(space1, space2); + isl_space_free(space1); + isl_space_free(space2); + if (equal < 0) + goto error; + if (!equal) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "spaces don't match", goto error); + n_out = isl_pw_multi_aff_dim(pma, isl_dim_out); + if (isl_pw_multi_aff_dim(pma, isl_dim_in) == 0 && n_out != 0) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid, + "cannot determine domains", goto error); + + space1 = isl_space_range(isl_pw_multi_aff_get_space(pma)); + res = isl_multi_union_pw_aff_alloc(space1); + + for (i = 0; i < n_out; ++i) { + isl_pw_aff *pa; + isl_union_pw_aff *upa; + + pa = isl_pw_multi_aff_get_pw_aff(pma, i); + upa = isl_multi_union_pw_aff_apply_pw_aff( + isl_multi_union_pw_aff_copy(mupa), pa); + res = isl_multi_union_pw_aff_set_union_pw_aff(res, i, upa); + } + + isl_pw_multi_aff_free(pma); + isl_multi_union_pw_aff_free(mupa); + return res; +error: + isl_multi_union_pw_aff_free(mupa); + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Compute the pullback of "mupa" by the function represented by "upma". + * In other words, plug in "upma" in "mupa". The result contains + * expressions defined over the domain space of "upma". + * + * Run over all elements of "mupa" and plug in "upma" in each of them. + */ +__isl_give isl_multi_union_pw_aff * +isl_multi_union_pw_aff_pullback_union_pw_multi_aff( + __isl_take isl_multi_union_pw_aff *mupa, + __isl_take isl_union_pw_multi_aff *upma) +{ + int i, n; + + mupa = isl_multi_union_pw_aff_align_params(mupa, + isl_union_pw_multi_aff_get_space(upma)); + upma = isl_union_pw_multi_aff_align_params(upma, + isl_multi_union_pw_aff_get_space(mupa)); + if (!mupa || !upma) + goto error; + + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + for (i = 0; i < n; ++i) { + isl_union_pw_aff *upa; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + upa = isl_union_pw_aff_pullback_union_pw_multi_aff(upa, + isl_union_pw_multi_aff_copy(upma)); + mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa); + } + + isl_union_pw_multi_aff_free(upma); + return mupa; +error: + isl_multi_union_pw_aff_free(mupa); + isl_union_pw_multi_aff_free(upma); + return NULL; +} + +/* Extract the sequence of elements in "mupa" with domain space "space" + * (ignoring parameters). + * + * For the elements of "mupa" that are not defined on the specified space, + * the corresponding element in the result is empty. + */ +__isl_give isl_multi_pw_aff *isl_multi_union_pw_aff_extract_multi_pw_aff( + __isl_keep isl_multi_union_pw_aff *mupa, __isl_take isl_space *space) +{ + int i, n; + isl_space *space_mpa = NULL; + isl_multi_pw_aff *mpa; + + if (!mupa || !space) + goto error; + + space_mpa = isl_multi_union_pw_aff_get_space(mupa); + if (!isl_space_match(space_mpa, isl_dim_param, space, isl_dim_param)) { + space = isl_space_drop_dims(space, isl_dim_param, + 0, isl_space_dim(space, isl_dim_param)); + space = isl_space_align_params(space, + isl_space_copy(space_mpa)); + if (!space) + goto error; + } + space_mpa = isl_space_map_from_domain_and_range(isl_space_copy(space), + space_mpa); + mpa = isl_multi_pw_aff_alloc(space_mpa); + + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, 1); + n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + for (i = 0; i < n; ++i) { + isl_union_pw_aff *upa; + isl_pw_aff *pa; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i); + pa = isl_union_pw_aff_extract_pw_aff(upa, + isl_space_copy(space)); + mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa); + isl_union_pw_aff_free(upa); + } + + isl_space_free(space); + return mpa; +error: + isl_space_free(space_mpa); + isl_space_free(space); + return NULL; +} Index: lib/Analysis/isl/isl_aff_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_aff_private.h @@ -0,0 +1,177 @@ +#ifndef ISL_AFF_PRIVATE_H +#define ISL_AFF_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +/* ls represents the domain space. + * + * If the first two elements of "v" (the denominator and the constant term) + * are zero, then the isl_aff represents NaN. + */ +struct isl_aff { + int ref; + + isl_local_space *ls; + isl_vec *v; +}; + +#undef EL +#define EL isl_aff + +#include + +struct isl_pw_aff_piece { + struct isl_set *set; + struct isl_aff *aff; +}; + +struct isl_pw_aff { + int ref; + + isl_space *dim; + + int n; + + size_t size; + struct isl_pw_aff_piece p[1]; +}; + +#undef EL +#define EL isl_pw_aff + +#include + +struct isl_pw_multi_aff_piece { + isl_set *set; + isl_multi_aff *maff; +}; + +struct isl_pw_multi_aff { + int ref; + + isl_space *dim; + + int n; + + size_t size; + struct isl_pw_multi_aff_piece p[1]; +}; + +__isl_give isl_aff *isl_aff_alloc_vec(__isl_take isl_local_space *ls, + __isl_take isl_vec *v); +__isl_give isl_aff *isl_aff_alloc(__isl_take isl_local_space *ls); + +__isl_give isl_aff *isl_aff_reset_space_and_domain(__isl_take isl_aff *aff, + __isl_take isl_space *space, __isl_take isl_space *domain); +__isl_give isl_aff *isl_aff_reset_domain_space(__isl_take isl_aff *aff, + __isl_take isl_space *dim); +__isl_give isl_aff *isl_aff_realign_domain(__isl_take isl_aff *aff, + __isl_take isl_reordering *r); + +int isl_aff_get_constant(__isl_keep isl_aff *aff, isl_int *v); +__isl_give isl_aff *isl_aff_set_constant(__isl_take isl_aff *aff, isl_int v); +__isl_give isl_aff *isl_aff_set_coefficient(__isl_take isl_aff *aff, + enum isl_dim_type type, int pos, isl_int v); +__isl_give isl_aff *isl_aff_add_constant(__isl_take isl_aff *aff, isl_int v); + +int isl_aff_plain_cmp(__isl_keep isl_aff *aff1, __isl_keep isl_aff *aff2); + +__isl_give isl_aff *isl_aff_normalize(__isl_take isl_aff *aff); + +__isl_give isl_aff *isl_aff_expand_divs( __isl_take isl_aff *aff, + __isl_take isl_mat *div, int *exp); + +__isl_give isl_pw_aff *isl_pw_aff_alloc_size(__isl_take isl_space *space, + int n); +__isl_give isl_pw_aff *isl_pw_aff_reset_space(__isl_take isl_pw_aff *pwaff, + __isl_take isl_space *dim); +__isl_give isl_pw_aff *isl_pw_aff_reset_domain_space( + __isl_take isl_pw_aff *pwaff, __isl_take isl_space *space); +__isl_give isl_pw_aff *isl_pw_aff_add_disjoint( + __isl_take isl_pw_aff *pwaff1, __isl_take isl_pw_aff *pwaff2); + +__isl_give isl_pw_aff *isl_pw_aff_union_opt(__isl_take isl_pw_aff *pwaff1, + __isl_take isl_pw_aff *pwaff2, int max); + +__isl_give isl_pw_aff *isl_pw_aff_set_rational(__isl_take isl_pw_aff *pwaff); +__isl_give isl_pw_aff_list *isl_pw_aff_list_set_rational( + __isl_take isl_pw_aff_list *list); + +__isl_give isl_pw_aff *isl_pw_aff_scale(__isl_take isl_pw_aff *pwaff, + isl_int f); +__isl_give isl_pw_aff *isl_pw_aff_scale_down(__isl_take isl_pw_aff *pwaff, + isl_int f); + +int isl_aff_matching_params(__isl_keep isl_aff *aff, + __isl_keep isl_space *space); +int isl_aff_check_match_domain_space(__isl_keep isl_aff *aff, + __isl_keep isl_space *space); + +#undef BASE +#define BASE aff + +#include + +__isl_give isl_multi_aff *isl_multi_aff_dup(__isl_keep isl_multi_aff *multi); + +__isl_give isl_multi_aff *isl_multi_aff_align_divs( + __isl_take isl_multi_aff *maff); + +__isl_give isl_multi_aff *isl_multi_aff_from_basic_set_equalities( + __isl_take isl_basic_set *bset); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_domain_space( + __isl_take isl_pw_multi_aff *pwmaff, __isl_take isl_space *space); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_space( + __isl_take isl_pw_multi_aff *pwmaff, __isl_take isl_space *space); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add_disjoint( + __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2); + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_out( + __isl_take isl_pw_multi_aff *pma, + enum isl_dim_type type, unsigned first, unsigned n); + +void isl_seq_substitute(isl_int *p, int pos, isl_int *subs, + int p_len, int subs_len, isl_int v); +void isl_seq_preimage(isl_int *dst, isl_int *src, + __isl_keep isl_multi_aff *ma, int n_before, int n_after, + int n_div_ma, int n_div_bmap, + isl_int f, isl_int c1, isl_int c2, isl_int g, int has_denom); + +__isl_give isl_aff *isl_aff_substitute_equalities(__isl_take isl_aff *aff, + __isl_take isl_basic_set *eq); +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_substitute( + __isl_take isl_pw_multi_aff *pma, enum isl_dim_type type, unsigned pos, + __isl_keep isl_pw_aff *subs); + +int isl_pw_aff_matching_params(__isl_keep isl_pw_aff *pa, + __isl_keep isl_space *space); +int isl_pw_aff_check_match_domain_space(__isl_keep isl_pw_aff *pa, + __isl_keep isl_space *space); + +#undef BASE +#define BASE pw_aff + +#include + +#undef EL +#define EL isl_union_pw_aff + +#include + +#undef BASE +#define BASE union_pw_aff + +#include + +#undef EL +#define EL isl_union_pw_multi_aff + +#include + +#endif Index: lib/Analysis/isl/isl_affine_hull.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_affine_hull.c @@ -0,0 +1,1472 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include "isl_equalities.h" +#include "isl_sample.h" +#include "isl_tab.h" +#include +#include + +struct isl_basic_map *isl_basic_map_implicit_equalities( + struct isl_basic_map *bmap) +{ + struct isl_tab *tab; + + if (!bmap) + return bmap; + + bmap = isl_basic_map_gauss(bmap, NULL); + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NO_IMPLICIT)) + return bmap; + if (bmap->n_ineq <= 1) + return bmap; + + tab = isl_tab_from_basic_map(bmap, 0); + if (isl_tab_detect_implicit_equalities(tab) < 0) + goto error; + bmap = isl_basic_map_update_from_tab(bmap, tab); + isl_tab_free(tab); + bmap = isl_basic_map_gauss(bmap, NULL); + ISL_F_SET(bmap, ISL_BASIC_MAP_NO_IMPLICIT); + return bmap; +error: + isl_tab_free(tab); + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_set *isl_basic_set_implicit_equalities( + struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_implicit_equalities((struct isl_basic_map*)bset); +} + +struct isl_map *isl_map_implicit_equalities(struct isl_map *map) +{ + int i; + + if (!map) + return map; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_implicit_equalities(map->p[i]); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Make eq[row][col] of both bmaps equal so we can add the row + * add the column to the common matrix. + * Note that because of the echelon form, the columns of row row + * after column col are zero. + */ +static void set_common_multiple( + struct isl_basic_set *bset1, struct isl_basic_set *bset2, + unsigned row, unsigned col) +{ + isl_int m, c; + + if (isl_int_eq(bset1->eq[row][col], bset2->eq[row][col])) + return; + + isl_int_init(c); + isl_int_init(m); + isl_int_lcm(m, bset1->eq[row][col], bset2->eq[row][col]); + isl_int_divexact(c, m, bset1->eq[row][col]); + isl_seq_scale(bset1->eq[row], bset1->eq[row], c, col+1); + isl_int_divexact(c, m, bset2->eq[row][col]); + isl_seq_scale(bset2->eq[row], bset2->eq[row], c, col+1); + isl_int_clear(c); + isl_int_clear(m); +} + +/* Delete a given equality, moving all the following equalities one up. + */ +static void delete_row(struct isl_basic_set *bset, unsigned row) +{ + isl_int *t; + int r; + + t = bset->eq[row]; + bset->n_eq--; + for (r = row; r < bset->n_eq; ++r) + bset->eq[r] = bset->eq[r+1]; + bset->eq[bset->n_eq] = t; +} + +/* Make first row entries in column col of bset1 identical to + * those of bset2, using the fact that entry bset1->eq[row][col]=a + * is non-zero. Initially, these elements of bset1 are all zero. + * For each row i < row, we set + * A[i] = a * A[i] + B[i][col] * A[row] + * B[i] = a * B[i] + * so that + * A[i][col] = B[i][col] = a * old(B[i][col]) + */ +static void construct_column( + struct isl_basic_set *bset1, struct isl_basic_set *bset2, + unsigned row, unsigned col) +{ + int r; + isl_int a; + isl_int b; + unsigned total; + + isl_int_init(a); + isl_int_init(b); + total = 1 + isl_basic_set_n_dim(bset1); + for (r = 0; r < row; ++r) { + if (isl_int_is_zero(bset2->eq[r][col])) + continue; + isl_int_gcd(b, bset2->eq[r][col], bset1->eq[row][col]); + isl_int_divexact(a, bset1->eq[row][col], b); + isl_int_divexact(b, bset2->eq[r][col], b); + isl_seq_combine(bset1->eq[r], a, bset1->eq[r], + b, bset1->eq[row], total); + isl_seq_scale(bset2->eq[r], bset2->eq[r], a, total); + } + isl_int_clear(a); + isl_int_clear(b); + delete_row(bset1, row); +} + +/* Make first row entries in column col of bset1 identical to + * those of bset2, using only these entries of the two matrices. + * Let t be the last row with different entries. + * For each row i < t, we set + * A[i] = (A[t][col]-B[t][col]) * A[i] + (B[i][col]-A[i][col) * A[t] + * B[i] = (A[t][col]-B[t][col]) * B[i] + (B[i][col]-A[i][col) * B[t] + * so that + * A[i][col] = B[i][col] = old(A[t][col]*B[i][col]-A[i][col]*B[t][col]) + */ +static int transform_column( + struct isl_basic_set *bset1, struct isl_basic_set *bset2, + unsigned row, unsigned col) +{ + int i, t; + isl_int a, b, g; + unsigned total; + + for (t = row-1; t >= 0; --t) + if (isl_int_ne(bset1->eq[t][col], bset2->eq[t][col])) + break; + if (t < 0) + return 0; + + total = 1 + isl_basic_set_n_dim(bset1); + isl_int_init(a); + isl_int_init(b); + isl_int_init(g); + isl_int_sub(b, bset1->eq[t][col], bset2->eq[t][col]); + for (i = 0; i < t; ++i) { + isl_int_sub(a, bset2->eq[i][col], bset1->eq[i][col]); + isl_int_gcd(g, a, b); + isl_int_divexact(a, a, g); + isl_int_divexact(g, b, g); + isl_seq_combine(bset1->eq[i], g, bset1->eq[i], a, bset1->eq[t], + total); + isl_seq_combine(bset2->eq[i], g, bset2->eq[i], a, bset2->eq[t], + total); + } + isl_int_clear(a); + isl_int_clear(b); + isl_int_clear(g); + delete_row(bset1, t); + delete_row(bset2, t); + return 1; +} + +/* The implementation is based on Section 5.2 of Michael Karr, + * "Affine Relationships Among Variables of a Program", + * except that the echelon form we use starts from the last column + * and that we are dealing with integer coefficients. + */ +static struct isl_basic_set *affine_hull( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + unsigned total; + int col; + int row; + + if (!bset1 || !bset2) + goto error; + + total = 1 + isl_basic_set_n_dim(bset1); + + row = 0; + for (col = total-1; col >= 0; --col) { + int is_zero1 = row >= bset1->n_eq || + isl_int_is_zero(bset1->eq[row][col]); + int is_zero2 = row >= bset2->n_eq || + isl_int_is_zero(bset2->eq[row][col]); + if (!is_zero1 && !is_zero2) { + set_common_multiple(bset1, bset2, row, col); + ++row; + } else if (!is_zero1 && is_zero2) { + construct_column(bset1, bset2, row, col); + } else if (is_zero1 && !is_zero2) { + construct_column(bset2, bset1, row, col); + } else { + if (transform_column(bset1, bset2, row, col)) + --row; + } + } + isl_assert(bset1->ctx, row == bset1->n_eq, goto error); + isl_basic_set_free(bset2); + bset1 = isl_basic_set_normalize_constraints(bset1); + return bset1; +error: + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return NULL; +} + +/* Find an integer point in the set represented by "tab" + * that lies outside of the equality "eq" e(x) = 0. + * If "up" is true, look for a point satisfying e(x) - 1 >= 0. + * Otherwise, look for a point satisfying -e(x) - 1 >= 0 (i.e., e(x) <= -1). + * The point, if found, is returned. + * If no point can be found, a zero-length vector is returned. + * + * Before solving an ILP problem, we first check if simply + * adding the normal of the constraint to one of the known + * integer points in the basic set represented by "tab" + * yields another point inside the basic set. + * + * The caller of this function ensures that the tableau is bounded or + * that tab->basis and tab->n_unbounded have been set appropriately. + */ +static struct isl_vec *outside_point(struct isl_tab *tab, isl_int *eq, int up) +{ + struct isl_ctx *ctx; + struct isl_vec *sample = NULL; + struct isl_tab_undo *snap; + unsigned dim; + + if (!tab) + return NULL; + ctx = tab->mat->ctx; + + dim = tab->n_var; + sample = isl_vec_alloc(ctx, 1 + dim); + if (!sample) + return NULL; + isl_int_set_si(sample->el[0], 1); + isl_seq_combine(sample->el + 1, + ctx->one, tab->bmap->sample->el + 1, + up ? ctx->one : ctx->negone, eq + 1, dim); + if (isl_basic_map_contains(tab->bmap, sample)) + return sample; + isl_vec_free(sample); + sample = NULL; + + snap = isl_tab_snap(tab); + + if (!up) + isl_seq_neg(eq, eq, 1 + dim); + isl_int_sub_ui(eq[0], eq[0], 1); + + if (isl_tab_extend_cons(tab, 1) < 0) + goto error; + if (isl_tab_add_ineq(tab, eq) < 0) + goto error; + + sample = isl_tab_sample(tab); + + isl_int_add_ui(eq[0], eq[0], 1); + if (!up) + isl_seq_neg(eq, eq, 1 + dim); + + if (sample && isl_tab_rollback(tab, snap) < 0) + goto error; + + return sample; +error: + isl_vec_free(sample); + return NULL; +} + +struct isl_basic_set *isl_basic_set_recession_cone(struct isl_basic_set *bset) +{ + int i; + + bset = isl_basic_set_cow(bset); + if (!bset) + return NULL; + isl_assert(bset->ctx, bset->n_div == 0, goto error); + + for (i = 0; i < bset->n_eq; ++i) + isl_int_set_si(bset->eq[i][0], 0); + + for (i = 0; i < bset->n_ineq; ++i) + isl_int_set_si(bset->ineq[i][0], 0); + + ISL_F_CLR(bset, ISL_BASIC_SET_NO_IMPLICIT); + return isl_basic_set_implicit_equalities(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +__isl_give isl_set *isl_set_recession_cone(__isl_take isl_set *set) +{ + int i; + + if (!set) + return NULL; + if (set->n == 0) + return set; + + set = isl_set_remove_divs(set); + set = isl_set_cow(set); + if (!set) + return NULL; + + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_recession_cone(set->p[i]); + if (!set->p[i]) + goto error; + } + + return set; +error: + isl_set_free(set); + return NULL; +} + +/* Move "sample" to a point that is one up (or down) from the original + * point in dimension "pos". + */ +static void adjacent_point(__isl_keep isl_vec *sample, int pos, int up) +{ + if (up) + isl_int_add_ui(sample->el[1 + pos], sample->el[1 + pos], 1); + else + isl_int_sub_ui(sample->el[1 + pos], sample->el[1 + pos], 1); +} + +/* Check if any points that are adjacent to "sample" also belong to "bset". + * If so, add them to "hull" and return the updated hull. + * + * Before checking whether and adjacent point belongs to "bset", we first + * check whether it already belongs to "hull" as this test is typically + * much cheaper. + */ +static __isl_give isl_basic_set *add_adjacent_points( + __isl_take isl_basic_set *hull, __isl_take isl_vec *sample, + __isl_keep isl_basic_set *bset) +{ + int i, up; + int dim; + + if (!sample) + goto error; + + dim = isl_basic_set_dim(hull, isl_dim_set); + + for (i = 0; i < dim; ++i) { + for (up = 0; up <= 1; ++up) { + int contains; + isl_basic_set *point; + + adjacent_point(sample, i, up); + contains = isl_basic_set_contains(hull, sample); + if (contains < 0) + goto error; + if (contains) { + adjacent_point(sample, i, !up); + continue; + } + contains = isl_basic_set_contains(bset, sample); + if (contains < 0) + goto error; + if (contains) { + point = isl_basic_set_from_vec( + isl_vec_copy(sample)); + hull = affine_hull(hull, point); + } + adjacent_point(sample, i, !up); + if (contains) + break; + } + } + + isl_vec_free(sample); + + return hull; +error: + isl_vec_free(sample); + isl_basic_set_free(hull); + return NULL; +} + +/* Extend an initial (under-)approximation of the affine hull of basic + * set represented by the tableau "tab" + * by looking for points that do not satisfy one of the equalities + * in the current approximation and adding them to that approximation + * until no such points can be found any more. + * + * The caller of this function ensures that "tab" is bounded or + * that tab->basis and tab->n_unbounded have been set appropriately. + * + * "bset" may be either NULL or the basic set represented by "tab". + * If "bset" is not NULL, we check for any point we find if any + * of its adjacent points also belong to "bset". + */ +static __isl_give isl_basic_set *extend_affine_hull(struct isl_tab *tab, + __isl_take isl_basic_set *hull, __isl_keep isl_basic_set *bset) +{ + int i, j; + unsigned dim; + + if (!tab || !hull) + goto error; + + dim = tab->n_var; + + if (isl_tab_extend_cons(tab, 2 * dim + 1) < 0) + goto error; + + for (i = 0; i < dim; ++i) { + struct isl_vec *sample; + struct isl_basic_set *point; + for (j = 0; j < hull->n_eq; ++j) { + sample = outside_point(tab, hull->eq[j], 1); + if (!sample) + goto error; + if (sample->size > 0) + break; + isl_vec_free(sample); + sample = outside_point(tab, hull->eq[j], 0); + if (!sample) + goto error; + if (sample->size > 0) + break; + isl_vec_free(sample); + + if (isl_tab_add_eq(tab, hull->eq[j]) < 0) + goto error; + } + if (j == hull->n_eq) + break; + if (tab->samples && + isl_tab_add_sample(tab, isl_vec_copy(sample)) < 0) + hull = isl_basic_set_free(hull); + if (bset) + hull = add_adjacent_points(hull, isl_vec_copy(sample), + bset); + point = isl_basic_set_from_vec(sample); + hull = affine_hull(hull, point); + if (!hull) + return NULL; + } + + return hull; +error: + isl_basic_set_free(hull); + return NULL; +} + +/* Drop all constraints in bmap that involve any of the dimensions + * first to first+n-1. + */ +static __isl_give isl_basic_map *isl_basic_map_drop_constraints_involving( + __isl_take isl_basic_map *bmap, unsigned first, unsigned n) +{ + int i; + + if (n == 0) + return bmap; + + bmap = isl_basic_map_cow(bmap); + + if (!bmap) + return NULL; + + for (i = bmap->n_eq - 1; i >= 0; --i) { + if (isl_seq_first_non_zero(bmap->eq[i] + 1 + first, n) == -1) + continue; + isl_basic_map_drop_equality(bmap, i); + } + + for (i = bmap->n_ineq - 1; i >= 0; --i) { + if (isl_seq_first_non_zero(bmap->ineq[i] + 1 + first, n) == -1) + continue; + isl_basic_map_drop_inequality(bmap, i); + } + + bmap = isl_basic_map_add_known_div_constraints(bmap); + return bmap; +} + +/* Drop all constraints in bset that involve any of the dimensions + * first to first+n-1. + */ +__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving( + __isl_take isl_basic_set *bset, unsigned first, unsigned n) +{ + return isl_basic_map_drop_constraints_involving(bset, first, n); +} + +/* Drop all constraints in bmap that do not involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_basic_map *isl_basic_map_drop_constraints_not_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + unsigned dim; + + if (n == 0) { + isl_space *space = isl_basic_map_get_space(bmap); + isl_basic_map_free(bmap); + return isl_basic_map_universe(space); + } + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + dim = isl_basic_map_dim(bmap, type); + if (first + n > dim || first + n < first) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "index out of bounds", return isl_basic_map_free(bmap)); + + first += isl_basic_map_offset(bmap, type) - 1; + + for (i = bmap->n_eq - 1; i >= 0; --i) { + if (isl_seq_first_non_zero(bmap->eq[i] + 1 + first, n) != -1) + continue; + isl_basic_map_drop_equality(bmap, i); + } + + for (i = bmap->n_ineq - 1; i >= 0; --i) { + if (isl_seq_first_non_zero(bmap->ineq[i] + 1 + first, n) != -1) + continue; + isl_basic_map_drop_inequality(bmap, i); + } + + bmap = isl_basic_map_add_known_div_constraints(bmap); + return bmap; +} + +/* Drop all constraints in bset that do not involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_basic_set *isl_basic_set_drop_constraints_not_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_basic_map_drop_constraints_not_involving_dims(bset, + type, first, n); +} + +/* Drop all constraints in bmap that involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + unsigned dim; + + if (!bmap) + return NULL; + if (n == 0) + return bmap; + + dim = isl_basic_map_dim(bmap, type); + if (first + n > dim || first + n < first) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "index out of bounds", return isl_basic_map_free(bmap)); + + bmap = isl_basic_map_remove_divs_involving_dims(bmap, type, first, n); + first += isl_basic_map_offset(bmap, type) - 1; + return isl_basic_map_drop_constraints_involving(bmap, first, n); +} + +/* Drop all constraints in bset that involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_basic_map_drop_constraints_involving_dims(bset, + type, first, n); +} + +/* Drop constraints from "map" by applying "drop" to each basic map. + */ +__isl_give isl_map *drop_constraints(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n, + __isl_give isl_basic_map *(*drop)(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n)) +{ + int i; + unsigned dim; + + if (!map) + return NULL; + + dim = isl_map_dim(map, type); + if (first + n > dim || first + n < first) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "index out of bounds", return isl_map_free(map)); + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = drop(map->p[i], type, first, n); + if (!map->p[i]) + return isl_map_free(map); + } + + if (map->n > 1) + ISL_F_CLR(map, ISL_MAP_DISJOINT); + + return map; +} + +/* Drop all constraints in map that involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_map *isl_map_drop_constraints_involving_dims( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + if (n == 0) + return map; + return drop_constraints(map, type, first, n, + &isl_basic_map_drop_constraints_involving_dims); +} + +/* Drop all constraints in "map" that do not involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_map *isl_map_drop_constraints_not_involving_dims( + __isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + if (n == 0) { + isl_space *space = isl_map_get_space(map); + isl_map_free(map); + return isl_map_universe(space); + } + return drop_constraints(map, type, first, n, + &isl_basic_map_drop_constraints_not_involving_dims); +} + +/* Drop all constraints in set that involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_set *isl_set_drop_constraints_involving_dims( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_map_drop_constraints_involving_dims(set, type, first, n); +} + +/* Drop all constraints in "set" that do not involve any of the dimensions + * first to first + n - 1 of the given type. + */ +__isl_give isl_set *isl_set_drop_constraints_not_involving_dims( + __isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_map_drop_constraints_not_involving_dims(set, type, first, n); +} + +/* Construct an initial underapproximation of the hull of "bset" + * from "sample" and any of its adjacent points that also belong to "bset". + */ +static __isl_give isl_basic_set *initialize_hull(__isl_keep isl_basic_set *bset, + __isl_take isl_vec *sample) +{ + isl_basic_set *hull; + + hull = isl_basic_set_from_vec(isl_vec_copy(sample)); + hull = add_adjacent_points(hull, sample, bset); + + return hull; +} + +/* Look for all equalities satisfied by the integer points in bset, + * which is assumed to be bounded. + * + * The equalities are obtained by successively looking for + * a point that is affinely independent of the points found so far. + * In particular, for each equality satisfied by the points so far, + * we check if there is any point on a hyperplane parallel to the + * corresponding hyperplane shifted by at least one (in either direction). + */ +static struct isl_basic_set *uset_affine_hull_bounded(struct isl_basic_set *bset) +{ + struct isl_vec *sample = NULL; + struct isl_basic_set *hull; + struct isl_tab *tab = NULL; + unsigned dim; + + if (isl_basic_set_plain_is_empty(bset)) + return bset; + + dim = isl_basic_set_n_dim(bset); + + if (bset->sample && bset->sample->size == 1 + dim) { + int contains = isl_basic_set_contains(bset, bset->sample); + if (contains < 0) + goto error; + if (contains) { + if (dim == 0) + return bset; + sample = isl_vec_copy(bset->sample); + } else { + isl_vec_free(bset->sample); + bset->sample = NULL; + } + } + + tab = isl_tab_from_basic_set(bset, 1); + if (!tab) + goto error; + if (tab->empty) { + isl_tab_free(tab); + isl_vec_free(sample); + return isl_basic_set_set_to_empty(bset); + } + + if (!sample) { + struct isl_tab_undo *snap; + snap = isl_tab_snap(tab); + sample = isl_tab_sample(tab); + if (isl_tab_rollback(tab, snap) < 0) + goto error; + isl_vec_free(tab->bmap->sample); + tab->bmap->sample = isl_vec_copy(sample); + } + + if (!sample) + goto error; + if (sample->size == 0) { + isl_tab_free(tab); + isl_vec_free(sample); + return isl_basic_set_set_to_empty(bset); + } + + hull = initialize_hull(bset, sample); + + hull = extend_affine_hull(tab, hull, bset); + isl_basic_set_free(bset); + isl_tab_free(tab); + + return hull; +error: + isl_vec_free(sample); + isl_tab_free(tab); + isl_basic_set_free(bset); + return NULL; +} + +/* Given an unbounded tableau and an integer point satisfying the tableau, + * construct an initial affine hull containing the recession cone + * shifted to the given point. + * + * The unbounded directions are taken from the last rows of the basis, + * which is assumed to have been initialized appropriately. + */ +static __isl_give isl_basic_set *initial_hull(struct isl_tab *tab, + __isl_take isl_vec *vec) +{ + int i; + int k; + struct isl_basic_set *bset = NULL; + struct isl_ctx *ctx; + unsigned dim; + + if (!vec || !tab) + return NULL; + ctx = vec->ctx; + isl_assert(ctx, vec->size != 0, goto error); + + bset = isl_basic_set_alloc(ctx, 0, vec->size - 1, 0, vec->size - 1, 0); + if (!bset) + goto error; + dim = isl_basic_set_n_dim(bset) - tab->n_unbounded; + for (i = 0; i < dim; ++i) { + k = isl_basic_set_alloc_equality(bset); + if (k < 0) + goto error; + isl_seq_cpy(bset->eq[k] + 1, tab->basis->row[1 + i] + 1, + vec->size - 1); + isl_seq_inner_product(bset->eq[k] + 1, vec->el +1, + vec->size - 1, &bset->eq[k][0]); + isl_int_neg(bset->eq[k][0], bset->eq[k][0]); + } + bset->sample = vec; + bset = isl_basic_set_gauss(bset, NULL); + + return bset; +error: + isl_basic_set_free(bset); + isl_vec_free(vec); + return NULL; +} + +/* Given a tableau of a set and a tableau of the corresponding + * recession cone, detect and add all equalities to the tableau. + * If the tableau is bounded, then we can simply keep the + * tableau in its state after the return from extend_affine_hull. + * However, if the tableau is unbounded, then + * isl_tab_set_initial_basis_with_cone will add some additional + * constraints to the tableau that have to be removed again. + * In this case, we therefore rollback to the state before + * any constraints were added and then add the equalities back in. + */ +struct isl_tab *isl_tab_detect_equalities(struct isl_tab *tab, + struct isl_tab *tab_cone) +{ + int j; + struct isl_vec *sample; + struct isl_basic_set *hull = NULL; + struct isl_tab_undo *snap; + + if (!tab || !tab_cone) + goto error; + + snap = isl_tab_snap(tab); + + isl_mat_free(tab->basis); + tab->basis = NULL; + + isl_assert(tab->mat->ctx, tab->bmap, goto error); + isl_assert(tab->mat->ctx, tab->samples, goto error); + isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, goto error); + isl_assert(tab->mat->ctx, tab->n_sample > tab->n_outside, goto error); + + if (isl_tab_set_initial_basis_with_cone(tab, tab_cone) < 0) + goto error; + + sample = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var); + if (!sample) + goto error; + + isl_seq_cpy(sample->el, tab->samples->row[tab->n_outside], sample->size); + + isl_vec_free(tab->bmap->sample); + tab->bmap->sample = isl_vec_copy(sample); + + if (tab->n_unbounded == 0) + hull = isl_basic_set_from_vec(isl_vec_copy(sample)); + else + hull = initial_hull(tab, isl_vec_copy(sample)); + + for (j = tab->n_outside + 1; j < tab->n_sample; ++j) { + isl_seq_cpy(sample->el, tab->samples->row[j], sample->size); + hull = affine_hull(hull, + isl_basic_set_from_vec(isl_vec_copy(sample))); + } + + isl_vec_free(sample); + + hull = extend_affine_hull(tab, hull, NULL); + if (!hull) + goto error; + + if (tab->n_unbounded == 0) { + isl_basic_set_free(hull); + return tab; + } + + if (isl_tab_rollback(tab, snap) < 0) + goto error; + + if (hull->n_eq > tab->n_zero) { + for (j = 0; j < hull->n_eq; ++j) { + isl_seq_normalize(tab->mat->ctx, hull->eq[j], 1 + tab->n_var); + if (isl_tab_add_eq(tab, hull->eq[j]) < 0) + goto error; + } + } + + isl_basic_set_free(hull); + + return tab; +error: + isl_basic_set_free(hull); + isl_tab_free(tab); + return NULL; +} + +/* Compute the affine hull of "bset", where "cone" is the recession cone + * of "bset". + * + * We first compute a unimodular transformation that puts the unbounded + * directions in the last dimensions. In particular, we take a transformation + * that maps all equalities to equalities (in HNF) on the first dimensions. + * Let x be the original dimensions and y the transformed, with y_1 bounded + * and y_2 unbounded. + * + * [ y_1 ] [ y_1 ] [ Q_1 ] + * x = U [ y_2 ] [ y_2 ] = [ Q_2 ] x + * + * Let's call the input basic set S. We compute S' = preimage(S, U) + * and drop the final dimensions including any constraints involving them. + * This results in set S''. + * Then we compute the affine hull A'' of S''. + * Let F y_1 >= g be the constraint system of A''. In the transformed + * space the y_2 are unbounded, so we can add them back without any constraints, + * resulting in + * + * [ y_1 ] + * [ F 0 ] [ y_2 ] >= g + * or + * [ Q_1 ] + * [ F 0 ] [ Q_2 ] x >= g + * or + * F Q_1 x >= g + * + * The affine hull in the original space is then obtained as + * A = preimage(A'', Q_1). + */ +static struct isl_basic_set *affine_hull_with_cone(struct isl_basic_set *bset, + struct isl_basic_set *cone) +{ + unsigned total; + unsigned cone_dim; + struct isl_basic_set *hull; + struct isl_mat *M, *U, *Q; + + if (!bset || !cone) + goto error; + + total = isl_basic_set_total_dim(cone); + cone_dim = total - cone->n_eq; + + M = isl_mat_sub_alloc6(bset->ctx, cone->eq, 0, cone->n_eq, 1, total); + M = isl_mat_left_hermite(M, 0, &U, &Q); + if (!M) + goto error; + isl_mat_free(M); + + U = isl_mat_lin_to_aff(U); + bset = isl_basic_set_preimage(bset, isl_mat_copy(U)); + + bset = isl_basic_set_drop_constraints_involving(bset, total - cone_dim, + cone_dim); + bset = isl_basic_set_drop_dims(bset, total - cone_dim, cone_dim); + + Q = isl_mat_lin_to_aff(Q); + Q = isl_mat_drop_rows(Q, 1 + total - cone_dim, cone_dim); + + if (bset && bset->sample && bset->sample->size == 1 + total) + bset->sample = isl_mat_vec_product(isl_mat_copy(Q), bset->sample); + + hull = uset_affine_hull_bounded(bset); + + if (!hull) { + isl_mat_free(Q); + isl_mat_free(U); + } else { + struct isl_vec *sample = isl_vec_copy(hull->sample); + U = isl_mat_drop_cols(U, 1 + total - cone_dim, cone_dim); + if (sample && sample->size > 0) + sample = isl_mat_vec_product(U, sample); + else + isl_mat_free(U); + hull = isl_basic_set_preimage(hull, Q); + if (hull) { + isl_vec_free(hull->sample); + hull->sample = sample; + } else + isl_vec_free(sample); + } + + isl_basic_set_free(cone); + + return hull; +error: + isl_basic_set_free(bset); + isl_basic_set_free(cone); + return NULL; +} + +/* Look for all equalities satisfied by the integer points in bset, + * which is assumed not to have any explicit equalities. + * + * The equalities are obtained by successively looking for + * a point that is affinely independent of the points found so far. + * In particular, for each equality satisfied by the points so far, + * we check if there is any point on a hyperplane parallel to the + * corresponding hyperplane shifted by at least one (in either direction). + * + * Before looking for any outside points, we first compute the recession + * cone. The directions of this recession cone will always be part + * of the affine hull, so there is no need for looking for any points + * in these directions. + * In particular, if the recession cone is full-dimensional, then + * the affine hull is simply the whole universe. + */ +static struct isl_basic_set *uset_affine_hull(struct isl_basic_set *bset) +{ + struct isl_basic_set *cone; + + if (isl_basic_set_plain_is_empty(bset)) + return bset; + + cone = isl_basic_set_recession_cone(isl_basic_set_copy(bset)); + if (!cone) + goto error; + if (cone->n_eq == 0) { + isl_space *space; + space = isl_basic_set_get_space(bset); + isl_basic_set_free(cone); + isl_basic_set_free(bset); + return isl_basic_set_universe(space); + } + + if (cone->n_eq < isl_basic_set_total_dim(cone)) + return affine_hull_with_cone(bset, cone); + + isl_basic_set_free(cone); + return uset_affine_hull_bounded(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Look for all equalities satisfied by the integer points in bmap + * that are independent of the equalities already explicitly available + * in bmap. + * + * We first remove all equalities already explicitly available, + * then look for additional equalities in the reduced space + * and then transform the result to the original space. + * The original equalities are _not_ added to this set. This is + * the responsibility of the calling function. + * The resulting basic set has all meaning about the dimensions removed. + * In particular, dimensions that correspond to existential variables + * in bmap and that are found to be fixed are not removed. + */ +static struct isl_basic_set *equalities_in_underlying_set( + struct isl_basic_map *bmap) +{ + struct isl_mat *T1 = NULL; + struct isl_mat *T2 = NULL; + struct isl_basic_set *bset = NULL; + struct isl_basic_set *hull = NULL; + + bset = isl_basic_map_underlying_set(bmap); + if (!bset) + return NULL; + if (bset->n_eq) + bset = isl_basic_set_remove_equalities(bset, &T1, &T2); + if (!bset) + goto error; + + hull = uset_affine_hull(bset); + if (!T2) + return hull; + + if (!hull) { + isl_mat_free(T1); + isl_mat_free(T2); + } else { + struct isl_vec *sample = isl_vec_copy(hull->sample); + if (sample && sample->size > 0) + sample = isl_mat_vec_product(T1, sample); + else + isl_mat_free(T1); + hull = isl_basic_set_preimage(hull, T2); + if (hull) { + isl_vec_free(hull->sample); + hull->sample = sample; + } else + isl_vec_free(sample); + } + + return hull; +error: + isl_mat_free(T1); + isl_mat_free(T2); + isl_basic_set_free(bset); + isl_basic_set_free(hull); + return NULL; +} + +/* Detect and make explicit all equalities satisfied by the (integer) + * points in bmap. + */ +struct isl_basic_map *isl_basic_map_detect_equalities( + struct isl_basic_map *bmap) +{ + int i, j; + struct isl_basic_set *hull = NULL; + + if (!bmap) + return NULL; + if (bmap->n_ineq == 0) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_ALL_EQUALITIES)) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) + return isl_basic_map_implicit_equalities(bmap); + + hull = equalities_in_underlying_set(isl_basic_map_copy(bmap)); + if (!hull) + goto error; + if (ISL_F_ISSET(hull, ISL_BASIC_SET_EMPTY)) { + isl_basic_set_free(hull); + return isl_basic_map_set_to_empty(bmap); + } + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), 0, + hull->n_eq, 0); + for (i = 0; i < hull->n_eq; ++i) { + j = isl_basic_map_alloc_equality(bmap); + if (j < 0) + goto error; + isl_seq_cpy(bmap->eq[j], hull->eq[i], + 1 + isl_basic_set_total_dim(hull)); + } + isl_vec_free(bmap->sample); + bmap->sample = isl_vec_copy(hull->sample); + isl_basic_set_free(hull); + ISL_F_SET(bmap, ISL_BASIC_MAP_NO_IMPLICIT | ISL_BASIC_MAP_ALL_EQUALITIES); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_set_free(hull); + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_detect_equalities( + __isl_take isl_basic_set *bset) +{ + return (isl_basic_set *) + isl_basic_map_detect_equalities((isl_basic_map *)bset); +} + +__isl_give isl_map *isl_map_detect_equalities(__isl_take isl_map *map) +{ + return isl_map_inline_foreach_basic_map(map, + &isl_basic_map_detect_equalities); +} + +__isl_give isl_set *isl_set_detect_equalities(__isl_take isl_set *set) +{ + return (isl_set *)isl_map_detect_equalities((isl_map *)set); +} + +/* Return the superset of "bmap" described by the equalities + * satisfied by "bmap" that are already known. + */ +__isl_give isl_basic_map *isl_basic_map_plain_affine_hull( + __isl_take isl_basic_map *bmap) +{ + bmap = isl_basic_map_cow(bmap); + if (bmap) + isl_basic_map_free_inequality(bmap, bmap->n_ineq); + bmap = isl_basic_map_finalize(bmap); + return bmap; +} + +/* Return the superset of "bset" described by the equalities + * satisfied by "bset" that are already known. + */ +__isl_give isl_basic_set *isl_basic_set_plain_affine_hull( + __isl_take isl_basic_set *bset) +{ + return isl_basic_map_plain_affine_hull(bset); +} + +/* After computing the rational affine hull (by detecting the implicit + * equalities), we compute the additional equalities satisfied by + * the integer points (if any) and add the original equalities back in. + */ +struct isl_basic_map *isl_basic_map_affine_hull(struct isl_basic_map *bmap) +{ + bmap = isl_basic_map_detect_equalities(bmap); + bmap = isl_basic_map_plain_affine_hull(bmap); + return bmap; +} + +struct isl_basic_set *isl_basic_set_affine_hull(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_affine_hull((struct isl_basic_map *)bset); +} + +/* Given a rational affine matrix "M", add stride constraints to "bmap" + * that ensure that + * + * M(x) + * + * is an integer vector. The variables x include all the variables + * of "bmap" except the unknown divs. + * + * If d is the common denominator of M, then we need to impose that + * + * d M(x) = 0 mod d + * + * or + * + * exists alpha : d M(x) = d alpha + * + * This function is similar to add_strides in isl_morph.c + */ +static __isl_give isl_basic_map *add_strides(__isl_take isl_basic_map *bmap, + __isl_keep isl_mat *M, int n_known) +{ + int i, div, k; + isl_int gcd; + + if (isl_int_is_one(M->row[0][0])) + return bmap; + + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), + M->n_row - 1, M->n_row - 1, 0); + + isl_int_init(gcd); + for (i = 1; i < M->n_row; ++i) { + isl_seq_gcd(M->row[i], M->n_col, &gcd); + if (isl_int_is_divisible_by(gcd, M->row[0][0])) + continue; + div = isl_basic_map_alloc_div(bmap); + if (div < 0) + goto error; + isl_int_set_si(bmap->div[div][0], 0); + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->eq[k], M->row[i], M->n_col); + isl_seq_clr(bmap->eq[k] + M->n_col, bmap->n_div - n_known); + isl_int_set(bmap->eq[k][M->n_col - n_known + div], + M->row[0][0]); + } + isl_int_clear(gcd); + + return bmap; +error: + isl_int_clear(gcd); + isl_basic_map_free(bmap); + return NULL; +} + +/* If there are any equalities that involve (multiple) unknown divs, + * then extract the stride information encoded by those equalities + * and make it explicitly available in "bmap". + * + * We first sort the divs so that the unknown divs appear last and + * then we count how many equalities involve these divs. + * + * Let these equalities be of the form + * + * A(x) + B y = 0 + * + * where y represents the unknown divs and x the remaining variables. + * Let [H 0] be the Hermite Normal Form of B, i.e., + * + * B = [H 0] Q + * + * Then x is a solution of the equalities iff + * + * H^-1 A(x) (= - [I 0] Q y) + * + * is an integer vector. Let d be the common denominator of H^-1. + * We impose + * + * d H^-1 A(x) = d alpha + * + * in add_strides, with alpha fresh existentially quantified variables. + */ +static __isl_give isl_basic_map *isl_basic_map_make_strides_explicit( + __isl_take isl_basic_map *bmap) +{ + int known; + int n_known; + int n, n_col; + int total; + isl_ctx *ctx; + isl_mat *A, *B, *M; + + known = isl_basic_map_divs_known(bmap); + if (known < 0) + return isl_basic_map_free(bmap); + if (known) + return bmap; + bmap = isl_basic_map_sort_divs(bmap); + bmap = isl_basic_map_gauss(bmap, NULL); + if (!bmap) + return NULL; + + for (n_known = 0; n_known < bmap->n_div; ++n_known) + if (isl_int_is_zero(bmap->div[n_known][0])) + break; + ctx = isl_basic_map_get_ctx(bmap); + total = isl_space_dim(bmap->dim, isl_dim_all); + for (n = 0; n < bmap->n_eq; ++n) + if (isl_seq_first_non_zero(bmap->eq[n] + 1 + total + n_known, + bmap->n_div - n_known) == -1) + break; + if (n == 0) + return bmap; + B = isl_mat_sub_alloc6(ctx, bmap->eq, 0, n, 0, 1 + total + n_known); + n_col = bmap->n_div - n_known; + A = isl_mat_sub_alloc6(ctx, bmap->eq, 0, n, 1 + total + n_known, n_col); + A = isl_mat_left_hermite(A, 0, NULL, NULL); + A = isl_mat_drop_cols(A, n, n_col - n); + A = isl_mat_lin_to_aff(A); + A = isl_mat_right_inverse(A); + B = isl_mat_insert_zero_rows(B, 0, 1); + B = isl_mat_set_element_si(B, 0, 0, 1); + M = isl_mat_product(A, B); + if (!M) + return isl_basic_map_free(bmap); + bmap = add_strides(bmap, M, n_known); + bmap = isl_basic_map_gauss(bmap, NULL); + isl_mat_free(M); + + return bmap; +} + +/* Compute the affine hull of each basic map in "map" separately + * and make all stride information explicit so that we can remove + * all unknown divs without losing this information. + * The result is also guaranteed to be gaussed. + * + * In simple cases where a div is determined by an equality, + * calling isl_basic_map_gauss is enough to make the stride information + * explicit, as it will derive an explicit representation for the div + * from the equality. If, however, the stride information + * is encoded through multiple unknown divs then we need to make + * some extra effort in isl_basic_map_make_strides_explicit. + */ +static __isl_give isl_map *isl_map_local_affine_hull(__isl_take isl_map *map) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_affine_hull(map->p[i]); + map->p[i] = isl_basic_map_gauss(map->p[i], NULL); + map->p[i] = isl_basic_map_make_strides_explicit(map->p[i]); + if (!map->p[i]) + return isl_map_free(map); + } + + return map; +} + +static __isl_give isl_set *isl_set_local_affine_hull(__isl_take isl_set *set) +{ + return isl_map_local_affine_hull(set); +} + +/* Return an empty basic map living in the same space as "map". + */ +static __isl_give isl_basic_map *replace_map_by_empty_basic_map( + __isl_take isl_map *map) +{ + isl_space *space; + + space = isl_map_get_space(map); + isl_map_free(map); + return isl_basic_map_empty(space); +} + +/* Compute the affine hull of "map". + * + * We first compute the affine hull of each basic map separately. + * Then we align the divs and recompute the affine hulls of the basic + * maps since some of them may now have extra divs. + * In order to avoid performing parametric integer programming to + * compute explicit expressions for the divs, possible leading to + * an explosion in the number of basic maps, we first drop all unknown + * divs before aligning the divs. Note that isl_map_local_affine_hull tries + * to make sure that all stride information is explicitly available + * in terms of known divs. This involves calling isl_basic_set_gauss, + * which is also needed because affine_hull assumes its input has been gaussed, + * while isl_map_affine_hull may be called on input that has not been gaussed, + * in particular from initial_facet_constraint. + * Similarly, align_divs may reorder some divs so that we need to + * gauss the result again. + * Finally, we combine the individual affine hulls into a single + * affine hull. + */ +__isl_give isl_basic_map *isl_map_affine_hull(__isl_take isl_map *map) +{ + struct isl_basic_map *model = NULL; + struct isl_basic_map *hull = NULL; + struct isl_set *set; + isl_basic_set *bset; + + map = isl_map_detect_equalities(map); + map = isl_map_local_affine_hull(map); + map = isl_map_remove_empty_parts(map); + map = isl_map_remove_unknown_divs(map); + map = isl_map_align_divs(map); + + if (!map) + return NULL; + + if (map->n == 0) + return replace_map_by_empty_basic_map(map); + + model = isl_basic_map_copy(map->p[0]); + set = isl_map_underlying_set(map); + set = isl_set_cow(set); + set = isl_set_local_affine_hull(set); + if (!set) + goto error; + + while (set->n > 1) + set->p[0] = affine_hull(set->p[0], set->p[--set->n]); + + bset = isl_basic_set_copy(set->p[0]); + hull = isl_basic_map_overlying_set(bset, model); + isl_set_free(set); + hull = isl_basic_map_simplify(hull); + return isl_basic_map_finalize(hull); +error: + isl_basic_map_free(model); + isl_set_free(set); + return NULL; +} + +struct isl_basic_set *isl_set_affine_hull(struct isl_set *set) +{ + return (struct isl_basic_set *) + isl_map_affine_hull((struct isl_map *)set); +} Index: lib/Analysis/isl/isl_arg.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_arg.c @@ -0,0 +1,1311 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include + +#include +#include +#include + +static struct isl_arg help_arg[] = { +ISL_ARG_PHANTOM_BOOL('h', "help", NULL, "print this help, then exit") +}; + +static void set_default_choice(struct isl_arg *arg, void *opt) +{ + if (arg->offset == (size_t) -1) + return; + *(unsigned *)(((char *)opt) + arg->offset) = arg->u.choice.default_value; +} + +static void set_default_flags(struct isl_arg *arg, void *opt) +{ + *(unsigned *)(((char *)opt) + arg->offset) = arg->u.flags.default_value; +} + +static void set_default_bool(struct isl_arg *arg, void *opt) +{ + if (arg->offset == (size_t) -1) + return; + *(unsigned *)(((char *)opt) + arg->offset) = arg->u.b.default_value; +} + +static void set_default_child(struct isl_arg *arg, void *opt) +{ + void *child; + + if (arg->offset == (size_t) -1) + child = opt; + else { + child = calloc(1, arg->u.child.child->options_size); + *(void **)(((char *)opt) + arg->offset) = child; + } + + if (child) + isl_args_set_defaults(arg->u.child.child, child); +} + +static void set_default_user(struct isl_arg *arg, void *opt) +{ + arg->u.user.init(((char *)opt) + arg->offset); +} + +static void set_default_int(struct isl_arg *arg, void *opt) +{ + *(int *)(((char *)opt) + arg->offset) = arg->u.i.default_value; +} + +static void set_default_long(struct isl_arg *arg, void *opt) +{ + *(long *)(((char *)opt) + arg->offset) = arg->u.l.default_value; +} + +static void set_default_ulong(struct isl_arg *arg, void *opt) +{ + *(unsigned long *)(((char *)opt) + arg->offset) = arg->u.ul.default_value; +} + +static void set_default_str(struct isl_arg *arg, void *opt) +{ + const char *str = NULL; + if (arg->u.str.default_value) + str = strdup(arg->u.str.default_value); + *(const char **)(((char *)opt) + arg->offset) = str; +} + +static void set_default_str_list(struct isl_arg *arg, void *opt) +{ + *(const char ***)(((char *) opt) + arg->offset) = NULL; + *(int *)(((char *) opt) + arg->u.str_list.offset_n) = 0; +} + +void isl_args_set_defaults(struct isl_args *args, void *opt) +{ + int i; + + for (i = 0; args->args[i].type != isl_arg_end; ++i) { + switch (args->args[i].type) { + case isl_arg_choice: + set_default_choice(&args->args[i], opt); + break; + case isl_arg_flags: + set_default_flags(&args->args[i], opt); + break; + case isl_arg_bool: + set_default_bool(&args->args[i], opt); + break; + case isl_arg_child: + set_default_child(&args->args[i], opt); + break; + case isl_arg_user: + set_default_user(&args->args[i], opt); + break; + case isl_arg_int: + set_default_int(&args->args[i], opt); + break; + case isl_arg_long: + set_default_long(&args->args[i], opt); + break; + case isl_arg_ulong: + set_default_ulong(&args->args[i], opt); + break; + case isl_arg_arg: + case isl_arg_str: + set_default_str(&args->args[i], opt); + break; + case isl_arg_str_list: + set_default_str_list(&args->args[i], opt); + break; + case isl_arg_alias: + case isl_arg_footer: + case isl_arg_version: + case isl_arg_end: + break; + } + } +} + +static void free_args(struct isl_arg *arg, void *opt); + +static void free_child(struct isl_arg *arg, void *opt) +{ + if (arg->offset == (size_t) -1) + free_args(arg->u.child.child->args, opt); + else + isl_args_free(arg->u.child.child, + *(void **)(((char *)opt) + arg->offset)); +} + +static void free_str_list(struct isl_arg *arg, void *opt) +{ + int i; + int n = *(int *)(((char *) opt) + arg->u.str_list.offset_n); + char **list = *(char ***)(((char *) opt) + arg->offset); + + for (i = 0; i < n; ++i) + free(list[i]); + free(list); +} + +static void free_user(struct isl_arg *arg, void *opt) +{ + if (arg->u.user.clear) + arg->u.user.clear(((char *)opt) + arg->offset); +} + +static void free_args(struct isl_arg *arg, void *opt) +{ + int i; + + for (i = 0; arg[i].type != isl_arg_end; ++i) { + switch (arg[i].type) { + case isl_arg_child: + free_child(&arg[i], opt); + break; + case isl_arg_arg: + case isl_arg_str: + free(*(char **)(((char *)opt) + arg[i].offset)); + break; + case isl_arg_str_list: + free_str_list(&arg[i], opt); + break; + case isl_arg_user: + free_user(&arg[i], opt); + break; + case isl_arg_alias: + case isl_arg_bool: + case isl_arg_choice: + case isl_arg_flags: + case isl_arg_int: + case isl_arg_long: + case isl_arg_ulong: + case isl_arg_version: + case isl_arg_footer: + case isl_arg_end: + break; + } + } +} + +void isl_args_free(struct isl_args *args, void *opt) +{ + if (!opt) + return; + + free_args(args->args, opt); + + free(opt); +} + +/* Data structure for collecting the prefixes of ancestor nodes. + * + * n is the number of prefixes. + * prefix[i] for i < n is a prefix of an ancestor. + * len[i] for i < n is the length of prefix[i]. + */ +struct isl_prefixes { + int n; + const char *prefix[10]; + size_t len[10]; +}; + +/* Add "prefix" to the list of prefixes and return the updated + * number of prefixes. + */ +static int add_prefix(struct isl_prefixes *prefixes, const char *prefix) +{ + int n = prefixes->n; + + if (!prefix) + return n; + + if (prefixes->n >= 10) { + fprintf(stderr, "too many prefixes\n"); + exit(EXIT_FAILURE); + } + prefixes->len[prefixes->n] = strlen(prefix); + prefixes->prefix[prefixes->n] = prefix; + prefixes->n++; + + return n; +} + +/* Drop all prefixes starting at "first". + */ +static void drop_prefix(struct isl_prefixes *prefixes, int first) +{ + prefixes->n = first; +} + +/* Print the prefixes in "prefixes". + */ +static int print_prefixes(struct isl_prefixes *prefixes) +{ + int i; + int len = 0; + + if (!prefixes) + return 0; + + for (i = 0; i < prefixes->n; ++i) { + printf("%s-", prefixes->prefix[i]); + len += strlen(prefixes->prefix[i]) + 1; + } + + return len; +} + +/* Check if "name" starts with one or more of the prefixes in "prefixes", + * starting at *first. If so, advance the pointer beyond the prefixes + * and return the updated pointer. Additionally, update *first to + * the index after the last prefix found. + */ +static const char *skip_prefixes(const char *name, + struct isl_prefixes *prefixes, int *first) +{ + int i; + + for (i = first ? *first : 0; i < prefixes->n; ++i) { + size_t len = prefixes->len[i]; + const char *prefix = prefixes->prefix[i]; + if (strncmp(name, prefix, len) == 0 && name[len] == '-') { + name += len + 1; + if (first) + *first = i + 1; + } + } + + return name; +} + +static int print_arg_help(struct isl_arg *decl, struct isl_prefixes *prefixes, + int no) +{ + int len = 0; + + if (!decl->long_name) { + printf(" -%c", decl->short_name); + return 4; + } + + if (decl->short_name) { + printf(" -%c, --", decl->short_name); + len += 8; + } else if (decl->flags & ISL_ARG_SINGLE_DASH) { + printf(" -"); + len += 3; + } else { + printf(" --"); + len += 8; + } + + if (no) { + printf("no-"); + len += 3; + } + len += print_prefixes(prefixes); + printf("%s", decl->long_name); + len += strlen(decl->long_name); + + while ((++decl)->type == isl_arg_alias) { + printf(", --"); + len += 4; + if (no) { + printf("no-"); + len += 3; + } + printf("%s", decl->long_name); + len += strlen(decl->long_name); + } + + return len; +} + +const void *isl_memrchr(const void *s, int c, size_t n) +{ + const char *p = s; + while (n-- > 0) + if (p[n] == c) + return p + n; + return NULL; +} + +static int wrap_msg(const char *s, int indent, int pos) +{ + int len; + int wrap_len = 75 - indent; + + if (pos + 1 >= indent) + printf("\n%*s", indent, ""); + else + printf("%*s", indent - pos, ""); + + len = strlen(s); + while (len > wrap_len) { + const char *space = isl_memrchr(s, ' ', wrap_len); + int l; + + if (!space) + space = strchr(s + wrap_len, ' '); + if (!space) + break; + l = space - s; + printf("%.*s", l, s); + s = space + 1; + len -= l + 1; + printf("\n%*s", indent, ""); + } + + printf("%s", s); + return len; +} + +static int print_help_msg(struct isl_arg *decl, int pos) +{ + if (!decl->help_msg) + return pos; + + return wrap_msg(decl->help_msg, 30, pos); +} + +static void print_default(struct isl_arg *decl, const char *def, int pos) +{ + const char *default_prefix = "[default: "; + const char *default_suffix = "]"; + int len; + + len = strlen(default_prefix) + strlen(def) + strlen(default_suffix); + + if (!decl->help_msg) { + if (pos >= 29) + printf("\n%30s", ""); + else + printf("%*s", 30 - pos, ""); + } else { + if (pos + len >= 48) + printf("\n%30s", ""); + else + printf(" "); + } + printf("%s%s%s", default_prefix, def, default_suffix); +} + +static void print_default_choice(struct isl_arg *decl, void *opt, int pos) +{ + int i; + const char *s = "none"; + unsigned *p; + + p = (unsigned *)(((char *) opt) + decl->offset); + for (i = 0; decl->u.choice.choice[i].name; ++i) + if (decl->u.choice.choice[i].value == *p) { + s = decl->u.choice.choice[i].name; + break; + } + + print_default(decl, s, pos); +} + +static void print_choice_help(struct isl_arg *decl, + struct isl_prefixes *prefixes, void *opt) +{ + int i; + int pos; + + pos = print_arg_help(decl, prefixes, 0); + printf("="); + pos++; + + for (i = 0; decl->u.choice.choice[i].name; ++i) { + if (i) { + printf("|"); + pos++; + } + printf("%s", decl->u.choice.choice[i].name); + pos += strlen(decl->u.choice.choice[i].name); + } + + pos = print_help_msg(decl, pos); + print_default_choice(decl, opt, pos); + + printf("\n"); +} + +static void print_default_flags(struct isl_arg *decl, void *opt, int pos) +{ + int i, first; + const char *default_prefix = "[default: "; + const char *default_suffix = "]"; + int len = strlen(default_prefix) + strlen(default_suffix); + unsigned *p; + + p = (unsigned *)(((char *) opt) + decl->offset); + for (i = 0; decl->u.flags.flags[i].name; ++i) + if ((*p & decl->u.flags.flags[i].mask) == + decl->u.flags.flags[i].value) + len += strlen(decl->u.flags.flags[i].name); + + if (!decl->help_msg) { + if (pos >= 29) + printf("\n%30s", ""); + else + printf("%*s", 30 - pos, ""); + } else { + if (pos + len >= 48) + printf("\n%30s", ""); + else + printf(" "); + } + printf("%s", default_prefix); + + for (first = 1, i = 0; decl->u.flags.flags[i].name; ++i) + if ((*p & decl->u.flags.flags[i].mask) == + decl->u.flags.flags[i].value) { + if (!first) + printf(","); + printf("%s", decl->u.flags.flags[i].name); + first = 0; + } + + printf("%s", default_suffix); +} + +static void print_flags_help(struct isl_arg *decl, + struct isl_prefixes *prefixes, void *opt) +{ + int i, j; + int pos; + + pos = print_arg_help(decl, prefixes, 0); + printf("="); + pos++; + + for (i = 0; decl->u.flags.flags[i].name; ++i) { + if (i) { + printf(","); + pos++; + } + for (j = i; + decl->u.flags.flags[j].mask == decl->u.flags.flags[i].mask; + ++j) { + if (j != i) { + printf("|"); + pos++; + } + printf("%s", decl->u.flags.flags[j].name); + pos += strlen(decl->u.flags.flags[j].name); + } + i = j - 1; + } + + pos = print_help_msg(decl, pos); + print_default_flags(decl, opt, pos); + + printf("\n"); +} + +static void print_bool_help(struct isl_arg *decl, + struct isl_prefixes *prefixes, void *opt) +{ + int pos; + unsigned *p = opt ? (unsigned *)(((char *) opt) + decl->offset) : NULL; + int no = p ? *p == 1 : 0; + pos = print_arg_help(decl, prefixes, no); + pos = print_help_msg(decl, pos); + if (decl->offset != (size_t) -1) + print_default(decl, no ? "yes" : "no", pos); + printf("\n"); +} + +static int print_argument_name(struct isl_arg *decl, const char *name, int pos) +{ + printf("%c<%s>", decl->long_name ? '=' : ' ', name); + return pos + 3 + strlen(name); +} + +static void print_int_help(struct isl_arg *decl, + struct isl_prefixes *prefixes, void *opt) +{ + int pos; + char val[20]; + int *p = (int *)(((char *) opt) + decl->offset); + pos = print_arg_help(decl, prefixes, 0); + pos = print_argument_name(decl, decl->argument_name, pos); + pos = print_help_msg(decl, pos); + snprintf(val, sizeof(val), "%d", *p); + print_default(decl, val, pos); + printf("\n"); +} + +static void print_long_help(struct isl_arg *decl, + struct isl_prefixes *prefixes, void *opt) +{ + int pos; + long *p = (long *)(((char *) opt) + decl->offset); + pos = print_arg_help(decl, prefixes, 0); + if (*p != decl->u.l.default_selected) { + printf("["); + pos++; + } + printf("=long"); + pos += 5; + if (*p != decl->u.l.default_selected) { + printf("]"); + pos++; + } + print_help_msg(decl, pos); + printf("\n"); +} + +static void print_ulong_help(struct isl_arg *decl, + struct isl_prefixes *prefixes) +{ + int pos; + pos = print_arg_help(decl, prefixes, 0); + printf("=ulong"); + pos += 6; + print_help_msg(decl, pos); + printf("\n"); +} + +static void print_str_help(struct isl_arg *decl, + struct isl_prefixes *prefixes, void *opt) +{ + int pos; + const char *a = decl->argument_name ? decl->argument_name : "string"; + const char **p = (const char **)(((char *) opt) + decl->offset); + pos = print_arg_help(decl, prefixes, 0); + pos = print_argument_name(decl, a, pos); + pos = print_help_msg(decl, pos); + if (*p) + print_default(decl, *p, pos); + printf("\n"); +} + +static void print_str_list_help(struct isl_arg *decl, + struct isl_prefixes *prefixes) +{ + int pos; + const char *a = decl->argument_name ? decl->argument_name : "string"; + pos = print_arg_help(decl, prefixes, 0); + pos = print_argument_name(decl, a, pos); + pos = print_help_msg(decl, pos); + printf("\n"); +} + +static void print_help(struct isl_arg *arg, + struct isl_prefixes *prefixes, void *opt) +{ + int i; + int any = 0; + + for (i = 0; arg[i].type != isl_arg_end; ++i) { + if (arg[i].flags & ISL_ARG_HIDDEN) + continue; + switch (arg[i].type) { + case isl_arg_flags: + print_flags_help(&arg[i], prefixes, opt); + any = 1; + break; + case isl_arg_choice: + print_choice_help(&arg[i], prefixes, opt); + any = 1; + break; + case isl_arg_bool: + print_bool_help(&arg[i], prefixes, opt); + any = 1; + break; + case isl_arg_int: + print_int_help(&arg[i], prefixes, opt); + any = 1; + break; + case isl_arg_long: + print_long_help(&arg[i], prefixes, opt); + any = 1; + break; + case isl_arg_ulong: + print_ulong_help(&arg[i], prefixes); + any = 1; + break; + case isl_arg_str: + print_str_help(&arg[i], prefixes, opt); + any = 1; + break; + case isl_arg_str_list: + print_str_list_help(&arg[i], prefixes); + any = 1; + break; + case isl_arg_alias: + case isl_arg_version: + case isl_arg_arg: + case isl_arg_footer: + case isl_arg_child: + case isl_arg_user: + case isl_arg_end: + break; + } + } + + for (i = 0; arg[i].type != isl_arg_end; ++i) { + void *child; + int first; + + if (arg[i].type != isl_arg_child) + continue; + if (arg[i].flags & ISL_ARG_HIDDEN) + continue; + + if (any) + printf("\n"); + if (arg[i].help_msg) + printf(" %s\n", arg[i].help_msg); + if (arg[i].offset == (size_t) -1) + child = opt; + else + child = *(void **)(((char *) opt) + arg[i].offset); + first = add_prefix(prefixes, arg[i].long_name); + print_help(arg[i].u.child.child->args, prefixes, child); + drop_prefix(prefixes, first); + any = 1; + } +} + +static const char *prog_name(const char *prog) +{ + const char *slash; + + slash = strrchr(prog, '/'); + if (slash) + prog = slash + 1; + if (strncmp(prog, "lt-", 3) == 0) + prog += 3; + + return prog; +} + +static int any_version(struct isl_arg *decl) +{ + int i; + + for (i = 0; decl[i].type != isl_arg_end; ++i) { + switch (decl[i].type) { + case isl_arg_version: + return 1; + case isl_arg_child: + if (any_version(decl[i].u.child.child->args)) + return 1; + break; + default: + break; + } + } + + return 0; +} + +static void print_help_and_exit(struct isl_arg *arg, const char *prog, + void *opt) +{ + int i; + struct isl_prefixes prefixes = { 0 }; + + printf("Usage: %s [OPTION...]", prog_name(prog)); + + for (i = 0; arg[i].type != isl_arg_end; ++i) + if (arg[i].type == isl_arg_arg) + printf(" %s", arg[i].argument_name); + + printf("\n\n"); + + print_help(arg, &prefixes, opt); + printf("\n"); + if (any_version(arg)) + printf(" -V, --version\n"); + print_bool_help(help_arg, NULL, NULL); + + for (i = 0; arg[i].type != isl_arg_end; ++i) { + if (arg[i].type != isl_arg_footer) + continue; + wrap_msg(arg[i].help_msg, 0, 0); + printf("\n"); + } + + exit(0); +} + +static int match_long_name(struct isl_arg *decl, + const char *start, const char *end) +{ + do { + if (end - start == strlen(decl->long_name) && + !strncmp(start, decl->long_name, end - start)) + return 1; + } while ((++decl)->type == isl_arg_alias); + + return 0; +} + +static const char *skip_dash_dash(struct isl_arg *decl, const char *arg) +{ + if (!strncmp(arg, "--", 2)) + return arg + 2; + if ((decl->flags & ISL_ARG_SINGLE_DASH) && arg[0] == '-') + return arg + 1; + return NULL; +} + +static const char *skip_name(struct isl_arg *decl, const char *arg, + struct isl_prefixes *prefixes, int need_argument, int *has_argument) +{ + const char *equal; + const char *name; + const char *end; + + if (arg[0] == '-' && arg[1] && arg[1] == decl->short_name) { + if (need_argument && !arg[2]) + return NULL; + if (has_argument) + *has_argument = arg[2] != '\0'; + return arg + 2; + } + if (!decl->long_name) + return NULL; + + name = skip_dash_dash(decl, arg); + if (!name) + return NULL; + + equal = strchr(name, '='); + if (need_argument && !equal) + return NULL; + + if (has_argument) + *has_argument = !!equal; + end = equal ? equal : name + strlen(name); + + name = skip_prefixes(name, prefixes, NULL); + + if (!match_long_name(decl, name, end)) + return NULL; + + return equal ? equal + 1 : end; +} + +static int parse_choice_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int i; + int has_argument; + const char *choice; + + choice = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!choice) + return 0; + + if (!has_argument && (!arg[1] || arg[1][0] == '-')) { + unsigned u = decl->u.choice.default_selected; + if (decl->offset != (size_t) -1) + *(unsigned *)(((char *)opt) + decl->offset) = u; + if (decl->u.choice.set) + decl->u.choice.set(opt, u); + + return 1; + } + + if (!has_argument) + choice = arg[1]; + + for (i = 0; decl->u.choice.choice[i].name; ++i) { + unsigned u; + + if (strcmp(choice, decl->u.choice.choice[i].name)) + continue; + + u = decl->u.choice.choice[i].value; + if (decl->offset != (size_t) -1) + *(unsigned *)(((char *)opt) + decl->offset) = u; + if (decl->u.choice.set) + decl->u.choice.set(opt, u); + + return has_argument ? 1 : 2; + } + + return 0; +} + +static int set_flag(struct isl_arg *decl, unsigned *val, const char *flag, + size_t len) +{ + int i; + + for (i = 0; decl->u.flags.flags[i].name; ++i) { + if (strncmp(flag, decl->u.flags.flags[i].name, len)) + continue; + + *val &= ~decl->u.flags.flags[i].mask; + *val |= decl->u.flags.flags[i].value; + + return 1; + } + + return 0; +} + +static int parse_flags_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int has_argument; + const char *flags; + const char *comma; + unsigned val; + + flags = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!flags) + return 0; + + if (!has_argument && !arg[1]) + return 0; + + if (!has_argument) + flags = arg[1]; + + val = 0; + + while ((comma = strchr(flags, ',')) != NULL) { + if (!set_flag(decl, &val, flags, comma - flags)) + return 0; + flags = comma + 1; + } + if (!set_flag(decl, &val, flags, strlen(flags))) + return 0; + + *(unsigned *)(((char *)opt) + decl->offset) = val; + + return has_argument ? 1 : 2; +} + +static int parse_bool_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + const char *name; + unsigned *p = (unsigned *)(((char *)opt) + decl->offset); + int next_prefix; + + if (skip_name(decl, arg[0], prefixes, 0, NULL)) { + if ((decl->flags & ISL_ARG_BOOL_ARG) && arg[1]) { + char *endptr; + int val = strtol(arg[1], &endptr, 0); + if (*endptr == '\0' && (val == 0 || val == 1)) { + if (decl->offset != (size_t) -1) + *p = val; + if (decl->u.b.set) + decl->u.b.set(opt, val); + return 2; + } + } + if (decl->offset != (size_t) -1) + *p = 1; + if (decl->u.b.set) + decl->u.b.set(opt, 1); + + return 1; + } + + if (!decl->long_name) + return 0; + + name = skip_dash_dash(decl, arg[0]); + if (!name) + return 0; + + next_prefix = 0; + name = skip_prefixes(name, prefixes, &next_prefix); + + if (strncmp(name, "no-", 3)) + return 0; + name += 3; + + name = skip_prefixes(name, prefixes, &next_prefix); + + if (match_long_name(decl, name, name + strlen(name))) { + if (decl->offset != (size_t) -1) + *p = 0; + if (decl->u.b.set) + decl->u.b.set(opt, 0); + + return 1; + } + + return 0; +} + +static int parse_str_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int has_argument; + const char *s; + char **p = (char **)(((char *)opt) + decl->offset); + + s = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!s) + return 0; + + if (has_argument) { + free(*p); + *p = strdup(s); + return 1; + } + + if (arg[1]) { + free(*p); + *p = strdup(arg[1]); + return 2; + } + + return 0; +} + +static int isl_arg_str_list_append(struct isl_arg *decl, void *opt, + const char *s) +{ + int *n = (int *)(((char *) opt) + decl->u.str_list.offset_n); + char **list = *(char ***)(((char *) opt) + decl->offset); + + list = realloc(list, (*n + 1) * sizeof(char *)); + if (!list) + return -1; + *(char ***)(((char *) opt) + decl->offset) = list; + list[*n] = strdup(s); + (*n)++; + return 0; +} + +static int parse_str_list_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int has_argument; + const char *s; + + s = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!s) + return 0; + + if (has_argument) { + isl_arg_str_list_append(decl, opt, s); + return 1; + } + + if (arg[1]) { + isl_arg_str_list_append(decl, opt, arg[1]); + return 2; + } + + return 0; +} + +static int parse_int_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int has_argument; + const char *val; + char *endptr; + int *p = (int *)(((char *)opt) + decl->offset); + + val = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!val) + return 0; + + if (has_argument) { + *p = atoi(val); + return 1; + } + + if (arg[1]) { + int i = strtol(arg[1], &endptr, 0); + if (*endptr == '\0') { + *p = i; + return 2; + } + } + + return 0; +} + +static int parse_long_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int has_argument; + const char *val; + char *endptr; + long *p = (long *)(((char *)opt) + decl->offset); + + val = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!val) + return 0; + + if (has_argument) { + long l = strtol(val, NULL, 0); + *p = l; + if (decl->u.l.set) + decl->u.l.set(opt, l); + return 1; + } + + if (arg[1]) { + long l = strtol(arg[1], &endptr, 0); + if (*endptr == '\0') { + *p = l; + if (decl->u.l.set) + decl->u.l.set(opt, l); + return 2; + } + } + + if (decl->u.l.default_value != decl->u.l.default_selected) { + *p = decl->u.l.default_selected; + if (decl->u.l.set) + decl->u.l.set(opt, decl->u.l.default_selected); + return 1; + } + + return 0; +} + +static int parse_ulong_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int has_argument; + const char *val; + char *endptr; + unsigned long *p = (unsigned long *)(((char *)opt) + decl->offset); + + val = skip_name(decl, arg[0], prefixes, 0, &has_argument); + if (!val) + return 0; + + if (has_argument) { + *p = strtoul(val, NULL, 0); + return 1; + } + + if (arg[1]) { + unsigned long ul = strtoul(arg[1], &endptr, 0); + if (*endptr == '\0') { + *p = ul; + return 2; + } + } + + return 0; +} + +static int parse_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt); + +static int parse_child_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + void *child; + int first, parsed; + + if (decl->offset == (size_t) -1) + child = opt; + else + child = *(void **)(((char *)opt) + decl->offset); + + first = add_prefix(prefixes, decl->long_name); + parsed = parse_option(decl->u.child.child->args, arg, prefixes, child); + drop_prefix(prefixes, first); + + return parsed; +} + +static int parse_option(struct isl_arg *decl, char **arg, + struct isl_prefixes *prefixes, void *opt) +{ + int i; + + for (i = 0; decl[i].type != isl_arg_end; ++i) { + int parsed = 0; + switch (decl[i].type) { + case isl_arg_choice: + parsed = parse_choice_option(&decl[i], arg, + prefixes, opt); + break; + case isl_arg_flags: + parsed = parse_flags_option(&decl[i], arg, + prefixes, opt); + break; + case isl_arg_int: + parsed = parse_int_option(&decl[i], arg, prefixes, opt); + break; + case isl_arg_long: + parsed = parse_long_option(&decl[i], arg, + prefixes, opt); + break; + case isl_arg_ulong: + parsed = parse_ulong_option(&decl[i], arg, + prefixes, opt); + break; + case isl_arg_bool: + parsed = parse_bool_option(&decl[i], arg, + prefixes, opt); + break; + case isl_arg_str: + parsed = parse_str_option(&decl[i], arg, prefixes, opt); + break; + case isl_arg_str_list: + parsed = parse_str_list_option(&decl[i], arg, prefixes, + opt); + break; + case isl_arg_child: + parsed = parse_child_option(&decl[i], arg, + prefixes, opt); + break; + case isl_arg_alias: + case isl_arg_arg: + case isl_arg_footer: + case isl_arg_user: + case isl_arg_version: + case isl_arg_end: + break; + } + if (parsed) + return parsed; + } + + return 0; +} + +static void print_version(struct isl_arg *decl) +{ + int i; + + for (i = 0; decl[i].type != isl_arg_end; ++i) { + switch (decl[i].type) { + case isl_arg_version: + decl[i].u.version.print_version(); + break; + case isl_arg_child: + print_version(decl[i].u.child.child->args); + break; + default: + break; + } + } +} + +static void print_version_and_exit(struct isl_arg *decl) +{ + print_version(decl); + + exit(0); +} + +static int drop_argument(int argc, char **argv, int drop, int n) +{ + for (; drop + n < argc; ++drop) + argv[drop] = argv[drop + n]; + + return argc - n; +} + +static int n_arg(struct isl_arg *arg) +{ + int i; + int n_arg = 0; + + for (i = 0; arg[i].type != isl_arg_end; ++i) + if (arg[i].type == isl_arg_arg) + n_arg++; + + return n_arg; +} + +static int next_arg(struct isl_arg *arg, int a) +{ + for (++a; arg[a].type != isl_arg_end; ++a) + if (arg[a].type == isl_arg_arg) + return a; + + return -1; +} + +/* Unless ISL_ARG_SKIP_HELP is set, check if "arg" is + * equal to "--help" and if so call print_help_and_exit. + */ +static void check_help(struct isl_args *args, char *arg, char *prog, void *opt, + unsigned flags) +{ + if (ISL_FL_ISSET(flags, ISL_ARG_SKIP_HELP)) + return; + + if (strcmp(arg, "--help") == 0) + print_help_and_exit(args->args, prog, opt); +} + +int isl_args_parse(struct isl_args *args, int argc, char **argv, void *opt, + unsigned flags) +{ + int a = -1; + int skip = 0; + int i; + int n; + struct isl_prefixes prefixes = { 0 }; + + n = n_arg(args->args); + + for (i = 1; i < argc; ++i) { + if ((strcmp(argv[i], "--version") == 0 || + strcmp(argv[i], "-V") == 0) && any_version(args->args)) + print_version_and_exit(args->args); + } + + while (argc > 1 + skip) { + int parsed; + if (argv[1 + skip][0] != '-') { + a = next_arg(args->args, a); + if (a >= 0) { + char **p; + p = (char **)(((char *)opt)+args->args[a].offset); + free(*p); + *p = strdup(argv[1 + skip]); + argc = drop_argument(argc, argv, 1 + skip, 1); + --n; + } else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) { + fprintf(stderr, "%s: extra argument: %s\n", + prog_name(argv[0]), argv[1 + skip]); + exit(-1); + } else + ++skip; + continue; + } + check_help(args, argv[1 + skip], argv[0], opt, flags); + parsed = parse_option(args->args, &argv[1 + skip], + &prefixes, opt); + if (parsed) + argc = drop_argument(argc, argv, 1 + skip, parsed); + else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) { + fprintf(stderr, "%s: unrecognized option: %s\n", + prog_name(argv[0]), argv[1 + skip]); + exit(-1); + } else + ++skip; + } + + if (n > 0) { + fprintf(stderr, "%s: expecting %d more argument(s)\n", + prog_name(argv[0]), n); + exit(-1); + } + + return argc; +} Index: lib/Analysis/isl/isl_ast.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast.c @@ -0,0 +1,2589 @@ +/* + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include + +#include + +#undef BASE +#define BASE ast_expr + +#include + +#undef BASE +#define BASE ast_node + +#include + +isl_ctx *isl_ast_print_options_get_ctx( + __isl_keep isl_ast_print_options *options) +{ + return options ? options->ctx : NULL; +} + +__isl_give isl_ast_print_options *isl_ast_print_options_alloc(isl_ctx *ctx) +{ + isl_ast_print_options *options; + + options = isl_calloc_type(ctx, isl_ast_print_options); + if (!options) + return NULL; + + options->ctx = ctx; + isl_ctx_ref(ctx); + options->ref = 1; + + return options; +} + +__isl_give isl_ast_print_options *isl_ast_print_options_dup( + __isl_keep isl_ast_print_options *options) +{ + isl_ctx *ctx; + isl_ast_print_options *dup; + + if (!options) + return NULL; + + ctx = isl_ast_print_options_get_ctx(options); + dup = isl_ast_print_options_alloc(ctx); + if (!dup) + return NULL; + + dup->print_for = options->print_for; + dup->print_for_user = options->print_for_user; + dup->print_user = options->print_user; + dup->print_user_user = options->print_user_user; + + return dup; +} + +__isl_give isl_ast_print_options *isl_ast_print_options_cow( + __isl_take isl_ast_print_options *options) +{ + if (!options) + return NULL; + + if (options->ref == 1) + return options; + options->ref--; + return isl_ast_print_options_dup(options); +} + +__isl_give isl_ast_print_options *isl_ast_print_options_copy( + __isl_keep isl_ast_print_options *options) +{ + if (!options) + return NULL; + + options->ref++; + return options; +} + +__isl_null isl_ast_print_options *isl_ast_print_options_free( + __isl_take isl_ast_print_options *options) +{ + if (!options) + return NULL; + + if (--options->ref > 0) + return NULL; + + isl_ctx_deref(options->ctx); + + free(options); + return NULL; +} + +/* Set the print_user callback of "options" to "print_user". + * + * If this callback is set, then it used to print user nodes in the AST. + * Otherwise, the expression associated to the user node is printed. + */ +__isl_give isl_ast_print_options *isl_ast_print_options_set_print_user( + __isl_take isl_ast_print_options *options, + __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user), + void *user) +{ + options = isl_ast_print_options_cow(options); + if (!options) + return NULL; + + options->print_user = print_user; + options->print_user_user = user; + + return options; +} + +/* Set the print_for callback of "options" to "print_for". + * + * If this callback is set, then it used to print for nodes in the AST. + */ +__isl_give isl_ast_print_options *isl_ast_print_options_set_print_for( + __isl_take isl_ast_print_options *options, + __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user), + void *user) +{ + options = isl_ast_print_options_cow(options); + if (!options) + return NULL; + + options->print_for = print_for; + options->print_for_user = user; + + return options; +} + +__isl_give isl_ast_expr *isl_ast_expr_copy(__isl_keep isl_ast_expr *expr) +{ + if (!expr) + return NULL; + + expr->ref++; + return expr; +} + +__isl_give isl_ast_expr *isl_ast_expr_dup(__isl_keep isl_ast_expr *expr) +{ + int i; + isl_ctx *ctx; + isl_ast_expr *dup; + + if (!expr) + return NULL; + + ctx = isl_ast_expr_get_ctx(expr); + switch (expr->type) { + case isl_ast_expr_int: + dup = isl_ast_expr_from_val(isl_val_copy(expr->u.v)); + break; + case isl_ast_expr_id: + dup = isl_ast_expr_from_id(isl_id_copy(expr->u.id)); + break; + case isl_ast_expr_op: + dup = isl_ast_expr_alloc_op(ctx, + expr->u.op.op, expr->u.op.n_arg); + if (!dup) + return NULL; + for (i = 0; i < expr->u.op.n_arg; ++i) + dup->u.op.args[i] = + isl_ast_expr_copy(expr->u.op.args[i]); + break; + case isl_ast_expr_error: + dup = NULL; + } + + if (!dup) + return NULL; + + return dup; +} + +__isl_give isl_ast_expr *isl_ast_expr_cow(__isl_take isl_ast_expr *expr) +{ + if (!expr) + return NULL; + + if (expr->ref == 1) + return expr; + expr->ref--; + return isl_ast_expr_dup(expr); +} + +__isl_null isl_ast_expr *isl_ast_expr_free(__isl_take isl_ast_expr *expr) +{ + int i; + + if (!expr) + return NULL; + + if (--expr->ref > 0) + return NULL; + + isl_ctx_deref(expr->ctx); + + switch (expr->type) { + case isl_ast_expr_int: + isl_val_free(expr->u.v); + break; + case isl_ast_expr_id: + isl_id_free(expr->u.id); + break; + case isl_ast_expr_op: + if (expr->u.op.args) + for (i = 0; i < expr->u.op.n_arg; ++i) + isl_ast_expr_free(expr->u.op.args[i]); + free(expr->u.op.args); + break; + case isl_ast_expr_error: + break; + } + + free(expr); + return NULL; +} + +isl_ctx *isl_ast_expr_get_ctx(__isl_keep isl_ast_expr *expr) +{ + return expr ? expr->ctx : NULL; +} + +enum isl_ast_expr_type isl_ast_expr_get_type(__isl_keep isl_ast_expr *expr) +{ + return expr ? expr->type : isl_ast_expr_error; +} + +/* Return the integer value represented by "expr". + */ +__isl_give isl_val *isl_ast_expr_get_val(__isl_keep isl_ast_expr *expr) +{ + if (!expr) + return NULL; + if (expr->type != isl_ast_expr_int) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an int", return NULL); + return isl_val_copy(expr->u.v); +} + +__isl_give isl_id *isl_ast_expr_get_id(__isl_keep isl_ast_expr *expr) +{ + if (!expr) + return NULL; + if (expr->type != isl_ast_expr_id) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an identifier", return NULL); + + return isl_id_copy(expr->u.id); +} + +enum isl_ast_op_type isl_ast_expr_get_op_type(__isl_keep isl_ast_expr *expr) +{ + if (!expr) + return isl_ast_op_error; + if (expr->type != isl_ast_expr_op) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an operation", return isl_ast_op_error); + return expr->u.op.op; +} + +int isl_ast_expr_get_op_n_arg(__isl_keep isl_ast_expr *expr) +{ + if (!expr) + return -1; + if (expr->type != isl_ast_expr_op) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an operation", return -1); + return expr->u.op.n_arg; +} + +__isl_give isl_ast_expr *isl_ast_expr_get_op_arg(__isl_keep isl_ast_expr *expr, + int pos) +{ + if (!expr) + return NULL; + if (expr->type != isl_ast_expr_op) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an operation", return NULL); + if (pos < 0 || pos >= expr->u.op.n_arg) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "index out of bounds", return NULL); + + return isl_ast_expr_copy(expr->u.op.args[pos]); +} + +/* Replace the argument at position "pos" of "expr" by "arg". + */ +__isl_give isl_ast_expr *isl_ast_expr_set_op_arg(__isl_take isl_ast_expr *expr, + int pos, __isl_take isl_ast_expr *arg) +{ + expr = isl_ast_expr_cow(expr); + if (!expr || !arg) + goto error; + if (expr->type != isl_ast_expr_op) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an operation", goto error); + if (pos < 0 || pos >= expr->u.op.n_arg) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "index out of bounds", goto error); + + isl_ast_expr_free(expr->u.op.args[pos]); + expr->u.op.args[pos] = arg; + + return expr; +error: + isl_ast_expr_free(arg); + return isl_ast_expr_free(expr); +} + +/* Is "expr1" equal to "expr2"? + */ +isl_bool isl_ast_expr_is_equal(__isl_keep isl_ast_expr *expr1, + __isl_keep isl_ast_expr *expr2) +{ + int i; + + if (!expr1 || !expr2) + return isl_bool_error; + + if (expr1 == expr2) + return isl_bool_true; + if (expr1->type != expr2->type) + return isl_bool_false; + switch (expr1->type) { + case isl_ast_expr_int: + return isl_val_eq(expr1->u.v, expr2->u.v); + case isl_ast_expr_id: + return expr1->u.id == expr2->u.id; + case isl_ast_expr_op: + if (expr1->u.op.op != expr2->u.op.op) + return isl_bool_false; + if (expr1->u.op.n_arg != expr2->u.op.n_arg) + return isl_bool_false; + for (i = 0; i < expr1->u.op.n_arg; ++i) { + isl_bool equal; + equal = isl_ast_expr_is_equal(expr1->u.op.args[i], + expr2->u.op.args[i]); + if (equal < 0 || !equal) + return equal; + } + return 1; + case isl_ast_expr_error: + return isl_bool_error; + } + + isl_die(isl_ast_expr_get_ctx(expr1), isl_error_internal, + "unhandled case", return isl_bool_error); +} + +/* Create a new operation expression of operation type "op", + * with "n_arg" as yet unspecified arguments. + */ +__isl_give isl_ast_expr *isl_ast_expr_alloc_op(isl_ctx *ctx, + enum isl_ast_op_type op, int n_arg) +{ + isl_ast_expr *expr; + + expr = isl_calloc_type(ctx, isl_ast_expr); + if (!expr) + return NULL; + + expr->ctx = ctx; + isl_ctx_ref(ctx); + expr->ref = 1; + expr->type = isl_ast_expr_op; + expr->u.op.op = op; + expr->u.op.n_arg = n_arg; + expr->u.op.args = isl_calloc_array(ctx, isl_ast_expr *, n_arg); + + if (n_arg && !expr->u.op.args) + return isl_ast_expr_free(expr); + + return expr; +} + +/* Create a new id expression representing "id". + */ +__isl_give isl_ast_expr *isl_ast_expr_from_id(__isl_take isl_id *id) +{ + isl_ctx *ctx; + isl_ast_expr *expr; + + if (!id) + return NULL; + + ctx = isl_id_get_ctx(id); + expr = isl_calloc_type(ctx, isl_ast_expr); + if (!expr) + goto error; + + expr->ctx = ctx; + isl_ctx_ref(ctx); + expr->ref = 1; + expr->type = isl_ast_expr_id; + expr->u.id = id; + + return expr; +error: + isl_id_free(id); + return NULL; +} + +/* Create a new integer expression representing "i". + */ +__isl_give isl_ast_expr *isl_ast_expr_alloc_int_si(isl_ctx *ctx, int i) +{ + isl_ast_expr *expr; + + expr = isl_calloc_type(ctx, isl_ast_expr); + if (!expr) + return NULL; + + expr->ctx = ctx; + isl_ctx_ref(ctx); + expr->ref = 1; + expr->type = isl_ast_expr_int; + expr->u.v = isl_val_int_from_si(ctx, i); + if (!expr->u.v) + return isl_ast_expr_free(expr); + + return expr; +} + +/* Create a new integer expression representing "v". + */ +__isl_give isl_ast_expr *isl_ast_expr_from_val(__isl_take isl_val *v) +{ + isl_ctx *ctx; + isl_ast_expr *expr; + + if (!v) + return NULL; + if (!isl_val_is_int(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting integer value", goto error); + + ctx = isl_val_get_ctx(v); + expr = isl_calloc_type(ctx, isl_ast_expr); + if (!expr) + goto error; + + expr->ctx = ctx; + isl_ctx_ref(ctx); + expr->ref = 1; + expr->type = isl_ast_expr_int; + expr->u.v = v; + + return expr; +error: + isl_val_free(v); + return NULL; +} + +/* Create an expression representing the unary operation "type" applied to + * "arg". + */ +__isl_give isl_ast_expr *isl_ast_expr_alloc_unary(enum isl_ast_op_type type, + __isl_take isl_ast_expr *arg) +{ + isl_ctx *ctx; + isl_ast_expr *expr = NULL; + + if (!arg) + return NULL; + + ctx = isl_ast_expr_get_ctx(arg); + expr = isl_ast_expr_alloc_op(ctx, type, 1); + if (!expr) + goto error; + + expr->u.op.args[0] = arg; + + return expr; +error: + isl_ast_expr_free(arg); + return NULL; +} + +/* Create an expression representing the negation of "arg". + */ +__isl_give isl_ast_expr *isl_ast_expr_neg(__isl_take isl_ast_expr *arg) +{ + return isl_ast_expr_alloc_unary(isl_ast_op_minus, arg); +} + +/* Create an expression representing the address of "expr". + */ +__isl_give isl_ast_expr *isl_ast_expr_address_of(__isl_take isl_ast_expr *expr) +{ + if (!expr) + return NULL; + + if (isl_ast_expr_get_type(expr) != isl_ast_expr_op || + isl_ast_expr_get_op_type(expr) != isl_ast_op_access) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "can only take address of access expressions", + return isl_ast_expr_free(expr)); + + return isl_ast_expr_alloc_unary(isl_ast_op_address_of, expr); +} + +/* Create an expression representing the binary operation "type" + * applied to "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_alloc_binary(enum isl_ast_op_type type, + __isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2) +{ + isl_ctx *ctx; + isl_ast_expr *expr = NULL; + + if (!expr1 || !expr2) + goto error; + + ctx = isl_ast_expr_get_ctx(expr1); + expr = isl_ast_expr_alloc_op(ctx, type, 2); + if (!expr) + goto error; + + expr->u.op.args[0] = expr1; + expr->u.op.args[1] = expr2; + + return expr; +error: + isl_ast_expr_free(expr1); + isl_ast_expr_free(expr2); + return NULL; +} + +/* Create an expression representing the sum of "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_add(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_add, expr1, expr2); +} + +/* Create an expression representing the difference of "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_sub(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_sub, expr1, expr2); +} + +/* Create an expression representing the product of "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_mul(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_mul, expr1, expr2); +} + +/* Create an expression representing the quotient of "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_div(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_div, expr1, expr2); +} + +/* Create an expression representing the quotient of the integer + * division of "expr1" by "expr2", where "expr1" is known to be + * non-negative. + */ +__isl_give isl_ast_expr *isl_ast_expr_pdiv_q(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_pdiv_q, expr1, expr2); +} + +/* Create an expression representing the remainder of the integer + * division of "expr1" by "expr2", where "expr1" is known to be + * non-negative. + */ +__isl_give isl_ast_expr *isl_ast_expr_pdiv_r(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_pdiv_r, expr1, expr2); +} + +/* Create an expression representing the conjunction of "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_and(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_and, expr1, expr2); +} + +/* Create an expression representing the conjunction of "expr1" and "expr2", + * where "expr2" is evaluated only if "expr1" is evaluated to true. + */ +__isl_give isl_ast_expr *isl_ast_expr_and_then(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_and_then, expr1, expr2); +} + +/* Create an expression representing the disjunction of "expr1" and "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_or(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_or, expr1, expr2); +} + +/* Create an expression representing the disjunction of "expr1" and "expr2", + * where "expr2" is evaluated only if "expr1" is evaluated to false. + */ +__isl_give isl_ast_expr *isl_ast_expr_or_else(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_or_else, expr1, expr2); +} + +/* Create an expression representing "expr1" less than or equal to "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_le(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_le, expr1, expr2); +} + +/* Create an expression representing "expr1" less than "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_lt(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_lt, expr1, expr2); +} + +/* Create an expression representing "expr1" greater than or equal to "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_ge(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_ge, expr1, expr2); +} + +/* Create an expression representing "expr1" greater than "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_gt(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_gt, expr1, expr2); +} + +/* Create an expression representing "expr1" equal to "expr2". + */ +__isl_give isl_ast_expr *isl_ast_expr_eq(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + return isl_ast_expr_alloc_binary(isl_ast_op_eq, expr1, expr2); +} + +/* Create an expression of type "type" with as arguments "arg0" followed + * by "arguments". + */ +static __isl_give isl_ast_expr *ast_expr_with_arguments( + enum isl_ast_op_type type, __isl_take isl_ast_expr *arg0, + __isl_take isl_ast_expr_list *arguments) +{ + int i, n; + isl_ctx *ctx; + isl_ast_expr *res = NULL; + + if (!arg0 || !arguments) + goto error; + + ctx = isl_ast_expr_get_ctx(arg0); + n = isl_ast_expr_list_n_ast_expr(arguments); + res = isl_ast_expr_alloc_op(ctx, type, 1 + n); + if (!res) + goto error; + for (i = 0; i < n; ++i) { + isl_ast_expr *arg; + arg = isl_ast_expr_list_get_ast_expr(arguments, i); + res->u.op.args[1 + i] = arg; + if (!arg) + goto error; + } + res->u.op.args[0] = arg0; + + isl_ast_expr_list_free(arguments); + return res; +error: + isl_ast_expr_free(arg0); + isl_ast_expr_list_free(arguments); + isl_ast_expr_free(res); + return NULL; +} + +/* Create an expression representing an access to "array" with index + * expressions "indices". + */ +__isl_give isl_ast_expr *isl_ast_expr_access(__isl_take isl_ast_expr *array, + __isl_take isl_ast_expr_list *indices) +{ + return ast_expr_with_arguments(isl_ast_op_access, array, indices); +} + +/* Create an expression representing a call to "function" with argument + * expressions "arguments". + */ +__isl_give isl_ast_expr *isl_ast_expr_call(__isl_take isl_ast_expr *function, + __isl_take isl_ast_expr_list *arguments) +{ + return ast_expr_with_arguments(isl_ast_op_call, function, arguments); +} + +/* For each subexpression of "expr" of type isl_ast_expr_id, + * if it appears in "id2expr", then replace it by the corresponding + * expression. + */ +__isl_give isl_ast_expr *isl_ast_expr_substitute_ids( + __isl_take isl_ast_expr *expr, __isl_take isl_id_to_ast_expr *id2expr) +{ + int i; + isl_maybe_isl_ast_expr m; + + if (!expr || !id2expr) + goto error; + + switch (expr->type) { + case isl_ast_expr_int: + break; + case isl_ast_expr_id: + m = isl_id_to_ast_expr_try_get(id2expr, expr->u.id); + if (m.valid < 0) + goto error; + if (!m.valid) + break; + isl_ast_expr_free(expr); + expr = m.value; + break; + case isl_ast_expr_op: + for (i = 0; i < expr->u.op.n_arg; ++i) { + isl_ast_expr *arg; + arg = isl_ast_expr_copy(expr->u.op.args[i]); + arg = isl_ast_expr_substitute_ids(arg, + isl_id_to_ast_expr_copy(id2expr)); + if (arg == expr->u.op.args[i]) { + isl_ast_expr_free(arg); + continue; + } + if (!arg) + expr = isl_ast_expr_free(expr); + expr = isl_ast_expr_cow(expr); + if (!expr) { + isl_ast_expr_free(arg); + break; + } + isl_ast_expr_free(expr->u.op.args[i]); + expr->u.op.args[i] = arg; + } + break; + case isl_ast_expr_error: + expr = isl_ast_expr_free(expr); + break; + } + + isl_id_to_ast_expr_free(id2expr); + return expr; +error: + isl_ast_expr_free(expr); + isl_id_to_ast_expr_free(id2expr); + return NULL; +} + +isl_ctx *isl_ast_node_get_ctx(__isl_keep isl_ast_node *node) +{ + return node ? node->ctx : NULL; +} + +enum isl_ast_node_type isl_ast_node_get_type(__isl_keep isl_ast_node *node) +{ + return node ? node->type : isl_ast_node_error; +} + +__isl_give isl_ast_node *isl_ast_node_alloc(isl_ctx *ctx, + enum isl_ast_node_type type) +{ + isl_ast_node *node; + + node = isl_calloc_type(ctx, isl_ast_node); + if (!node) + return NULL; + + node->ctx = ctx; + isl_ctx_ref(ctx); + node->ref = 1; + node->type = type; + + return node; +} + +/* Create an if node with the given guard. + * + * The then body needs to be filled in later. + */ +__isl_give isl_ast_node *isl_ast_node_alloc_if(__isl_take isl_ast_expr *guard) +{ + isl_ast_node *node; + + if (!guard) + return NULL; + + node = isl_ast_node_alloc(isl_ast_expr_get_ctx(guard), isl_ast_node_if); + if (!node) + goto error; + node->u.i.guard = guard; + + return node; +error: + isl_ast_expr_free(guard); + return NULL; +} + +/* Create a for node with the given iterator. + * + * The remaining fields need to be filled in later. + */ +__isl_give isl_ast_node *isl_ast_node_alloc_for(__isl_take isl_id *id) +{ + isl_ast_node *node; + isl_ctx *ctx; + + if (!id) + return NULL; + + ctx = isl_id_get_ctx(id); + node = isl_ast_node_alloc(ctx, isl_ast_node_for); + if (!node) + goto error; + + node->u.f.iterator = isl_ast_expr_from_id(id); + if (!node->u.f.iterator) + return isl_ast_node_free(node); + + return node; +error: + isl_id_free(id); + return NULL; +} + +/* Create a mark node, marking "node" with "id". + */ +__isl_give isl_ast_node *isl_ast_node_alloc_mark(__isl_take isl_id *id, + __isl_take isl_ast_node *node) +{ + isl_ctx *ctx; + isl_ast_node *mark; + + if (!id || !node) + goto error; + + ctx = isl_id_get_ctx(id); + mark = isl_ast_node_alloc(ctx, isl_ast_node_mark); + if (!mark) + goto error; + + mark->u.m.mark = id; + mark->u.m.node = node; + + return mark; +error: + isl_id_free(id); + isl_ast_node_free(node); + return NULL; +} + +/* Create a user node evaluating "expr". + */ +__isl_give isl_ast_node *isl_ast_node_alloc_user(__isl_take isl_ast_expr *expr) +{ + isl_ctx *ctx; + isl_ast_node *node; + + if (!expr) + return NULL; + + ctx = isl_ast_expr_get_ctx(expr); + node = isl_ast_node_alloc(ctx, isl_ast_node_user); + if (!node) + goto error; + + node->u.e.expr = expr; + + return node; +error: + isl_ast_expr_free(expr); + return NULL; +} + +/* Create a block node with the given children. + */ +__isl_give isl_ast_node *isl_ast_node_alloc_block( + __isl_take isl_ast_node_list *list) +{ + isl_ast_node *node; + isl_ctx *ctx; + + if (!list) + return NULL; + + ctx = isl_ast_node_list_get_ctx(list); + node = isl_ast_node_alloc(ctx, isl_ast_node_block); + if (!node) + goto error; + + node->u.b.children = list; + + return node; +error: + isl_ast_node_list_free(list); + return NULL; +} + +/* Represent the given list of nodes as a single node, either by + * extract the node from a single element list or by creating + * a block node with the list of nodes as children. + */ +__isl_give isl_ast_node *isl_ast_node_from_ast_node_list( + __isl_take isl_ast_node_list *list) +{ + isl_ast_node *node; + + if (isl_ast_node_list_n_ast_node(list) != 1) + return isl_ast_node_alloc_block(list); + + node = isl_ast_node_list_get_ast_node(list, 0); + isl_ast_node_list_free(list); + + return node; +} + +__isl_give isl_ast_node *isl_ast_node_copy(__isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + + node->ref++; + return node; +} + +__isl_give isl_ast_node *isl_ast_node_dup(__isl_keep isl_ast_node *node) +{ + isl_ast_node *dup; + + if (!node) + return NULL; + + dup = isl_ast_node_alloc(isl_ast_node_get_ctx(node), node->type); + if (!dup) + return NULL; + + switch (node->type) { + case isl_ast_node_if: + dup->u.i.guard = isl_ast_expr_copy(node->u.i.guard); + dup->u.i.then = isl_ast_node_copy(node->u.i.then); + dup->u.i.else_node = isl_ast_node_copy(node->u.i.else_node); + if (!dup->u.i.guard || !dup->u.i.then || + (node->u.i.else_node && !dup->u.i.else_node)) + return isl_ast_node_free(dup); + break; + case isl_ast_node_for: + dup->u.f.iterator = isl_ast_expr_copy(node->u.f.iterator); + dup->u.f.init = isl_ast_expr_copy(node->u.f.init); + dup->u.f.cond = isl_ast_expr_copy(node->u.f.cond); + dup->u.f.inc = isl_ast_expr_copy(node->u.f.inc); + dup->u.f.body = isl_ast_node_copy(node->u.f.body); + if (!dup->u.f.iterator || !dup->u.f.init || !dup->u.f.cond || + !dup->u.f.inc || !dup->u.f.body) + return isl_ast_node_free(dup); + break; + case isl_ast_node_block: + dup->u.b.children = isl_ast_node_list_copy(node->u.b.children); + if (!dup->u.b.children) + return isl_ast_node_free(dup); + break; + case isl_ast_node_mark: + dup->u.m.mark = isl_id_copy(node->u.m.mark); + dup->u.m.node = isl_ast_node_copy(node->u.m.node); + if (!dup->u.m.mark || !dup->u.m.node) + return isl_ast_node_free(dup); + break; + case isl_ast_node_user: + dup->u.e.expr = isl_ast_expr_copy(node->u.e.expr); + if (!dup->u.e.expr) + return isl_ast_node_free(dup); + break; + case isl_ast_node_error: + break; + } + + return dup; +} + +__isl_give isl_ast_node *isl_ast_node_cow(__isl_take isl_ast_node *node) +{ + if (!node) + return NULL; + + if (node->ref == 1) + return node; + node->ref--; + return isl_ast_node_dup(node); +} + +__isl_null isl_ast_node *isl_ast_node_free(__isl_take isl_ast_node *node) +{ + if (!node) + return NULL; + + if (--node->ref > 0) + return NULL; + + switch (node->type) { + case isl_ast_node_if: + isl_ast_expr_free(node->u.i.guard); + isl_ast_node_free(node->u.i.then); + isl_ast_node_free(node->u.i.else_node); + break; + case isl_ast_node_for: + isl_ast_expr_free(node->u.f.iterator); + isl_ast_expr_free(node->u.f.init); + isl_ast_expr_free(node->u.f.cond); + isl_ast_expr_free(node->u.f.inc); + isl_ast_node_free(node->u.f.body); + break; + case isl_ast_node_block: + isl_ast_node_list_free(node->u.b.children); + break; + case isl_ast_node_mark: + isl_id_free(node->u.m.mark); + isl_ast_node_free(node->u.m.node); + break; + case isl_ast_node_user: + isl_ast_expr_free(node->u.e.expr); + break; + case isl_ast_node_error: + break; + } + + isl_id_free(node->annotation); + isl_ctx_deref(node->ctx); + free(node); + + return NULL; +} + +/* Replace the body of the for node "node" by "body". + */ +__isl_give isl_ast_node *isl_ast_node_for_set_body( + __isl_take isl_ast_node *node, __isl_take isl_ast_node *body) +{ + node = isl_ast_node_cow(node); + if (!node || !body) + goto error; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", goto error); + + isl_ast_node_free(node->u.f.body); + node->u.f.body = body; + + return node; +error: + isl_ast_node_free(node); + isl_ast_node_free(body); + return NULL; +} + +__isl_give isl_ast_node *isl_ast_node_for_get_body( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", return NULL); + return isl_ast_node_copy(node->u.f.body); +} + +/* Mark the given for node as being degenerate. + */ +__isl_give isl_ast_node *isl_ast_node_for_mark_degenerate( + __isl_take isl_ast_node *node) +{ + node = isl_ast_node_cow(node); + if (!node) + return NULL; + node->u.f.degenerate = 1; + return node; +} + +isl_bool isl_ast_node_for_is_degenerate(__isl_keep isl_ast_node *node) +{ + if (!node) + return isl_bool_error; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", return isl_bool_error); + return node->u.f.degenerate; +} + +__isl_give isl_ast_expr *isl_ast_node_for_get_iterator( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", return NULL); + return isl_ast_expr_copy(node->u.f.iterator); +} + +__isl_give isl_ast_expr *isl_ast_node_for_get_init( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", return NULL); + return isl_ast_expr_copy(node->u.f.init); +} + +/* Return the condition expression of the given for node. + * + * If the for node is degenerate, then the condition is not explicitly + * stored in the node. Instead, it is constructed as + * + * iterator <= init + */ +__isl_give isl_ast_expr *isl_ast_node_for_get_cond( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", return NULL); + if (!node->u.f.degenerate) + return isl_ast_expr_copy(node->u.f.cond); + + return isl_ast_expr_alloc_binary(isl_ast_op_le, + isl_ast_expr_copy(node->u.f.iterator), + isl_ast_expr_copy(node->u.f.init)); +} + +/* Return the increment of the given for node. + * + * If the for node is degenerate, then the increment is not explicitly + * stored in the node. We simply return "1". + */ +__isl_give isl_ast_expr *isl_ast_node_for_get_inc( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", return NULL); + if (!node->u.f.degenerate) + return isl_ast_expr_copy(node->u.f.inc); + return isl_ast_expr_alloc_int_si(isl_ast_node_get_ctx(node), 1); +} + +/* Replace the then branch of the if node "node" by "child". + */ +__isl_give isl_ast_node *isl_ast_node_if_set_then( + __isl_take isl_ast_node *node, __isl_take isl_ast_node *child) +{ + node = isl_ast_node_cow(node); + if (!node || !child) + goto error; + if (node->type != isl_ast_node_if) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not an if node", goto error); + + isl_ast_node_free(node->u.i.then); + node->u.i.then = child; + + return node; +error: + isl_ast_node_free(node); + isl_ast_node_free(child); + return NULL; +} + +__isl_give isl_ast_node *isl_ast_node_if_get_then( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_if) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not an if node", return NULL); + return isl_ast_node_copy(node->u.i.then); +} + +isl_bool isl_ast_node_if_has_else( + __isl_keep isl_ast_node *node) +{ + if (!node) + return isl_bool_error; + if (node->type != isl_ast_node_if) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not an if node", return isl_bool_error); + return node->u.i.else_node != NULL; +} + +__isl_give isl_ast_node *isl_ast_node_if_get_else( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_if) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not an if node", return NULL); + return isl_ast_node_copy(node->u.i.else_node); +} + +__isl_give isl_ast_expr *isl_ast_node_if_get_cond( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_if) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a guard node", return NULL); + return isl_ast_expr_copy(node->u.i.guard); +} + +__isl_give isl_ast_node_list *isl_ast_node_block_get_children( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_block) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a block node", return NULL); + return isl_ast_node_list_copy(node->u.b.children); +} + +__isl_give isl_ast_expr *isl_ast_node_user_get_expr( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_user) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a user node", return NULL); + + return isl_ast_expr_copy(node->u.e.expr); +} + +/* Return the mark identifier of the mark node "node". + */ +__isl_give isl_id *isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_mark) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a mark node", return NULL); + + return isl_id_copy(node->u.m.mark); +} + +/* Return the node marked by mark node "node". + */ +__isl_give isl_ast_node *isl_ast_node_mark_get_node( + __isl_keep isl_ast_node *node) +{ + if (!node) + return NULL; + if (node->type != isl_ast_node_mark) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a mark node", return NULL); + + return isl_ast_node_copy(node->u.m.node); +} + +__isl_give isl_id *isl_ast_node_get_annotation(__isl_keep isl_ast_node *node) +{ + return node ? isl_id_copy(node->annotation) : NULL; +} + +/* Replace node->annotation by "annotation". + */ +__isl_give isl_ast_node *isl_ast_node_set_annotation( + __isl_take isl_ast_node *node, __isl_take isl_id *annotation) +{ + node = isl_ast_node_cow(node); + if (!node || !annotation) + goto error; + + isl_id_free(node->annotation); + node->annotation = annotation; + + return node; +error: + isl_id_free(annotation); + return isl_ast_node_free(node); +} + +/* Traverse the elements of "list" and all their descendants + * in depth first preorder. + * + * Return isl_stat_ok on success and isl_stat_error on failure. + */ +static isl_stat nodelist_foreach(__isl_keep isl_ast_node_list *list, + isl_bool (*fn)(__isl_keep isl_ast_node *node, void *user), void *user) +{ + int i; + + if (!list) + return isl_stat_error; + + for (i = 0; i < list->n; ++i) { + isl_stat ok; + isl_ast_node *node = list->p[i]; + + ok = isl_ast_node_foreach_descendant_top_down(node, fn, user); + if (ok < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Traverse the descendants of "node" (including the node itself) + * in depth first preorder. + * + * If "fn" returns isl_bool_error on any of the nodes, then the traversal + * is aborted. + * If "fn" returns isl_bool_false on any of the nodes, then the subtree rooted + * at that node is skipped. + * + * Return isl_stat_ok on success and isl_stat_error on failure. + */ +isl_stat isl_ast_node_foreach_descendant_top_down( + __isl_keep isl_ast_node *node, + isl_bool (*fn)(__isl_keep isl_ast_node *node, void *user), void *user) +{ + isl_bool more; + isl_stat ok; + + if (!node) + return isl_stat_error; + + more = fn(node, user); + if (more < 0) + return isl_stat_error; + if (!more) + return isl_stat_ok; + + switch (node->type) { + case isl_ast_node_for: + node = node->u.f.body; + return isl_ast_node_foreach_descendant_top_down(node, fn, user); + case isl_ast_node_if: + ok = isl_ast_node_foreach_descendant_top_down(node->u.i.then, + fn, user); + if (ok < 0) + return isl_stat_error; + if (!node->u.i.else_node) + return isl_stat_ok; + node = node->u.i.else_node; + return isl_ast_node_foreach_descendant_top_down(node, fn, user); + case isl_ast_node_block: + return nodelist_foreach(node->u.b.children, fn, user); + case isl_ast_node_mark: + node = node->u.m.node; + return isl_ast_node_foreach_descendant_top_down(node, fn, user); + case isl_ast_node_user: + break; + case isl_ast_node_error: + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Textual C representation of the various operators. + */ +static char *op_str[] = { + [isl_ast_op_and] = "&&", + [isl_ast_op_and_then] = "&&", + [isl_ast_op_or] = "||", + [isl_ast_op_or_else] = "||", + [isl_ast_op_max] = "max", + [isl_ast_op_min] = "min", + [isl_ast_op_minus] = "-", + [isl_ast_op_add] = "+", + [isl_ast_op_sub] = "-", + [isl_ast_op_mul] = "*", + [isl_ast_op_fdiv_q] = "floord", + [isl_ast_op_pdiv_q] = "/", + [isl_ast_op_pdiv_r] = "%", + [isl_ast_op_zdiv_r] = "%", + [isl_ast_op_div] = "/", + [isl_ast_op_eq] = "==", + [isl_ast_op_le] = "<=", + [isl_ast_op_ge] = ">=", + [isl_ast_op_lt] = "<", + [isl_ast_op_gt] = ">", + [isl_ast_op_member] = ".", + [isl_ast_op_address_of] = "&" +}; + +/* Precedence in C of the various operators. + * Based on http://en.wikipedia.org/wiki/Operators_in_C_and_C++ + * Lowest value means highest precedence. + */ +static int op_prec[] = { + [isl_ast_op_and] = 13, + [isl_ast_op_and_then] = 13, + [isl_ast_op_or] = 14, + [isl_ast_op_or_else] = 14, + [isl_ast_op_max] = 2, + [isl_ast_op_min] = 2, + [isl_ast_op_minus] = 3, + [isl_ast_op_add] = 6, + [isl_ast_op_sub] = 6, + [isl_ast_op_mul] = 5, + [isl_ast_op_div] = 5, + [isl_ast_op_fdiv_q] = 2, + [isl_ast_op_pdiv_q] = 5, + [isl_ast_op_pdiv_r] = 5, + [isl_ast_op_zdiv_r] = 5, + [isl_ast_op_cond] = 15, + [isl_ast_op_select] = 15, + [isl_ast_op_eq] = 9, + [isl_ast_op_le] = 8, + [isl_ast_op_ge] = 8, + [isl_ast_op_lt] = 8, + [isl_ast_op_gt] = 8, + [isl_ast_op_call] = 2, + [isl_ast_op_access] = 2, + [isl_ast_op_member] = 2, + [isl_ast_op_address_of] = 3 +}; + +/* Is the operator left-to-right associative? + */ +static int op_left[] = { + [isl_ast_op_and] = 1, + [isl_ast_op_and_then] = 1, + [isl_ast_op_or] = 1, + [isl_ast_op_or_else] = 1, + [isl_ast_op_max] = 1, + [isl_ast_op_min] = 1, + [isl_ast_op_minus] = 0, + [isl_ast_op_add] = 1, + [isl_ast_op_sub] = 1, + [isl_ast_op_mul] = 1, + [isl_ast_op_div] = 1, + [isl_ast_op_fdiv_q] = 1, + [isl_ast_op_pdiv_q] = 1, + [isl_ast_op_pdiv_r] = 1, + [isl_ast_op_zdiv_r] = 1, + [isl_ast_op_cond] = 0, + [isl_ast_op_select] = 0, + [isl_ast_op_eq] = 1, + [isl_ast_op_le] = 1, + [isl_ast_op_ge] = 1, + [isl_ast_op_lt] = 1, + [isl_ast_op_gt] = 1, + [isl_ast_op_call] = 1, + [isl_ast_op_access] = 1, + [isl_ast_op_member] = 1, + [isl_ast_op_address_of] = 0 +}; + +static int is_and(enum isl_ast_op_type op) +{ + return op == isl_ast_op_and || op == isl_ast_op_and_then; +} + +static int is_or(enum isl_ast_op_type op) +{ + return op == isl_ast_op_or || op == isl_ast_op_or_else; +} + +static int is_add_sub(enum isl_ast_op_type op) +{ + return op == isl_ast_op_add || op == isl_ast_op_sub; +} + +static int is_div_mod(enum isl_ast_op_type op) +{ + return op == isl_ast_op_div || + op == isl_ast_op_pdiv_r || + op == isl_ast_op_zdiv_r; +} + +/* Do we need/want parentheses around "expr" as a subexpression of + * an "op" operation? If "left" is set, then "expr" is the left-most + * operand. + * + * We only need parentheses if "expr" represents an operation. + * + * If op has a higher precedence than expr->u.op.op, then we need + * parentheses. + * If op and expr->u.op.op have the same precedence, but the operations + * are performed in an order that is different from the associativity, + * then we need parentheses. + * + * An and inside an or technically does not require parentheses, + * but some compilers complain about that, so we add them anyway. + * + * Computations such as "a / b * c" and "a % b + c" can be somewhat + * difficult to read, so we add parentheses for those as well. + */ +static int sub_expr_need_parens(enum isl_ast_op_type op, + __isl_keep isl_ast_expr *expr, int left) +{ + if (expr->type != isl_ast_expr_op) + return 0; + + if (op_prec[expr->u.op.op] > op_prec[op]) + return 1; + if (op_prec[expr->u.op.op] == op_prec[op] && left != op_left[op]) + return 1; + + if (is_or(op) && is_and(expr->u.op.op)) + return 1; + if (op == isl_ast_op_mul && expr->u.op.op != isl_ast_op_mul && + op_prec[expr->u.op.op] == op_prec[op]) + return 1; + if (is_add_sub(op) && is_div_mod(expr->u.op.op)) + return 1; + + return 0; +} + +/* Print "expr" as a subexpression of an "op" operation. + * If "left" is set, then "expr" is the left-most operand. + */ +static __isl_give isl_printer *print_sub_expr(__isl_take isl_printer *p, + enum isl_ast_op_type op, __isl_keep isl_ast_expr *expr, int left) +{ + int need_parens; + + need_parens = sub_expr_need_parens(op, expr, left); + + if (need_parens) + p = isl_printer_print_str(p, "("); + p = isl_printer_print_ast_expr(p, expr); + if (need_parens) + p = isl_printer_print_str(p, ")"); + return p; +} + +#define isl_ast_op_last isl_ast_op_address_of + +/* Data structure that holds the user-specified textual + * representations for the operators. + * The entries are either NULL or copies of strings. + * A NULL entry means that the default name should be used. + */ +struct isl_ast_op_names { + char *op_str[isl_ast_op_last + 1]; +}; + +/* Create an empty struct isl_ast_op_names. + */ +static void *create_names(isl_ctx *ctx) +{ + return isl_calloc_type(ctx, struct isl_ast_op_names); +} + +/* Free a struct isl_ast_op_names along with all memory + * owned by the struct. + */ +static void free_names(void *user) +{ + int i; + struct isl_ast_op_names *names = user; + + if (!user) + return; + + for (i = 0; i <= isl_ast_op_last; ++i) + free(names->op_str[i]); + free(user); +} + +/* Create an identifier that is used to store + * an isl_ast_op_names note. + */ +static __isl_give isl_id *names_id(isl_ctx *ctx) +{ + return isl_id_alloc(ctx, "isl_ast_op_type_names", NULL); +} + +/* Ensure that "p" has a note identified by "id". + * If there is no such note yet, then it is created by "note_create" and + * scheduled do be freed by "note_free". + */ +static __isl_give isl_printer *alloc_note(__isl_take isl_printer *p, + __isl_keep isl_id *id, void *(*note_create)(isl_ctx *), + void (*note_free)(void *)) +{ + isl_ctx *ctx; + isl_id *note_id; + isl_bool has_note; + void *note; + + has_note = isl_printer_has_note(p, id); + if (has_note < 0) + return isl_printer_free(p); + if (has_note) + return p; + + ctx = isl_printer_get_ctx(p); + note = note_create(ctx); + if (!note) + return isl_printer_free(p); + note_id = isl_id_alloc(ctx, NULL, note); + if (!note_id) + note_free(note); + else + note_id = isl_id_set_free_user(note_id, note_free); + + p = isl_printer_set_note(p, isl_id_copy(id), note_id); + + return p; +} + +/* Ensure that "p" has an isl_ast_op_names note identified by "id". + */ +static __isl_give isl_printer *alloc_names(__isl_take isl_printer *p, + __isl_keep isl_id *id) +{ + return alloc_note(p, id, &create_names, &free_names); +} + +/* Retrieve the note identified by "id" from "p". + * The note is assumed to exist. + */ +static void *get_note(__isl_keep isl_printer *p, __isl_keep isl_id *id) +{ + void *note; + + id = isl_printer_get_note(p, isl_id_copy(id)); + note = isl_id_get_user(id); + isl_id_free(id); + + return note; +} + +/* Use "name" to print operations of type "type" to "p". + * + * Store the name in an isl_ast_op_names note attached to "p", such that + * it can be retrieved by get_op_str. + */ +__isl_give isl_printer *isl_ast_op_type_set_print_name( + __isl_take isl_printer *p, enum isl_ast_op_type type, + __isl_keep const char *name) +{ + isl_id *id; + struct isl_ast_op_names *names; + + if (!p) + return NULL; + if (type > isl_ast_op_last) + isl_die(isl_printer_get_ctx(p), isl_error_invalid, + "invalid type", return isl_printer_free(p)); + + id = names_id(isl_printer_get_ctx(p)); + p = alloc_names(p, id); + names = get_note(p, id); + isl_id_free(id); + if (!names) + return isl_printer_free(p); + free(names->op_str[type]); + names->op_str[type] = strdup(name); + + return p; +} + +/* Return the textual representation of "type". + * + * If there is a user-specified name in an isl_ast_op_names note + * associated to "p", then return that. + * Otherwise, return the default name in op_str. + */ +static const char *get_op_str(__isl_keep isl_printer *p, + enum isl_ast_op_type type) +{ + isl_id *id; + isl_bool has_names; + struct isl_ast_op_names *names = NULL; + + id = names_id(isl_printer_get_ctx(p)); + has_names = isl_printer_has_note(p, id); + if (has_names >= 0 && has_names) + names = get_note(p, id); + isl_id_free(id); + if (names && names->op_str[type]) + return names->op_str[type]; + return op_str[type]; +} + +/* Print a min or max reduction "expr". + */ +static __isl_give isl_printer *print_min_max(__isl_take isl_printer *p, + __isl_keep isl_ast_expr *expr) +{ + int i = 0; + + for (i = 1; i < expr->u.op.n_arg; ++i) { + p = isl_printer_print_str(p, get_op_str(p, expr->u.op.op)); + p = isl_printer_print_str(p, "("); + } + p = isl_printer_print_ast_expr(p, expr->u.op.args[0]); + for (i = 1; i < expr->u.op.n_arg; ++i) { + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_ast_expr(p, expr->u.op.args[i]); + p = isl_printer_print_str(p, ")"); + } + + return p; +} + +/* Print a function call "expr". + * + * The first argument represents the function to be called. + */ +static __isl_give isl_printer *print_call(__isl_take isl_printer *p, + __isl_keep isl_ast_expr *expr) +{ + int i = 0; + + p = isl_printer_print_ast_expr(p, expr->u.op.args[0]); + p = isl_printer_print_str(p, "("); + for (i = 1; i < expr->u.op.n_arg; ++i) { + if (i != 1) + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_ast_expr(p, expr->u.op.args[i]); + } + p = isl_printer_print_str(p, ")"); + + return p; +} + +/* Print an array access "expr". + * + * The first argument represents the array being accessed. + */ +static __isl_give isl_printer *print_access(__isl_take isl_printer *p, + __isl_keep isl_ast_expr *expr) +{ + int i = 0; + + p = isl_printer_print_ast_expr(p, expr->u.op.args[0]); + for (i = 1; i < expr->u.op.n_arg; ++i) { + p = isl_printer_print_str(p, "["); + p = isl_printer_print_ast_expr(p, expr->u.op.args[i]); + p = isl_printer_print_str(p, "]"); + } + + return p; +} + +/* Print "expr" to "p". + * + * If we are printing in isl format, then we also print an indication + * of the size of the expression (if it was computed). + */ +__isl_give isl_printer *isl_printer_print_ast_expr(__isl_take isl_printer *p, + __isl_keep isl_ast_expr *expr) +{ + if (!p) + return NULL; + if (!expr) + return isl_printer_free(p); + + switch (expr->type) { + case isl_ast_expr_op: + if (expr->u.op.op == isl_ast_op_call) { + p = print_call(p, expr); + break; + } + if (expr->u.op.op == isl_ast_op_access) { + p = print_access(p, expr); + break; + } + if (expr->u.op.n_arg == 1) { + p = isl_printer_print_str(p, + get_op_str(p, expr->u.op.op)); + p = print_sub_expr(p, expr->u.op.op, + expr->u.op.args[0], 0); + break; + } + if (expr->u.op.op == isl_ast_op_fdiv_q) { + const char *name = get_op_str(p, isl_ast_op_fdiv_q); + p = isl_printer_print_str(p, name); + p = isl_printer_print_str(p, "("); + p = isl_printer_print_ast_expr(p, expr->u.op.args[0]); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_ast_expr(p, expr->u.op.args[1]); + p = isl_printer_print_str(p, ")"); + break; + } + if (expr->u.op.op == isl_ast_op_max || + expr->u.op.op == isl_ast_op_min) { + p = print_min_max(p, expr); + break; + } + if (expr->u.op.op == isl_ast_op_cond || + expr->u.op.op == isl_ast_op_select) { + p = isl_printer_print_ast_expr(p, expr->u.op.args[0]); + p = isl_printer_print_str(p, " ? "); + p = isl_printer_print_ast_expr(p, expr->u.op.args[1]); + p = isl_printer_print_str(p, " : "); + p = isl_printer_print_ast_expr(p, expr->u.op.args[2]); + break; + } + if (expr->u.op.n_arg != 2) + isl_die(isl_printer_get_ctx(p), isl_error_internal, + "operation should have two arguments", + goto error); + p = print_sub_expr(p, expr->u.op.op, expr->u.op.args[0], 1); + if (expr->u.op.op != isl_ast_op_member) + p = isl_printer_print_str(p, " "); + p = isl_printer_print_str(p, get_op_str(p, expr->u.op.op)); + if (expr->u.op.op != isl_ast_op_member) + p = isl_printer_print_str(p, " "); + p = print_sub_expr(p, expr->u.op.op, expr->u.op.args[1], 0); + break; + case isl_ast_expr_id: + p = isl_printer_print_str(p, isl_id_get_name(expr->u.id)); + break; + case isl_ast_expr_int: + p = isl_printer_print_val(p, expr->u.v); + break; + case isl_ast_expr_error: + break; + } + + return p; +error: + isl_printer_free(p); + return NULL; +} + +/* Print "node" to "p" in "isl format". + */ +static __isl_give isl_printer *print_ast_node_isl(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node) +{ + p = isl_printer_print_str(p, "("); + switch (node->type) { + case isl_ast_node_for: + if (node->u.f.degenerate) { + p = isl_printer_print_ast_expr(p, node->u.f.init); + } else { + p = isl_printer_print_str(p, "init: "); + p = isl_printer_print_ast_expr(p, node->u.f.init); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "cond: "); + p = isl_printer_print_ast_expr(p, node->u.f.cond); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "inc: "); + p = isl_printer_print_ast_expr(p, node->u.f.inc); + } + if (node->u.f.body) { + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "body: "); + p = isl_printer_print_ast_node(p, node->u.f.body); + } + break; + case isl_ast_node_mark: + p = isl_printer_print_str(p, "mark: "); + p = isl_printer_print_id(p, node->u.m.mark); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "node: "); + p = isl_printer_print_ast_node(p, node->u.m.node); + case isl_ast_node_user: + p = isl_printer_print_ast_expr(p, node->u.e.expr); + break; + case isl_ast_node_if: + p = isl_printer_print_str(p, "guard: "); + p = isl_printer_print_ast_expr(p, node->u.i.guard); + if (node->u.i.then) { + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "then: "); + p = isl_printer_print_ast_node(p, node->u.i.then); + } + if (node->u.i.else_node) { + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "else: "); + p = isl_printer_print_ast_node(p, node->u.i.else_node); + } + break; + case isl_ast_node_block: + p = isl_printer_print_ast_node_list(p, node->u.b.children); + break; + case isl_ast_node_error: + break; + } + p = isl_printer_print_str(p, ")"); + return p; +} + +/* Do we need to print a block around the body "node" of a for or if node? + * + * If the node is a block, then we need to print a block. + * Also if the node is a degenerate for then we will print it as + * an assignment followed by the body of the for loop, so we need a block + * as well. + * If the node is an if node with an else, then we print a block + * to avoid spurious dangling else warnings emitted by some compilers. + * If the node is a mark, then in principle, we would have to check + * the child of the mark node. However, even if the child would not + * require us to print a block, for readability it is probably best + * to print a block anyway. + * If the ast_always_print_block option has been set, then we print a block. + */ +static int need_block(__isl_keep isl_ast_node *node) +{ + isl_ctx *ctx; + + if (node->type == isl_ast_node_block) + return 1; + if (node->type == isl_ast_node_for && node->u.f.degenerate) + return 1; + if (node->type == isl_ast_node_if && node->u.i.else_node) + return 1; + if (node->type == isl_ast_node_mark) + return 1; + + ctx = isl_ast_node_get_ctx(node); + return isl_options_get_ast_always_print_block(ctx); +} + +static __isl_give isl_printer *print_ast_node_c(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node, + __isl_keep isl_ast_print_options *options, int in_block, int in_list); +static __isl_give isl_printer *print_if_c(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node, + __isl_keep isl_ast_print_options *options, int new_line, + int force_block); + +/* Print the body "node" of a for or if node. + * If "else_node" is set, then it is printed as well. + * If "force_block" is set, then print out the body as a block. + * + * We first check if we need to print out a block. + * We always print out a block if there is an else node to make + * sure that the else node is matched to the correct if node. + * For consistency, the corresponding else node is also printed as a block. + * + * If the else node is itself an if, then we print it as + * + * } else if (..) { + * } + * + * Otherwise the else node is printed as + * + * } else { + * node + * } + */ +static __isl_give isl_printer *print_body_c(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node, __isl_keep isl_ast_node *else_node, + __isl_keep isl_ast_print_options *options, int force_block) +{ + if (!node) + return isl_printer_free(p); + + if (!force_block && !else_node && !need_block(node)) { + p = isl_printer_end_line(p); + p = isl_printer_indent(p, 2); + p = isl_ast_node_print(node, p, + isl_ast_print_options_copy(options)); + p = isl_printer_indent(p, -2); + return p; + } + + p = isl_printer_print_str(p, " {"); + p = isl_printer_end_line(p); + p = isl_printer_indent(p, 2); + p = print_ast_node_c(p, node, options, 1, 0); + p = isl_printer_indent(p, -2); + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "}"); + if (else_node) { + if (else_node->type == isl_ast_node_if) { + p = isl_printer_print_str(p, " else "); + p = print_if_c(p, else_node, options, 0, 1); + } else { + p = isl_printer_print_str(p, " else"); + p = print_body_c(p, else_node, NULL, options, 1); + } + } else + p = isl_printer_end_line(p); + + return p; +} + +/* Print the start of a compound statement. + */ +static __isl_give isl_printer *start_block(__isl_take isl_printer *p) +{ + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "{"); + p = isl_printer_end_line(p); + p = isl_printer_indent(p, 2); + + return p; +} + +/* Print the end of a compound statement. + */ +static __isl_give isl_printer *end_block(__isl_take isl_printer *p) +{ + p = isl_printer_indent(p, -2); + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "}"); + p = isl_printer_end_line(p); + + return p; +} + +/* Print the for node "node". + * + * If the for node is degenerate, it is printed as + * + * type iterator = init; + * body + * + * Otherwise, it is printed as + * + * for (type iterator = init; cond; iterator += inc) + * body + * + * "in_block" is set if we are currently inside a block. + * "in_list" is set if the current node is not alone in the block. + * If we are not in a block or if the current not is not alone in the block + * then we print a block around a degenerate for loop such that the variable + * declaration will not conflict with any potential other declaration + * of the same variable. + */ +static __isl_give isl_printer *print_for_c(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node, + __isl_keep isl_ast_print_options *options, int in_block, int in_list) +{ + isl_id *id; + const char *name; + const char *type; + + type = isl_options_get_ast_iterator_type(isl_printer_get_ctx(p)); + if (!node->u.f.degenerate) { + id = isl_ast_expr_get_id(node->u.f.iterator); + name = isl_id_get_name(id); + isl_id_free(id); + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "for ("); + p = isl_printer_print_str(p, type); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_str(p, name); + p = isl_printer_print_str(p, " = "); + p = isl_printer_print_ast_expr(p, node->u.f.init); + p = isl_printer_print_str(p, "; "); + p = isl_printer_print_ast_expr(p, node->u.f.cond); + p = isl_printer_print_str(p, "; "); + p = isl_printer_print_str(p, name); + p = isl_printer_print_str(p, " += "); + p = isl_printer_print_ast_expr(p, node->u.f.inc); + p = isl_printer_print_str(p, ")"); + p = print_body_c(p, node->u.f.body, NULL, options, 0); + } else { + id = isl_ast_expr_get_id(node->u.f.iterator); + name = isl_id_get_name(id); + isl_id_free(id); + if (!in_block || in_list) + p = start_block(p); + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, type); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_str(p, name); + p = isl_printer_print_str(p, " = "); + p = isl_printer_print_ast_expr(p, node->u.f.init); + p = isl_printer_print_str(p, ";"); + p = isl_printer_end_line(p); + p = print_ast_node_c(p, node->u.f.body, options, 1, 0); + if (!in_block || in_list) + p = end_block(p); + } + + return p; +} + +/* Print the if node "node". + * If "new_line" is set then the if node should be printed on a new line. + * If "force_block" is set, then print out the body as a block. + */ +static __isl_give isl_printer *print_if_c(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node, + __isl_keep isl_ast_print_options *options, int new_line, + int force_block) +{ + if (new_line) + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "if ("); + p = isl_printer_print_ast_expr(p, node->u.i.guard); + p = isl_printer_print_str(p, ")"); + p = print_body_c(p, node->u.i.then, node->u.i.else_node, options, + force_block); + + return p; +} + +/* Print the "node" to "p". + * + * "in_block" is set if we are currently inside a block. + * If so, we do not print a block around the children of a block node. + * We do this to avoid an extra block around the body of a degenerate + * for node. + * + * "in_list" is set if the current node is not alone in the block. + */ +static __isl_give isl_printer *print_ast_node_c(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node, + __isl_keep isl_ast_print_options *options, int in_block, int in_list) +{ + switch (node->type) { + case isl_ast_node_for: + if (options->print_for) + return options->print_for(p, + isl_ast_print_options_copy(options), + node, options->print_for_user); + p = print_for_c(p, node, options, in_block, in_list); + break; + case isl_ast_node_if: + p = print_if_c(p, node, options, 1, 0); + break; + case isl_ast_node_block: + if (!in_block) + p = start_block(p); + p = isl_ast_node_list_print(node->u.b.children, p, options); + if (!in_block) + p = end_block(p); + break; + case isl_ast_node_mark: + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "// "); + p = isl_printer_print_str(p, isl_id_get_name(node->u.m.mark)); + p = isl_printer_end_line(p); + p = print_ast_node_c(p, node->u.m.node, options, 0, in_list); + break; + case isl_ast_node_user: + if (options->print_user) + return options->print_user(p, + isl_ast_print_options_copy(options), + node, options->print_user_user); + p = isl_printer_start_line(p); + p = isl_printer_print_ast_expr(p, node->u.e.expr); + p = isl_printer_print_str(p, ";"); + p = isl_printer_end_line(p); + break; + case isl_ast_node_error: + break; + } + return p; +} + +/* Print the for node "node" to "p". + */ +__isl_give isl_printer *isl_ast_node_for_print(__isl_keep isl_ast_node *node, + __isl_take isl_printer *p, __isl_take isl_ast_print_options *options) +{ + if (!node || !options) + goto error; + if (node->type != isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not a for node", goto error); + p = print_for_c(p, node, options, 0, 0); + isl_ast_print_options_free(options); + return p; +error: + isl_ast_print_options_free(options); + isl_printer_free(p); + return NULL; +} + +/* Print the if node "node" to "p". + */ +__isl_give isl_printer *isl_ast_node_if_print(__isl_keep isl_ast_node *node, + __isl_take isl_printer *p, __isl_take isl_ast_print_options *options) +{ + if (!node || !options) + goto error; + if (node->type != isl_ast_node_if) + isl_die(isl_ast_node_get_ctx(node), isl_error_invalid, + "not an if node", goto error); + p = print_if_c(p, node, options, 1, 0); + isl_ast_print_options_free(options); + return p; +error: + isl_ast_print_options_free(options); + isl_printer_free(p); + return NULL; +} + +/* Print "node" to "p". + */ +__isl_give isl_printer *isl_ast_node_print(__isl_keep isl_ast_node *node, + __isl_take isl_printer *p, __isl_take isl_ast_print_options *options) +{ + if (!options || !node) + goto error; + p = print_ast_node_c(p, node, options, 0, 0); + isl_ast_print_options_free(options); + return p; +error: + isl_ast_print_options_free(options); + isl_printer_free(p); + return NULL; +} + +/* Print "node" to "p". + */ +__isl_give isl_printer *isl_printer_print_ast_node(__isl_take isl_printer *p, + __isl_keep isl_ast_node *node) +{ + int format; + isl_ast_print_options *options; + + if (!p) + return NULL; + + format = isl_printer_get_output_format(p); + switch (format) { + case ISL_FORMAT_ISL: + p = print_ast_node_isl(p, node); + break; + case ISL_FORMAT_C: + options = isl_ast_print_options_alloc(isl_printer_get_ctx(p)); + p = isl_ast_node_print(node, p, options); + break; + default: + isl_die(isl_printer_get_ctx(p), isl_error_unsupported, + "output format not supported for ast_node", + return isl_printer_free(p)); + } + + return p; +} + +/* Print the list of nodes "list" to "p". + */ +__isl_give isl_printer *isl_ast_node_list_print( + __isl_keep isl_ast_node_list *list, __isl_take isl_printer *p, + __isl_keep isl_ast_print_options *options) +{ + int i; + + if (!p || !list || !options) + return isl_printer_free(p); + + for (i = 0; i < list->n; ++i) + p = print_ast_node_c(p, list->p[i], options, 1, 1); + + return p; +} + +#define ISL_AST_MACRO_FLOORD (1 << 0) +#define ISL_AST_MACRO_MIN (1 << 1) +#define ISL_AST_MACRO_MAX (1 << 2) +#define ISL_AST_MACRO_ALL (ISL_AST_MACRO_FLOORD | \ + ISL_AST_MACRO_MIN | \ + ISL_AST_MACRO_MAX) + +/* If "expr" contains an isl_ast_op_min, isl_ast_op_max or isl_ast_op_fdiv_q + * then set the corresponding bit in "macros". + */ +static int ast_expr_required_macros(__isl_keep isl_ast_expr *expr, int macros) +{ + int i; + + if (macros == ISL_AST_MACRO_ALL) + return macros; + + if (expr->type != isl_ast_expr_op) + return macros; + + if (expr->u.op.op == isl_ast_op_min) + macros |= ISL_AST_MACRO_MIN; + if (expr->u.op.op == isl_ast_op_max) + macros |= ISL_AST_MACRO_MAX; + if (expr->u.op.op == isl_ast_op_fdiv_q) + macros |= ISL_AST_MACRO_FLOORD; + + for (i = 0; i < expr->u.op.n_arg; ++i) + macros = ast_expr_required_macros(expr->u.op.args[i], macros); + + return macros; +} + +static int ast_node_list_required_macros(__isl_keep isl_ast_node_list *list, + int macros); + +/* If "node" contains an isl_ast_op_min, isl_ast_op_max or isl_ast_op_fdiv_q + * then set the corresponding bit in "macros". + */ +static int ast_node_required_macros(__isl_keep isl_ast_node *node, int macros) +{ + if (macros == ISL_AST_MACRO_ALL) + return macros; + + switch (node->type) { + case isl_ast_node_for: + macros = ast_expr_required_macros(node->u.f.init, macros); + if (!node->u.f.degenerate) { + macros = ast_expr_required_macros(node->u.f.cond, + macros); + macros = ast_expr_required_macros(node->u.f.inc, + macros); + } + macros = ast_node_required_macros(node->u.f.body, macros); + break; + case isl_ast_node_if: + macros = ast_expr_required_macros(node->u.i.guard, macros); + macros = ast_node_required_macros(node->u.i.then, macros); + if (node->u.i.else_node) + macros = ast_node_required_macros(node->u.i.else_node, + macros); + break; + case isl_ast_node_block: + macros = ast_node_list_required_macros(node->u.b.children, + macros); + break; + case isl_ast_node_mark: + macros = ast_node_required_macros(node->u.m.node, macros); + break; + case isl_ast_node_user: + macros = ast_expr_required_macros(node->u.e.expr, macros); + break; + case isl_ast_node_error: + break; + } + + return macros; +} + +/* If "list" contains an isl_ast_op_min, isl_ast_op_max or isl_ast_op_fdiv_q + * then set the corresponding bit in "macros". + */ +static int ast_node_list_required_macros(__isl_keep isl_ast_node_list *list, + int macros) +{ + int i; + + for (i = 0; i < list->n; ++i) + macros = ast_node_required_macros(list->p[i], macros); + + return macros; +} + +/* Data structure for keeping track of whether a macro definition + * for a given type has already been printed. + * The value is zero if no definition has been printed and non-zero otherwise. + */ +struct isl_ast_op_printed { + char printed[isl_ast_op_last + 1]; +}; + +/* Create an empty struct isl_ast_op_printed. + */ +static void *create_printed(isl_ctx *ctx) +{ + return isl_calloc_type(ctx, struct isl_ast_op_printed); +} + +/* Free a struct isl_ast_op_printed. + */ +static void free_printed(void *user) +{ + free(user); +} + +/* Ensure that "p" has an isl_ast_op_printed note identified by "id". + */ +static __isl_give isl_printer *alloc_printed(__isl_take isl_printer *p, + __isl_keep isl_id *id) +{ + return alloc_note(p, id, &create_printed, &free_printed); +} + +/* Create an identifier that is used to store + * an isl_ast_op_printed note. + */ +static __isl_give isl_id *printed_id(isl_ctx *ctx) +{ + return isl_id_alloc(ctx, "isl_ast_op_type_printed", NULL); +} + +/* Did the user specify that a macro definition should only be + * printed once and has a macro definition for "type" already + * been printed to "p"? + * If definitions should only be printed once, but a definition + * for "p" has not yet been printed, then mark it as having been + * printed so that it will not printed again. + * The actual printing is taken care of by the caller. + */ +static isl_bool already_printed_once(__isl_keep isl_printer *p, + enum isl_ast_op_type type) +{ + isl_ctx *ctx; + isl_id *id; + struct isl_ast_op_printed *printed; + + if (!p) + return isl_bool_error; + + ctx = isl_printer_get_ctx(p); + if (!isl_options_get_ast_print_macro_once(ctx)) + return isl_bool_false; + + if (type > isl_ast_op_last) + isl_die(isl_printer_get_ctx(p), isl_error_invalid, + "invalid type", return isl_bool_error); + + id = printed_id(isl_printer_get_ctx(p)); + p = alloc_printed(p, id); + printed = get_note(p, id); + isl_id_free(id); + if (!printed) + return isl_bool_error; + + if (printed->printed[type]) + return isl_bool_true; + + printed->printed[type] = 1; + return isl_bool_false; +} + +/* Print a macro definition for the operator "type". + * + * If the user has specified that a macro definition should + * only be printed once to any given printer and if the macro definition + * has already been printed to "p", then do not print the definition. + */ +__isl_give isl_printer *isl_ast_op_type_print_macro( + enum isl_ast_op_type type, __isl_take isl_printer *p) +{ + isl_bool skip; + + skip = already_printed_once(p, type); + if (skip < 0) + return isl_printer_free(p); + if (skip) + return p; + + switch (type) { + case isl_ast_op_min: + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "#define "); + p = isl_printer_print_str(p, get_op_str(p, type)); + p = isl_printer_print_str(p, + "(x,y) ((x) < (y) ? (x) : (y))"); + p = isl_printer_end_line(p); + break; + case isl_ast_op_max: + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "#define "); + p = isl_printer_print_str(p, get_op_str(p, type)); + p = isl_printer_print_str(p, + "(x,y) ((x) > (y) ? (x) : (y))"); + p = isl_printer_end_line(p); + break; + case isl_ast_op_fdiv_q: + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "#define "); + p = isl_printer_print_str(p, get_op_str(p, type)); + p = isl_printer_print_str(p, + "(n,d) " + "(((n)<0) ? -((-(n)+(d)-1)/(d)) : (n)/(d))"); + p = isl_printer_end_line(p); + break; + default: + break; + } + + return p; +} + +/* Call "fn" for each type of operation represented in the "macros" + * bit vector. + */ +static isl_stat foreach_ast_op_type(int macros, + isl_stat (*fn)(enum isl_ast_op_type type, void *user), void *user) +{ + if (macros & ISL_AST_MACRO_MIN && fn(isl_ast_op_min, user) < 0) + return isl_stat_error; + if (macros & ISL_AST_MACRO_MAX && fn(isl_ast_op_max, user) < 0) + return isl_stat_error; + if (macros & ISL_AST_MACRO_FLOORD && fn(isl_ast_op_fdiv_q, user) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Call "fn" for each type of operation that appears in "expr" + * and that requires a macro definition. + */ +isl_stat isl_ast_expr_foreach_ast_op_type(__isl_keep isl_ast_expr *expr, + isl_stat (*fn)(enum isl_ast_op_type type, void *user), void *user) +{ + int macros; + + if (!expr) + return isl_stat_error; + + macros = ast_expr_required_macros(expr, 0); + return foreach_ast_op_type(macros, fn, user); +} + +/* Call "fn" for each type of operation that appears in "node" + * and that requires a macro definition. + */ +isl_stat isl_ast_node_foreach_ast_op_type(__isl_keep isl_ast_node *node, + isl_stat (*fn)(enum isl_ast_op_type type, void *user), void *user) +{ + int macros; + + if (!node) + return isl_stat_error; + + macros = ast_node_required_macros(node, 0); + return foreach_ast_op_type(macros, fn, user); +} + +static isl_stat ast_op_type_print_macro(enum isl_ast_op_type type, void *user) +{ + isl_printer **p = user; + + *p = isl_ast_op_type_print_macro(type, *p); + + return isl_stat_ok; +} + +/* Print macro definitions for all the macros used in the result + * of printing "expr". + */ +__isl_give isl_printer *isl_ast_expr_print_macros( + __isl_keep isl_ast_expr *expr, __isl_take isl_printer *p) +{ + if (isl_ast_expr_foreach_ast_op_type(expr, + &ast_op_type_print_macro, &p) < 0) + return isl_printer_free(p); + return p; +} + +/* Print macro definitions for all the macros used in the result + * of printing "node". + */ +__isl_give isl_printer *isl_ast_node_print_macros( + __isl_keep isl_ast_node *node, __isl_take isl_printer *p) +{ + if (isl_ast_node_foreach_ast_op_type(node, + &ast_op_type_print_macro, &p) < 0) + return isl_printer_free(p); + return p; +} Index: lib/Analysis/isl/isl_ast_build.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_build.c @@ -0,0 +1,2565 @@ +/* + * Copyright 2012-2013 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Construct a map that isolates the current dimension. + * + * Essentially, the current dimension of "set" is moved to the single output + * dimension in the result, with the current dimension in the domain replaced + * by an unconstrained variable. + */ +__isl_give isl_map *isl_ast_build_map_to_iterator( + __isl_keep isl_ast_build *build, __isl_take isl_set *set) +{ + isl_map *map; + + map = isl_map_from_domain(set); + map = isl_map_add_dims(map, isl_dim_out, 1); + + if (!build) + return isl_map_free(map); + + map = isl_map_equate(map, isl_dim_in, build->depth, isl_dim_out, 0); + map = isl_map_eliminate(map, isl_dim_in, build->depth, 1); + + return map; +} + +/* Initialize the information derived during the AST generation to default + * values for a schedule domain in "space". + * + * We also check that the remaining fields are not NULL so that + * the calling functions don't have to perform this test. + */ +static __isl_give isl_ast_build *isl_ast_build_init_derived( + __isl_take isl_ast_build *build, __isl_take isl_space *space) +{ + isl_ctx *ctx; + isl_vec *strides; + + build = isl_ast_build_cow(build); + if (!build || !build->domain) + goto error; + + ctx = isl_ast_build_get_ctx(build); + strides = isl_vec_alloc(ctx, isl_space_dim(space, isl_dim_set)); + strides = isl_vec_set_si(strides, 1); + + isl_vec_free(build->strides); + build->strides = strides; + + space = isl_space_map_from_set(space); + isl_multi_aff_free(build->offsets); + build->offsets = isl_multi_aff_zero(isl_space_copy(space)); + isl_multi_aff_free(build->values); + build->values = isl_multi_aff_identity(isl_space_copy(space)); + isl_multi_aff_free(build->internal2input); + build->internal2input = isl_multi_aff_identity(space); + + if (!build->iterators || !build->domain || !build->generated || + !build->pending || !build->values || !build->internal2input || + !build->strides || !build->offsets || !build->options) + return isl_ast_build_free(build); + + return build; +error: + isl_space_free(space); + return isl_ast_build_free(build); +} + +/* Return an isl_id called "c%d", with "%d" set to "i". + * If an isl_id with such a name already appears among the parameters + * in build->domain, then adjust the name to "c%d_%d". + */ +static __isl_give isl_id *generate_name(isl_ctx *ctx, int i, + __isl_keep isl_ast_build *build) +{ + int j; + char name[16]; + isl_set *dom = build->domain; + + snprintf(name, sizeof(name), "c%d", i); + j = 0; + while (isl_set_find_dim_by_name(dom, isl_dim_param, name) >= 0) + snprintf(name, sizeof(name), "c%d_%d", i, j++); + return isl_id_alloc(ctx, name, NULL); +} + +/* Create an isl_ast_build with "set" as domain. + * + * The input set is usually a parameter domain, but we currently allow it to + * be any kind of set. We set the domain of the returned isl_ast_build + * to "set" and initialize all the other fields to default values. + */ +__isl_give isl_ast_build *isl_ast_build_from_context(__isl_take isl_set *set) +{ + int i, n; + isl_ctx *ctx; + isl_space *space; + isl_ast_build *build; + + set = isl_set_compute_divs(set); + if (!set) + return NULL; + + ctx = isl_set_get_ctx(set); + + build = isl_calloc_type(ctx, isl_ast_build); + if (!build) + goto error; + + build->ref = 1; + build->domain = set; + build->generated = isl_set_copy(build->domain); + build->pending = isl_set_universe(isl_set_get_space(build->domain)); + build->options = isl_union_map_empty(isl_space_params_alloc(ctx, 0)); + n = isl_set_dim(set, isl_dim_set); + build->depth = n; + build->iterators = isl_id_list_alloc(ctx, n); + for (i = 0; i < n; ++i) { + isl_id *id; + if (isl_set_has_dim_id(set, isl_dim_set, i)) + id = isl_set_get_dim_id(set, isl_dim_set, i); + else + id = generate_name(ctx, i, build); + build->iterators = isl_id_list_add(build->iterators, id); + } + space = isl_set_get_space(set); + if (isl_space_is_params(space)) + space = isl_space_set_from_params(space); + + return isl_ast_build_init_derived(build, space); +error: + isl_set_free(set); + return NULL; +} + +/* Create an isl_ast_build with a universe (parametric) context. + */ +__isl_give isl_ast_build *isl_ast_build_alloc(isl_ctx *ctx) +{ + isl_space *space; + isl_set *context; + + space = isl_space_params_alloc(ctx, 0); + context = isl_set_universe(space); + + return isl_ast_build_from_context(context); +} + +__isl_give isl_ast_build *isl_ast_build_copy(__isl_keep isl_ast_build *build) +{ + if (!build) + return NULL; + + build->ref++; + return build; +} + +__isl_give isl_ast_build *isl_ast_build_dup(__isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_ast_build *dup; + + if (!build) + return NULL; + + ctx = isl_ast_build_get_ctx(build); + dup = isl_calloc_type(ctx, isl_ast_build); + if (!dup) + return NULL; + + dup->ref = 1; + dup->outer_pos = build->outer_pos; + dup->depth = build->depth; + dup->iterators = isl_id_list_copy(build->iterators); + dup->domain = isl_set_copy(build->domain); + dup->generated = isl_set_copy(build->generated); + dup->pending = isl_set_copy(build->pending); + dup->values = isl_multi_aff_copy(build->values); + dup->internal2input = isl_multi_aff_copy(build->internal2input); + dup->value = isl_pw_aff_copy(build->value); + dup->strides = isl_vec_copy(build->strides); + dup->offsets = isl_multi_aff_copy(build->offsets); + dup->executed = isl_union_map_copy(build->executed); + dup->single_valued = build->single_valued; + dup->options = isl_union_map_copy(build->options); + dup->at_each_domain = build->at_each_domain; + dup->at_each_domain_user = build->at_each_domain_user; + dup->before_each_for = build->before_each_for; + dup->before_each_for_user = build->before_each_for_user; + dup->after_each_for = build->after_each_for; + dup->after_each_for_user = build->after_each_for_user; + dup->before_each_mark = build->before_each_mark; + dup->before_each_mark_user = build->before_each_mark_user; + dup->after_each_mark = build->after_each_mark; + dup->after_each_mark_user = build->after_each_mark_user; + dup->create_leaf = build->create_leaf; + dup->create_leaf_user = build->create_leaf_user; + dup->node = isl_schedule_node_copy(build->node); + if (build->loop_type) { + int i; + + dup->n = build->n; + dup->loop_type = isl_alloc_array(ctx, + enum isl_ast_loop_type, dup->n); + if (dup->n && !dup->loop_type) + return isl_ast_build_free(dup); + for (i = 0; i < dup->n; ++i) + dup->loop_type[i] = build->loop_type[i]; + } + + if (!dup->iterators || !dup->domain || !dup->generated || + !dup->pending || !dup->values || + !dup->strides || !dup->offsets || !dup->options || + (build->internal2input && !dup->internal2input) || + (build->executed && !dup->executed) || + (build->value && !dup->value) || + (build->node && !dup->node)) + return isl_ast_build_free(dup); + + return dup; +} + +/* Align the parameters of "build" to those of "model", introducing + * additional parameters if needed. + */ +__isl_give isl_ast_build *isl_ast_build_align_params( + __isl_take isl_ast_build *build, __isl_take isl_space *model) +{ + build = isl_ast_build_cow(build); + if (!build) + goto error; + + build->domain = isl_set_align_params(build->domain, + isl_space_copy(model)); + build->generated = isl_set_align_params(build->generated, + isl_space_copy(model)); + build->pending = isl_set_align_params(build->pending, + isl_space_copy(model)); + build->values = isl_multi_aff_align_params(build->values, + isl_space_copy(model)); + build->offsets = isl_multi_aff_align_params(build->offsets, + isl_space_copy(model)); + build->options = isl_union_map_align_params(build->options, + isl_space_copy(model)); + if (build->internal2input) { + build->internal2input = + isl_multi_aff_align_params(build->internal2input, + model); + if (!build->internal2input) + return isl_ast_build_free(build); + } else { + isl_space_free(model); + } + + if (!build->domain || !build->values || !build->offsets || + !build->options) + return isl_ast_build_free(build); + + return build; +error: + isl_space_free(model); + return NULL; +} + +__isl_give isl_ast_build *isl_ast_build_cow(__isl_take isl_ast_build *build) +{ + if (!build) + return NULL; + + if (build->ref == 1) + return build; + build->ref--; + return isl_ast_build_dup(build); +} + +__isl_null isl_ast_build *isl_ast_build_free( + __isl_take isl_ast_build *build) +{ + if (!build) + return NULL; + + if (--build->ref > 0) + return NULL; + + isl_id_list_free(build->iterators); + isl_set_free(build->domain); + isl_set_free(build->generated); + isl_set_free(build->pending); + isl_multi_aff_free(build->values); + isl_multi_aff_free(build->internal2input); + isl_pw_aff_free(build->value); + isl_vec_free(build->strides); + isl_multi_aff_free(build->offsets); + isl_multi_aff_free(build->schedule_map); + isl_union_map_free(build->executed); + isl_union_map_free(build->options); + isl_schedule_node_free(build->node); + free(build->loop_type); + isl_set_free(build->isolated); + + free(build); + + return NULL; +} + +isl_ctx *isl_ast_build_get_ctx(__isl_keep isl_ast_build *build) +{ + return build ? isl_set_get_ctx(build->domain) : NULL; +} + +/* Replace build->options by "options". + */ +__isl_give isl_ast_build *isl_ast_build_set_options( + __isl_take isl_ast_build *build, __isl_take isl_union_map *options) +{ + build = isl_ast_build_cow(build); + + if (!build || !options) + goto error; + + isl_union_map_free(build->options); + build->options = options; + + return build; +error: + isl_union_map_free(options); + return isl_ast_build_free(build); +} + +/* Set the iterators for the next code generation. + * + * If we still have some iterators left from the previous code generation + * (if any) or if iterators have already been set by a previous + * call to this function, then we remove them first. + */ +__isl_give isl_ast_build *isl_ast_build_set_iterators( + __isl_take isl_ast_build *build, __isl_take isl_id_list *iterators) +{ + int dim, n_it; + + build = isl_ast_build_cow(build); + if (!build) + goto error; + + dim = isl_set_dim(build->domain, isl_dim_set); + n_it = isl_id_list_n_id(build->iterators); + if (n_it < dim) + isl_die(isl_ast_build_get_ctx(build), isl_error_internal, + "isl_ast_build in inconsistent state", goto error); + if (n_it > dim) + build->iterators = isl_id_list_drop(build->iterators, + dim, n_it - dim); + build->iterators = isl_id_list_concat(build->iterators, iterators); + if (!build->iterators) + return isl_ast_build_free(build); + + return build; +error: + isl_id_list_free(iterators); + return isl_ast_build_free(build); +} + +/* Set the "at_each_domain" callback of "build" to "fn". + */ +__isl_give isl_ast_build *isl_ast_build_set_at_each_domain( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user), void *user) +{ + build = isl_ast_build_cow(build); + + if (!build) + return NULL; + + build->at_each_domain = fn; + build->at_each_domain_user = user; + + return build; +} + +/* Set the "before_each_for" callback of "build" to "fn". + */ +__isl_give isl_ast_build *isl_ast_build_set_before_each_for( + __isl_take isl_ast_build *build, + __isl_give isl_id *(*fn)(__isl_keep isl_ast_build *build, + void *user), void *user) +{ + build = isl_ast_build_cow(build); + + if (!build) + return NULL; + + build->before_each_for = fn; + build->before_each_for_user = user; + + return build; +} + +/* Set the "after_each_for" callback of "build" to "fn". + */ +__isl_give isl_ast_build *isl_ast_build_set_after_each_for( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user), void *user) +{ + build = isl_ast_build_cow(build); + + if (!build) + return NULL; + + build->after_each_for = fn; + build->after_each_for_user = user; + + return build; +} + +/* Set the "before_each_mark" callback of "build" to "fn". + */ +__isl_give isl_ast_build *isl_ast_build_set_before_each_mark( + __isl_take isl_ast_build *build, + isl_stat (*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build, + void *user), void *user) +{ + build = isl_ast_build_cow(build); + + if (!build) + return NULL; + + build->before_each_mark = fn; + build->before_each_mark_user = user; + + return build; +} + +/* Set the "after_each_mark" callback of "build" to "fn". + */ +__isl_give isl_ast_build *isl_ast_build_set_after_each_mark( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user), void *user) +{ + build = isl_ast_build_cow(build); + + if (!build) + return NULL; + + build->after_each_mark = fn; + build->after_each_mark_user = user; + + return build; +} + +/* Set the "create_leaf" callback of "build" to "fn". + */ +__isl_give isl_ast_build *isl_ast_build_set_create_leaf( + __isl_take isl_ast_build *build, + __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_build *build, + void *user), void *user) +{ + build = isl_ast_build_cow(build); + + if (!build) + return NULL; + + build->create_leaf = fn; + build->create_leaf_user = user; + + return build; +} + +/* Clear all information that is specific to this code generation + * and that is (probably) not meaningful to any nested code generation. + */ +__isl_give isl_ast_build *isl_ast_build_clear_local_info( + __isl_take isl_ast_build *build) +{ + isl_space *space; + + build = isl_ast_build_cow(build); + if (!build) + return NULL; + + space = isl_union_map_get_space(build->options); + isl_union_map_free(build->options); + build->options = isl_union_map_empty(space); + + build->at_each_domain = NULL; + build->at_each_domain_user = NULL; + build->before_each_for = NULL; + build->before_each_for_user = NULL; + build->after_each_for = NULL; + build->after_each_for_user = NULL; + build->before_each_mark = NULL; + build->before_each_mark_user = NULL; + build->after_each_mark = NULL; + build->after_each_mark_user = NULL; + build->create_leaf = NULL; + build->create_leaf_user = NULL; + + if (!build->options) + return isl_ast_build_free(build); + + return build; +} + +/* Have any loops been eliminated? + * That is, do any of the original schedule dimensions have a fixed + * value that has been substituted? + */ +static int any_eliminated(isl_ast_build *build) +{ + int i; + + for (i = 0; i < build->depth; ++i) + if (isl_ast_build_has_affine_value(build, i)) + return 1; + + return 0; +} + +/* Clear build->schedule_map. + * This function should be called whenever anything that might affect + * the result of isl_ast_build_get_schedule_map_multi_aff changes. + * In particular, it should be called when the depth is changed or + * when an iterator is determined to have a fixed value. + */ +static void isl_ast_build_reset_schedule_map(__isl_keep isl_ast_build *build) +{ + if (!build) + return; + isl_multi_aff_free(build->schedule_map); + build->schedule_map = NULL; +} + +/* Do we need a (non-trivial) schedule map? + * That is, is the internal schedule space different from + * the external schedule space? + * + * The internal and external schedule spaces are only the same + * if code has been generated for the entire schedule and if none + * of the loops have been eliminated. + */ +__isl_give int isl_ast_build_need_schedule_map(__isl_keep isl_ast_build *build) +{ + int dim; + + if (!build) + return -1; + + dim = isl_set_dim(build->domain, isl_dim_set); + return build->depth != dim || any_eliminated(build); +} + +/* Return a mapping from the internal schedule space to the external + * schedule space in the form of an isl_multi_aff. + * The internal schedule space originally corresponds to that of the + * input schedule. This may change during the code generation if + * if isl_ast_build_insert_dim is ever called. + * The external schedule space corresponds to the + * loops that have been generated. + * + * Currently, the only difference between the internal schedule domain + * and the external schedule domain is that some dimensions are projected + * out in the external schedule domain. In particular, the dimensions + * for which no code has been generated yet and the dimensions that correspond + * to eliminated loops. + * + * We cache a copy of the schedule_map in build->schedule_map. + * The cache is cleared through isl_ast_build_reset_schedule_map + * whenever anything changes that might affect the result of this function. + */ +__isl_give isl_multi_aff *isl_ast_build_get_schedule_map_multi_aff( + __isl_keep isl_ast_build *build) +{ + isl_space *space; + isl_multi_aff *ma; + + if (!build) + return NULL; + if (build->schedule_map) + return isl_multi_aff_copy(build->schedule_map); + + space = isl_ast_build_get_space(build, 1); + space = isl_space_map_from_set(space); + ma = isl_multi_aff_identity(space); + if (isl_ast_build_need_schedule_map(build)) { + int i; + int dim = isl_set_dim(build->domain, isl_dim_set); + ma = isl_multi_aff_drop_dims(ma, isl_dim_out, + build->depth, dim - build->depth); + for (i = build->depth - 1; i >= 0; --i) + if (isl_ast_build_has_affine_value(build, i)) + ma = isl_multi_aff_drop_dims(ma, + isl_dim_out, i, 1); + } + + build->schedule_map = ma; + return isl_multi_aff_copy(build->schedule_map); +} + +/* Return a mapping from the internal schedule space to the external + * schedule space in the form of an isl_map. + */ +__isl_give isl_map *isl_ast_build_get_schedule_map( + __isl_keep isl_ast_build *build) +{ + isl_multi_aff *ma; + + ma = isl_ast_build_get_schedule_map_multi_aff(build); + return isl_map_from_multi_aff(ma); +} + +/* Return the position of the dimension in build->domain for which + * an AST node is currently being generated. + */ +int isl_ast_build_get_depth(__isl_keep isl_ast_build *build) +{ + return build ? build->depth : -1; +} + +/* Prepare for generating code for the next level. + * In particular, increase the depth and reset any information + * that is local to the current depth. + */ +__isl_give isl_ast_build *isl_ast_build_increase_depth( + __isl_take isl_ast_build *build) +{ + build = isl_ast_build_cow(build); + if (!build) + return NULL; + build->depth++; + isl_ast_build_reset_schedule_map(build); + build->value = isl_pw_aff_free(build->value); + return build; +} + +void isl_ast_build_dump(__isl_keep isl_ast_build *build) +{ + if (!build) + return; + + fprintf(stderr, "domain: "); + isl_set_dump(build->domain); + fprintf(stderr, "generated: "); + isl_set_dump(build->generated); + fprintf(stderr, "pending: "); + isl_set_dump(build->pending); + fprintf(stderr, "iterators: "); + isl_id_list_dump(build->iterators); + fprintf(stderr, "values: "); + isl_multi_aff_dump(build->values); + if (build->value) { + fprintf(stderr, "value: "); + isl_pw_aff_dump(build->value); + } + fprintf(stderr, "strides: "); + isl_vec_dump(build->strides); + fprintf(stderr, "offsets: "); + isl_multi_aff_dump(build->offsets); + fprintf(stderr, "internal2input: "); + isl_multi_aff_dump(build->internal2input); +} + +/* Initialize "build" for AST construction in schedule space "space" + * in the case that build->domain is a parameter set. + * + * build->iterators is assumed to have been updated already. + */ +static __isl_give isl_ast_build *isl_ast_build_init( + __isl_take isl_ast_build *build, __isl_take isl_space *space) +{ + isl_set *set; + + build = isl_ast_build_cow(build); + if (!build) + goto error; + + set = isl_set_universe(isl_space_copy(space)); + build->domain = isl_set_intersect_params(isl_set_copy(set), + build->domain); + build->pending = isl_set_intersect_params(isl_set_copy(set), + build->pending); + build->generated = isl_set_intersect_params(set, build->generated); + + return isl_ast_build_init_derived(build, space); +error: + isl_ast_build_free(build); + isl_space_free(space); + return NULL; +} + +/* Assign "aff" to *user and return -1, effectively extracting + * the first (and presumably only) affine expression in the isl_pw_aff + * on which this function is used. + */ +static isl_stat extract_single_piece(__isl_take isl_set *set, + __isl_take isl_aff *aff, void *user) +{ + isl_aff **p = user; + + *p = aff; + isl_set_free(set); + + return isl_stat_error; +} + +/* Intersect "set" with the stride constraint of "build", if any. + */ +static __isl_give isl_set *intersect_stride_constraint(__isl_take isl_set *set, + __isl_keep isl_ast_build *build) +{ + isl_set *stride; + + if (!build) + return isl_set_free(set); + if (!isl_ast_build_has_stride(build, build->depth)) + return set; + + stride = isl_ast_build_get_stride_constraint(build); + return isl_set_intersect(set, stride); +} + +/* Check if the given bounds on the current dimension (together with + * the stride constraint, if any) imply that + * this current dimension attains only a single value (in terms of + * parameters and outer dimensions). + * If so, we record it in build->value. + * If, moreover, this value can be represented as a single affine expression, + * then we also update build->values, effectively marking the current + * dimension as "eliminated". + * + * When computing the gist of the fixed value that can be represented + * as a single affine expression, it is important to only take into + * account the domain constraints in the original AST build and + * not the domain of the affine expression itself. + * Otherwise, a [i/3] is changed into a i/3 because we know that i + * is a multiple of 3, but then we end up not expressing anywhere + * in the context that i is a multiple of 3. + */ +static __isl_give isl_ast_build *update_values( + __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds) +{ + int sv; + isl_pw_multi_aff *pma; + isl_aff *aff = NULL; + isl_map *it_map; + isl_set *set; + + set = isl_set_from_basic_set(bounds); + set = isl_set_intersect(set, isl_set_copy(build->domain)); + set = intersect_stride_constraint(set, build); + it_map = isl_ast_build_map_to_iterator(build, set); + + sv = isl_map_is_single_valued(it_map); + if (sv < 0) + build = isl_ast_build_free(build); + if (!build || !sv) { + isl_map_free(it_map); + return build; + } + + pma = isl_pw_multi_aff_from_map(it_map); + build->value = isl_pw_multi_aff_get_pw_aff(pma, 0); + build->value = isl_ast_build_compute_gist_pw_aff(build, build->value); + build->value = isl_pw_aff_coalesce(build->value); + isl_pw_multi_aff_free(pma); + + if (!build->value) + return isl_ast_build_free(build); + + if (isl_pw_aff_n_piece(build->value) != 1) + return build; + + isl_pw_aff_foreach_piece(build->value, &extract_single_piece, &aff); + + build->values = isl_multi_aff_set_aff(build->values, build->depth, aff); + if (!build->values) + return isl_ast_build_free(build); + isl_ast_build_reset_schedule_map(build); + return build; +} + +/* Update the AST build based on the given loop bounds for + * the current dimension and the stride information available in the build. + * + * We first make sure that the bounds do not refer to any iterators + * that have already been eliminated. + * Then, we check if the bounds imply that the current iterator + * has a fixed value. + * If they do and if this fixed value can be expressed as a single + * affine expression, we eliminate the iterators from the bounds. + * Note that we cannot simply plug in this single value using + * isl_basic_set_preimage_multi_aff as the single value may only + * be defined on a subset of the domain. Plugging in the value + * would restrict the build domain to this subset, while this + * restriction may not be reflected in the generated code. + * Finally, we intersect build->domain with the updated bounds. + * We also add the stride constraint unless we have been able + * to find a fixed value expressed as a single affine expression. + * + * Note that the check for a fixed value in update_values requires + * us to intersect the bounds with the current build domain. + * When we intersect build->domain with the updated bounds in + * the final step, we make sure that these updated bounds have + * not been intersected with the old build->domain. + * Otherwise, we would indirectly intersect the build domain with itself, + * which can lead to inefficiencies, in particular if the build domain + * contains any unknown divs. + * + * The pending and generated sets are not updated by this function to + * match the updated domain. + * The caller still needs to call isl_ast_build_set_pending_generated. + */ +__isl_give isl_ast_build *isl_ast_build_set_loop_bounds( + __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds) +{ + isl_set *set; + + build = isl_ast_build_cow(build); + if (!build) + goto error; + + build = update_values(build, isl_basic_set_copy(bounds)); + if (!build) + goto error; + set = isl_set_from_basic_set(bounds); + if (isl_ast_build_has_affine_value(build, build->depth)) { + set = isl_set_eliminate(set, isl_dim_set, build->depth, 1); + set = isl_set_compute_divs(set); + build->pending = isl_set_intersect(build->pending, + isl_set_copy(set)); + build->domain = isl_set_intersect(build->domain, set); + } else { + build->domain = isl_set_intersect(build->domain, set); + build = isl_ast_build_include_stride(build); + if (!build) + goto error; + } + + if (!build->domain || !build->pending || !build->generated) + return isl_ast_build_free(build); + + return build; +error: + isl_ast_build_free(build); + isl_basic_set_free(bounds); + return NULL; +} + +/* Update the pending and generated sets of "build" according to "bounds". + * If the build has an affine value at the current depth, + * then isl_ast_build_set_loop_bounds has already set the pending set. + * Otherwise, do it here. + */ +__isl_give isl_ast_build *isl_ast_build_set_pending_generated( + __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds) +{ + isl_basic_set *generated, *pending; + + if (!build) + goto error; + + if (isl_ast_build_has_affine_value(build, build->depth)) { + isl_basic_set_free(bounds); + return build; + } + + build = isl_ast_build_cow(build); + if (!build) + goto error; + + pending = isl_basic_set_copy(bounds); + pending = isl_basic_set_drop_constraints_involving_dims(pending, + isl_dim_set, build->depth, 1); + build->pending = isl_set_intersect(build->pending, + isl_set_from_basic_set(pending)); + generated = bounds; + generated = isl_basic_set_drop_constraints_not_involving_dims( + generated, isl_dim_set, build->depth, 1); + build->generated = isl_set_intersect(build->generated, + isl_set_from_basic_set(generated)); + + if (!build->pending || !build->generated) + return isl_ast_build_free(build); + + return build; +error: + isl_ast_build_free(build); + isl_basic_set_free(bounds); + return NULL; +} + +/* Intersect build->domain with "set", where "set" is specified + * in terms of the internal schedule domain. + */ +static __isl_give isl_ast_build *isl_ast_build_restrict_internal( + __isl_take isl_ast_build *build, __isl_take isl_set *set) +{ + build = isl_ast_build_cow(build); + if (!build) + goto error; + + set = isl_set_compute_divs(set); + build->domain = isl_set_intersect(build->domain, set); + build->domain = isl_set_coalesce(build->domain); + + if (!build->domain) + return isl_ast_build_free(build); + + return build; +error: + isl_ast_build_free(build); + isl_set_free(set); + return NULL; +} + +/* Intersect build->generated and build->domain with "set", + * where "set" is specified in terms of the internal schedule domain. + */ +__isl_give isl_ast_build *isl_ast_build_restrict_generated( + __isl_take isl_ast_build *build, __isl_take isl_set *set) +{ + set = isl_set_compute_divs(set); + build = isl_ast_build_restrict_internal(build, isl_set_copy(set)); + build = isl_ast_build_cow(build); + if (!build) + goto error; + + build->generated = isl_set_intersect(build->generated, set); + build->generated = isl_set_coalesce(build->generated); + + if (!build->generated) + return isl_ast_build_free(build); + + return build; +error: + isl_ast_build_free(build); + isl_set_free(set); + return NULL; +} + +/* Replace the set of pending constraints by "guard", which is then + * no longer considered as pending. + * That is, add "guard" to the generated constraints and clear all pending + * constraints, making the domain equal to the generated constraints. + */ +__isl_give isl_ast_build *isl_ast_build_replace_pending_by_guard( + __isl_take isl_ast_build *build, __isl_take isl_set *guard) +{ + build = isl_ast_build_restrict_generated(build, guard); + build = isl_ast_build_cow(build); + if (!build) + return NULL; + + isl_set_free(build->domain); + build->domain = isl_set_copy(build->generated); + isl_set_free(build->pending); + build->pending = isl_set_universe(isl_set_get_space(build->domain)); + + if (!build->pending) + return isl_ast_build_free(build); + + return build; +} + +/* Intersect build->domain with "set", where "set" is specified + * in terms of the external schedule domain. + */ +__isl_give isl_ast_build *isl_ast_build_restrict( + __isl_take isl_ast_build *build, __isl_take isl_set *set) +{ + if (isl_set_is_params(set)) + return isl_ast_build_restrict_generated(build, set); + + if (isl_ast_build_need_schedule_map(build)) { + isl_multi_aff *ma; + ma = isl_ast_build_get_schedule_map_multi_aff(build); + set = isl_set_preimage_multi_aff(set, ma); + } + return isl_ast_build_restrict_generated(build, set); +} + +/* Replace build->executed by "executed". + */ +__isl_give isl_ast_build *isl_ast_build_set_executed( + __isl_take isl_ast_build *build, __isl_take isl_union_map *executed) +{ + build = isl_ast_build_cow(build); + if (!build) + goto error; + + isl_union_map_free(build->executed); + build->executed = executed; + + return build; +error: + isl_ast_build_free(build); + isl_union_map_free(executed); + return NULL; +} + +/* Does "build" point to a band node? + * That is, are we currently handling a band node inside a schedule tree? + */ +int isl_ast_build_has_schedule_node(__isl_keep isl_ast_build *build) +{ + if (!build) + return -1; + return build->node != NULL; +} + +/* Return a copy of the band node that "build" refers to. + */ +__isl_give isl_schedule_node *isl_ast_build_get_schedule_node( + __isl_keep isl_ast_build *build) +{ + if (!build) + return NULL; + return isl_schedule_node_copy(build->node); +} + +/* Extract the loop AST generation types for the members of build->node + * and store them in build->loop_type. + */ +static __isl_give isl_ast_build *extract_loop_types( + __isl_take isl_ast_build *build) +{ + int i; + isl_ctx *ctx; + isl_schedule_node *node; + + if (!build) + return NULL; + ctx = isl_ast_build_get_ctx(build); + if (!build->node) + isl_die(ctx, isl_error_internal, "missing AST node", + return isl_ast_build_free(build)); + + free(build->loop_type); + build->n = isl_schedule_node_band_n_member(build->node); + build->loop_type = isl_alloc_array(ctx, + enum isl_ast_loop_type, build->n); + if (build->n && !build->loop_type) + return isl_ast_build_free(build); + node = build->node; + for (i = 0; i < build->n; ++i) + build->loop_type[i] = + isl_schedule_node_band_member_get_ast_loop_type(node, i); + + return build; +} + +/* Replace the band node that "build" refers to by "node" and + * extract the corresponding loop AST generation types. + */ +__isl_give isl_ast_build *isl_ast_build_set_schedule_node( + __isl_take isl_ast_build *build, + __isl_take isl_schedule_node *node) +{ + build = isl_ast_build_cow(build); + if (!build || !node) + goto error; + + isl_schedule_node_free(build->node); + build->node = node; + + build = extract_loop_types(build); + + return build; +error: + isl_ast_build_free(build); + isl_schedule_node_free(node); + return NULL; +} + +/* Remove any reference to a band node from "build". + */ +__isl_give isl_ast_build *isl_ast_build_reset_schedule_node( + __isl_take isl_ast_build *build) +{ + build = isl_ast_build_cow(build); + if (!build) + return NULL; + + isl_schedule_node_free(build->node); + build->node = NULL; + + return build; +} + +/* Return a copy of the current schedule domain. + */ +__isl_give isl_set *isl_ast_build_get_domain(__isl_keep isl_ast_build *build) +{ + return build ? isl_set_copy(build->domain) : NULL; +} + +/* Return a copy of the set of pending constraints. + */ +__isl_give isl_set *isl_ast_build_get_pending( + __isl_keep isl_ast_build *build) +{ + return build ? isl_set_copy(build->pending) : NULL; +} + +/* Return a copy of the set of generated constraints. + */ +__isl_give isl_set *isl_ast_build_get_generated( + __isl_keep isl_ast_build *build) +{ + return build ? isl_set_copy(build->generated) : NULL; +} + +/* Return a copy of the map from the internal schedule domain + * to the original input schedule domain. + */ +__isl_give isl_multi_aff *isl_ast_build_get_internal2input( + __isl_keep isl_ast_build *build) +{ + return build ? isl_multi_aff_copy(build->internal2input) : NULL; +} + +/* Return the number of variables of the given type + * in the (internal) schedule space. + */ +unsigned isl_ast_build_dim(__isl_keep isl_ast_build *build, + enum isl_dim_type type) +{ + if (!build) + return 0; + return isl_set_dim(build->domain, type); +} + +/* Return the (schedule) space of "build". + * + * If "internal" is set, then this space is the space of the internal + * representation of the entire schedule, including those parts for + * which no code has been generated yet. + * + * If "internal" is not set, then this space is the external representation + * of the loops generated so far. + */ +__isl_give isl_space *isl_ast_build_get_space(__isl_keep isl_ast_build *build, + int internal) +{ + int i; + int dim; + isl_space *space; + + if (!build) + return NULL; + + space = isl_set_get_space(build->domain); + if (internal) + return space; + + if (!isl_ast_build_need_schedule_map(build)) + return space; + + dim = isl_set_dim(build->domain, isl_dim_set); + space = isl_space_drop_dims(space, isl_dim_set, + build->depth, dim - build->depth); + for (i = build->depth - 1; i >= 0; --i) + if (isl_ast_build_has_affine_value(build, i)) + space = isl_space_drop_dims(space, isl_dim_set, i, 1); + + return space; +} + +/* Return the external representation of the schedule space of "build", + * i.e., a space with a dimension for each loop generated so far, + * with the names of the dimensions set to the loop iterators. + */ +__isl_give isl_space *isl_ast_build_get_schedule_space( + __isl_keep isl_ast_build *build) +{ + isl_space *space; + int i, skip; + + if (!build) + return NULL; + + space = isl_ast_build_get_space(build, 0); + + skip = 0; + for (i = 0; i < build->depth; ++i) { + isl_id *id; + + if (isl_ast_build_has_affine_value(build, i)) { + skip++; + continue; + } + + id = isl_ast_build_get_iterator_id(build, i); + space = isl_space_set_dim_id(space, isl_dim_set, i - skip, id); + } + + return space; +} + +/* Return the current schedule, as stored in build->executed, in terms + * of the external schedule domain. + */ +__isl_give isl_union_map *isl_ast_build_get_schedule( + __isl_keep isl_ast_build *build) +{ + isl_union_map *executed; + isl_union_map *schedule; + + if (!build) + return NULL; + + executed = isl_union_map_copy(build->executed); + if (isl_ast_build_need_schedule_map(build)) { + isl_map *proj = isl_ast_build_get_schedule_map(build); + executed = isl_union_map_apply_domain(executed, + isl_union_map_from_map(proj)); + } + schedule = isl_union_map_reverse(executed); + + return schedule; +} + +/* Return the iterator attached to the internal schedule dimension "pos". + */ +__isl_give isl_id *isl_ast_build_get_iterator_id( + __isl_keep isl_ast_build *build, int pos) +{ + if (!build) + return NULL; + + return isl_id_list_get_id(build->iterators, pos); +} + +/* Set the stride and offset of the current dimension to the given + * value and expression. + * + * If we had already found a stride before, then the two strides + * are combined into a single stride. + * + * In particular, if the new stride information is of the form + * + * i = f + s (...) + * + * and the old stride information is of the form + * + * i = f2 + s2 (...) + * + * then we compute the extended gcd of s and s2 + * + * a s + b s2 = g, + * + * with g = gcd(s,s2), multiply the first equation with t1 = b s2/g + * and the second with t2 = a s1/g. + * This results in + * + * i = (b s2 + a s1)/g i = t1 f + t2 f2 + (s s2)/g (...) + * + * so that t1 f + t2 f2 is the combined offset and (s s2)/g = lcm(s,s2) + * is the combined stride. + */ +static __isl_give isl_ast_build *set_stride(__isl_take isl_ast_build *build, + __isl_take isl_val *stride, __isl_take isl_aff *offset) +{ + int pos; + + build = isl_ast_build_cow(build); + if (!build || !stride || !offset) + goto error; + + pos = build->depth; + + if (isl_ast_build_has_stride(build, pos)) { + isl_val *stride2, *a, *b, *g; + isl_aff *offset2; + + stride2 = isl_vec_get_element_val(build->strides, pos); + g = isl_val_gcdext(isl_val_copy(stride), isl_val_copy(stride2), + &a, &b); + a = isl_val_mul(a, isl_val_copy(stride)); + a = isl_val_div(a, isl_val_copy(g)); + stride2 = isl_val_div(stride2, g); + b = isl_val_mul(b, isl_val_copy(stride2)); + stride = isl_val_mul(stride, stride2); + + offset2 = isl_multi_aff_get_aff(build->offsets, pos); + offset2 = isl_aff_scale_val(offset2, a); + offset = isl_aff_scale_val(offset, b); + offset = isl_aff_add(offset, offset2); + } + + build->strides = isl_vec_set_element_val(build->strides, pos, stride); + build->offsets = isl_multi_aff_set_aff(build->offsets, pos, offset); + if (!build->strides || !build->offsets) + return isl_ast_build_free(build); + + return build; +error: + isl_val_free(stride); + isl_aff_free(offset); + return isl_ast_build_free(build); +} + +/* Return a set expressing the stride constraint at the current depth. + * + * In particular, if the current iterator (i) is known to attain values + * + * f + s a + * + * where f is the offset and s is the stride, then the returned set + * expresses the constraint + * + * (f - i) mod s = 0 + */ +__isl_give isl_set *isl_ast_build_get_stride_constraint( + __isl_keep isl_ast_build *build) +{ + isl_aff *aff; + isl_set *set; + isl_val *stride; + int pos; + + if (!build) + return NULL; + + pos = build->depth; + + if (!isl_ast_build_has_stride(build, pos)) + return isl_set_universe(isl_ast_build_get_space(build, 1)); + + stride = isl_ast_build_get_stride(build, pos); + aff = isl_ast_build_get_offset(build, pos); + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, pos, -1); + aff = isl_aff_mod_val(aff, stride); + set = isl_set_from_basic_set(isl_aff_zero_basic_set(aff)); + + return set; +} + +/* Return the expansion implied by the stride and offset at the current + * depth. + * + * That is, return the mapping + * + * [i_0, ..., i_{d-1}, i_d, i_{d+1}, ...] + * -> [i_0, ..., i_{d-1}, s * i_d + offset(i), i_{d+1}, ...] + * + * where s is the stride at the current depth d and offset(i) is + * the corresponding offset. + */ +__isl_give isl_multi_aff *isl_ast_build_get_stride_expansion( + __isl_keep isl_ast_build *build) +{ + isl_space *space; + isl_multi_aff *ma; + int pos; + isl_aff *aff, *offset; + isl_val *stride; + + if (!build) + return NULL; + + pos = isl_ast_build_get_depth(build); + space = isl_ast_build_get_space(build, 1); + space = isl_space_map_from_set(space); + ma = isl_multi_aff_identity(space); + + if (!isl_ast_build_has_stride(build, pos)) + return ma; + + offset = isl_ast_build_get_offset(build, pos); + stride = isl_ast_build_get_stride(build, pos); + aff = isl_multi_aff_get_aff(ma, pos); + aff = isl_aff_scale_val(aff, stride); + aff = isl_aff_add(aff, offset); + ma = isl_multi_aff_set_aff(ma, pos, aff); + + return ma; +} + +/* Add constraints corresponding to any previously detected + * stride on the current dimension to build->domain. + */ +__isl_give isl_ast_build *isl_ast_build_include_stride( + __isl_take isl_ast_build *build) +{ + isl_set *set; + + if (!build) + return NULL; + if (!isl_ast_build_has_stride(build, build->depth)) + return build; + build = isl_ast_build_cow(build); + if (!build) + return NULL; + + set = isl_ast_build_get_stride_constraint(build); + + build->domain = isl_set_intersect(build->domain, isl_set_copy(set)); + build->generated = isl_set_intersect(build->generated, set); + if (!build->domain || !build->generated) + return isl_ast_build_free(build); + + return build; +} + +/* Information used inside detect_stride. + * + * "build" may be updated by detect_stride to include stride information. + * "pos" is equal to build->depth. + */ +struct isl_detect_stride_data { + isl_ast_build *build; + int pos; +}; + +/* Check if constraint "c" imposes any stride on dimension data->pos + * and, if so, update the stride information in data->build. + * + * In order to impose a stride on the dimension, "c" needs to be an equality + * and it needs to involve the dimension. Note that "c" may also be + * a div constraint and thus an inequality that we cannot use. + * + * Let c be of the form + * + * h(p) + g * v * i + g * stride * f(alpha) = 0 + * + * with h(p) an expression in terms of the parameters and outer dimensions + * and f(alpha) an expression in terms of the existentially quantified + * variables. Note that the inner dimensions have been eliminated so + * they do not appear in "c". + * + * If "stride" is not zero and not one, then it represents a non-trivial stride + * on "i". We compute a and b such that + * + * a v + b stride = 1 + * + * We have + * + * g v i = -h(p) + g stride f(alpha) + * + * a g v i = -a h(p) + g stride f(alpha) + * + * a g v i + b g stride i = -a h(p) + g stride * (...) + * + * g i = -a h(p) + g stride * (...) + * + * i = -a h(p)/g + stride * (...) + * + * The expression "-a h(p)/g" can therefore be used as offset. + */ +static isl_stat detect_stride(__isl_take isl_constraint *c, void *user) +{ + struct isl_detect_stride_data *data = user; + int i, n_div; + isl_ctx *ctx; + isl_val *v, *stride, *m; + + if (!isl_constraint_is_equality(c) || + !isl_constraint_involves_dims(c, isl_dim_set, data->pos, 1)) { + isl_constraint_free(c); + return isl_stat_ok; + } + + ctx = isl_constraint_get_ctx(c); + stride = isl_val_zero(ctx); + n_div = isl_constraint_dim(c, isl_dim_div); + for (i = 0; i < n_div; ++i) { + v = isl_constraint_get_coefficient_val(c, isl_dim_div, i); + stride = isl_val_gcd(stride, v); + } + + v = isl_constraint_get_coefficient_val(c, isl_dim_set, data->pos); + m = isl_val_gcd(isl_val_copy(stride), isl_val_copy(v)); + stride = isl_val_div(stride, isl_val_copy(m)); + v = isl_val_div(v, isl_val_copy(m)); + + if (!isl_val_is_zero(stride) && !isl_val_is_one(stride)) { + isl_aff *aff; + isl_val *gcd, *a, *b; + + gcd = isl_val_gcdext(v, isl_val_copy(stride), &a, &b); + isl_val_free(gcd); + isl_val_free(b); + + aff = isl_constraint_get_aff(c); + for (i = 0; i < n_div; ++i) + aff = isl_aff_set_coefficient_si(aff, + isl_dim_div, i, 0); + aff = isl_aff_set_coefficient_si(aff, isl_dim_in, data->pos, 0); + a = isl_val_neg(a); + aff = isl_aff_scale_val(aff, a); + aff = isl_aff_scale_down_val(aff, m); + data->build = set_stride(data->build, stride, aff); + } else { + isl_val_free(stride); + isl_val_free(m); + isl_val_free(v); + } + + isl_constraint_free(c); + return isl_stat_ok; +} + +/* Check if the constraints in "set" imply any stride on the current + * dimension and, if so, record the stride information in "build" + * and return the updated "build". + * + * We compute the affine hull and then check if any of the constraints + * in the hull imposes any stride on the current dimension. + * + * We assume that inner dimensions have been eliminated from "set" + * by the caller. This is needed because the common stride + * may be imposed by different inner dimensions on different parts of + * the domain. + */ +__isl_give isl_ast_build *isl_ast_build_detect_strides( + __isl_take isl_ast_build *build, __isl_take isl_set *set) +{ + isl_basic_set *hull; + struct isl_detect_stride_data data; + + if (!build) + goto error; + + data.build = build; + data.pos = isl_ast_build_get_depth(build); + hull = isl_set_affine_hull(set); + + if (isl_basic_set_foreach_constraint(hull, &detect_stride, &data) < 0) + data.build = isl_ast_build_free(data.build); + + isl_basic_set_free(hull); + return data.build; +error: + isl_set_free(set); + return NULL; +} + +struct isl_ast_build_involves_data { + int depth; + int involves; +}; + +/* Check if "map" involves the input dimension data->depth. + */ +static isl_stat involves_depth(__isl_take isl_map *map, void *user) +{ + struct isl_ast_build_involves_data *data = user; + + data->involves = isl_map_involves_dims(map, isl_dim_in, data->depth, 1); + isl_map_free(map); + + if (data->involves < 0 || data->involves) + return isl_stat_error; + return isl_stat_ok; +} + +/* Do any options depend on the value of the dimension at the current depth? + */ +int isl_ast_build_options_involve_depth(__isl_keep isl_ast_build *build) +{ + struct isl_ast_build_involves_data data; + + if (!build) + return -1; + + data.depth = build->depth; + data.involves = 0; + + if (isl_union_map_foreach_map(build->options, + &involves_depth, &data) < 0) { + if (data.involves < 0 || !data.involves) + return -1; + } + + return data.involves; +} + +/* Construct the map + * + * { [i] -> [i] : i < pos; [i] -> [i + 1] : i >= pos } + * + * with "space" the parameter space of the constructed map. + */ +static __isl_give isl_map *construct_insertion_map(__isl_take isl_space *space, + int pos) +{ + isl_constraint *c; + isl_basic_map *bmap1, *bmap2; + + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, 1); + space = isl_space_map_from_set(space); + c = isl_constraint_alloc_equality(isl_local_space_from_space(space)); + c = isl_constraint_set_coefficient_si(c, isl_dim_in, 0, 1); + c = isl_constraint_set_coefficient_si(c, isl_dim_out, 0, -1); + bmap1 = isl_basic_map_from_constraint(isl_constraint_copy(c)); + c = isl_constraint_set_constant_si(c, 1); + bmap2 = isl_basic_map_from_constraint(c); + + bmap1 = isl_basic_map_upper_bound_si(bmap1, isl_dim_in, 0, pos - 1); + bmap2 = isl_basic_map_lower_bound_si(bmap2, isl_dim_in, 0, pos); + + return isl_basic_map_union(bmap1, bmap2); +} + +static const char *option_str[] = { + [isl_ast_loop_atomic] = "atomic", + [isl_ast_loop_unroll] = "unroll", + [isl_ast_loop_separate] = "separate" +}; + +/* Update the "options" to reflect the insertion of a dimension + * at position "pos" in the schedule domain space. + * "space" is the original domain space before the insertion and + * may be named and/or structured. + * + * The (relevant) input options all have "space" as domain, which + * has to be mapped to the extended space. + * The values of the ranges also refer to the schedule domain positions + * and they therefore also need to be adjusted. In particular, values + * smaller than pos do not need to change, while values greater than or + * equal to pos need to be incremented. + * That is, we need to apply the following map. + * + * { atomic[i] -> atomic[i] : i < pos; [i] -> [i + 1] : i >= pos; + * unroll[i] -> unroll[i] : i < pos; [i] -> [i + 1] : i >= pos; + * separate[i] -> separate[i] : i < pos; [i] -> [i + 1] : i >= pos; + * separation_class[[i] -> [c]] + * -> separation_class[[i] -> [c]] : i < pos; + * separation_class[[i] -> [c]] + * -> separation_class[[i + 1] -> [c]] : i >= pos } + */ +static __isl_give isl_union_map *options_insert_dim( + __isl_take isl_union_map *options, __isl_take isl_space *space, int pos) +{ + isl_map *map; + isl_union_map *insertion; + enum isl_ast_loop_type type; + const char *name = "separation_class"; + + space = isl_space_map_from_set(space); + map = isl_map_identity(space); + map = isl_map_insert_dims(map, isl_dim_out, pos, 1); + options = isl_union_map_apply_domain(options, + isl_union_map_from_map(map)); + + if (!options) + return NULL; + + map = construct_insertion_map(isl_union_map_get_space(options), pos); + + insertion = isl_union_map_empty(isl_union_map_get_space(options)); + + for (type = isl_ast_loop_atomic; + type <= isl_ast_loop_separate; ++type) { + isl_map *map_type = isl_map_copy(map); + const char *name = option_str[type]; + map_type = isl_map_set_tuple_name(map_type, isl_dim_in, name); + map_type = isl_map_set_tuple_name(map_type, isl_dim_out, name); + insertion = isl_union_map_add_map(insertion, map_type); + } + + map = isl_map_product(map, isl_map_identity(isl_map_get_space(map))); + map = isl_map_set_tuple_name(map, isl_dim_in, name); + map = isl_map_set_tuple_name(map, isl_dim_out, name); + insertion = isl_union_map_add_map(insertion, map); + + options = isl_union_map_apply_range(options, insertion); + + return options; +} + +/* If we are generating an AST from a schedule tree (build->node is set), + * then update the loop AST generation types + * to reflect the insertion of a dimension at (global) position "pos" + * in the schedule domain space. + * We do not need to adjust any isolate option since we would not be inserting + * any dimensions if there were any isolate option. + */ +static __isl_give isl_ast_build *node_insert_dim( + __isl_take isl_ast_build *build, int pos) +{ + int i; + int local_pos; + enum isl_ast_loop_type *loop_type; + isl_ctx *ctx; + + build = isl_ast_build_cow(build); + if (!build) + return NULL; + if (!build->node) + return build; + + ctx = isl_ast_build_get_ctx(build); + local_pos = pos - build->outer_pos; + loop_type = isl_realloc_array(ctx, build->loop_type, + enum isl_ast_loop_type, build->n + 1); + if (!loop_type) + return isl_ast_build_free(build); + build->loop_type = loop_type; + for (i = build->n - 1; i >= local_pos; --i) + loop_type[i + 1] = loop_type[i]; + loop_type[local_pos] = isl_ast_loop_default; + build->n++; + + return build; +} + +/* Insert a single dimension in the schedule domain at position "pos". + * The new dimension is given an isl_id with the empty string as name. + * + * The main difficulty is updating build->options to reflect the + * extra dimension. This is handled in options_insert_dim. + * + * Note that because of the dimension manipulations, the resulting + * schedule domain space will always be unnamed and unstructured. + * However, the original schedule domain space may be named and/or + * structured, so we have to take this possibility into account + * while performing the transformations. + * + * Since the inserted schedule dimension is used by the caller + * to differentiate between different domain spaces, there is + * no longer a uniform mapping from the internal schedule space + * to the input schedule space. The internal2input mapping is + * therefore removed. + */ +__isl_give isl_ast_build *isl_ast_build_insert_dim( + __isl_take isl_ast_build *build, int pos) +{ + isl_ctx *ctx; + isl_space *space, *ma_space; + isl_id *id; + isl_multi_aff *ma; + + build = isl_ast_build_cow(build); + if (!build) + return NULL; + + ctx = isl_ast_build_get_ctx(build); + id = isl_id_alloc(ctx, "", NULL); + if (!build->node) + space = isl_ast_build_get_space(build, 1); + build->iterators = isl_id_list_insert(build->iterators, pos, id); + build->domain = isl_set_insert_dims(build->domain, + isl_dim_set, pos, 1); + build->generated = isl_set_insert_dims(build->generated, + isl_dim_set, pos, 1); + build->pending = isl_set_insert_dims(build->pending, + isl_dim_set, pos, 1); + build->strides = isl_vec_insert_els(build->strides, pos, 1); + build->strides = isl_vec_set_element_si(build->strides, pos, 1); + ma_space = isl_space_params(isl_multi_aff_get_space(build->offsets)); + ma_space = isl_space_set_from_params(ma_space); + ma_space = isl_space_add_dims(ma_space, isl_dim_set, 1); + ma_space = isl_space_map_from_set(ma_space); + ma = isl_multi_aff_zero(isl_space_copy(ma_space)); + build->offsets = isl_multi_aff_splice(build->offsets, pos, pos, ma); + ma = isl_multi_aff_identity(ma_space); + build->values = isl_multi_aff_splice(build->values, pos, pos, ma); + if (!build->node) + build->options = options_insert_dim(build->options, space, pos); + build->internal2input = isl_multi_aff_free(build->internal2input); + + if (!build->iterators || !build->domain || !build->generated || + !build->pending || !build->values || + !build->strides || !build->offsets || !build->options) + return isl_ast_build_free(build); + + build = node_insert_dim(build, pos); + + return build; +} + +/* Scale down the current dimension by a factor of "m". + * "umap" is an isl_union_map that implements the scaling down. + * That is, it is of the form + * + * { [.... i ....] -> [.... i' ....] : i = m i' } + * + * This function is called right after the strides have been + * detected, but before any constraints on the current dimension + * have been included in build->domain. + * We therefore only need to update stride, offset, the options and + * the mapping from internal schedule space to the original schedule + * space, if we are still keeping track of such a mapping. + * The latter mapping is updated by plugging in + * { [... i ...] -> [... m i ... ] }. + */ +__isl_give isl_ast_build *isl_ast_build_scale_down( + __isl_take isl_ast_build *build, __isl_take isl_val *m, + __isl_take isl_union_map *umap) +{ + isl_aff *aff; + isl_val *v; + int depth; + + build = isl_ast_build_cow(build); + if (!build || !umap || !m) + goto error; + + depth = build->depth; + + if (build->internal2input) { + isl_space *space; + isl_multi_aff *ma; + isl_aff *aff; + + space = isl_multi_aff_get_space(build->internal2input); + space = isl_space_map_from_set(isl_space_domain(space)); + ma = isl_multi_aff_identity(space); + aff = isl_multi_aff_get_aff(ma, depth); + aff = isl_aff_scale_val(aff, isl_val_copy(m)); + ma = isl_multi_aff_set_aff(ma, depth, aff); + build->internal2input = + isl_multi_aff_pullback_multi_aff(build->internal2input, ma); + if (!build->internal2input) + goto error; + } + + v = isl_vec_get_element_val(build->strides, depth); + v = isl_val_div(v, isl_val_copy(m)); + build->strides = isl_vec_set_element_val(build->strides, depth, v); + + aff = isl_multi_aff_get_aff(build->offsets, depth); + aff = isl_aff_scale_down_val(aff, m); + build->offsets = isl_multi_aff_set_aff(build->offsets, depth, aff); + build->options = isl_union_map_apply_domain(build->options, umap); + if (!build->strides || !build->offsets || !build->options) + return isl_ast_build_free(build); + + return build; +error: + isl_val_free(m); + isl_union_map_free(umap); + return isl_ast_build_free(build); +} + +/* Return a list of "n" isl_ids called "c%d", with "%d" starting at "first". + * If an isl_id with such a name already appears among the parameters + * in build->domain, then adjust the name to "c%d_%d". + */ +static __isl_give isl_id_list *generate_names(isl_ctx *ctx, int n, int first, + __isl_keep isl_ast_build *build) +{ + int i; + isl_id_list *names; + + names = isl_id_list_alloc(ctx, n); + for (i = 0; i < n; ++i) { + isl_id *id; + + id = generate_name(ctx, first + i, build); + names = isl_id_list_add(names, id); + } + + return names; +} + +/* Embed "options" into the given isl_ast_build space. + * + * This function is called from within a nested call to + * isl_ast_build_node_from_schedule_map. + * "options" refers to the additional schedule, + * while space refers to both the space of the outer isl_ast_build and + * that of the additional schedule. + * Specifically, space is of the form + * + * [I -> S] + * + * while options lives in the space(s) + * + * S -> * + * + * We compute + * + * [I -> S] -> S + * + * and compose this with options, to obtain the new options + * living in the space(s) + * + * [I -> S] -> * + */ +static __isl_give isl_union_map *embed_options( + __isl_take isl_union_map *options, __isl_take isl_space *space) +{ + isl_map *map; + + map = isl_map_universe(isl_space_unwrap(space)); + map = isl_map_range_map(map); + + options = isl_union_map_apply_range( + isl_union_map_from_map(map), options); + + return options; +} + +/* Update "build" for use in a (possibly nested) code generation. That is, + * extend "build" from an AST build on some domain O to an AST build + * on domain [O -> S], with S corresponding to "space". + * If the original domain is a parameter domain, then the new domain is + * simply S. + * "iterators" is a list of iterators for S, but the number of elements + * may be smaller or greater than the number of set dimensions of S. + * If "keep_iterators" is set, then any extra ids in build->iterators + * are reused for S. Otherwise, these extra ids are dropped. + * + * We first update build->outer_pos to the current depth. + * This depth is zero in case this is the outermost code generation. + * + * We then add additional ids such that the number of iterators is at least + * equal to the dimension of the new build domain. + * + * If the original domain is parametric, then we are constructing + * an isl_ast_build for the outer code generation and we pass control + * to isl_ast_build_init. + * + * Otherwise, we adjust the fields of "build" to include "space". + */ +__isl_give isl_ast_build *isl_ast_build_product( + __isl_take isl_ast_build *build, __isl_take isl_space *space) +{ + isl_ctx *ctx; + isl_vec *strides; + isl_set *set; + isl_multi_aff *embedding; + int dim, n_it; + + build = isl_ast_build_cow(build); + if (!build) + goto error; + + build->outer_pos = build->depth; + + ctx = isl_ast_build_get_ctx(build); + dim = isl_set_dim(build->domain, isl_dim_set); + dim += isl_space_dim(space, isl_dim_set); + n_it = isl_id_list_n_id(build->iterators); + if (n_it < dim) { + isl_id_list *l; + l = generate_names(ctx, dim - n_it, n_it, build); + build->iterators = isl_id_list_concat(build->iterators, l); + } + + if (isl_set_is_params(build->domain)) + return isl_ast_build_init(build, space); + + set = isl_set_universe(isl_space_copy(space)); + build->domain = isl_set_product(build->domain, isl_set_copy(set)); + build->pending = isl_set_product(build->pending, isl_set_copy(set)); + build->generated = isl_set_product(build->generated, set); + + strides = isl_vec_alloc(ctx, isl_space_dim(space, isl_dim_set)); + strides = isl_vec_set_si(strides, 1); + build->strides = isl_vec_concat(build->strides, strides); + + space = isl_space_map_from_set(space); + build->offsets = isl_multi_aff_align_params(build->offsets, + isl_space_copy(space)); + build->offsets = isl_multi_aff_product(build->offsets, + isl_multi_aff_zero(isl_space_copy(space))); + build->values = isl_multi_aff_align_params(build->values, + isl_space_copy(space)); + embedding = isl_multi_aff_identity(space); + build->values = isl_multi_aff_product(build->values, + isl_multi_aff_copy(embedding)); + if (build->internal2input) { + build->internal2input = + isl_multi_aff_product(build->internal2input, embedding); + build->internal2input = + isl_multi_aff_flatten_range(build->internal2input); + if (!build->internal2input) + return isl_ast_build_free(build); + } else { + isl_multi_aff_free(embedding); + } + + space = isl_ast_build_get_space(build, 1); + build->options = embed_options(build->options, space); + + if (!build->iterators || !build->domain || !build->generated || + !build->pending || !build->values || + !build->strides || !build->offsets || !build->options) + return isl_ast_build_free(build); + + return build; +error: + isl_ast_build_free(build); + isl_space_free(space); + return NULL; +} + +/* Does "aff" only attain non-negative values over build->domain? + * That is, does it not attain any negative values? + */ +int isl_ast_build_aff_is_nonneg(__isl_keep isl_ast_build *build, + __isl_keep isl_aff *aff) +{ + isl_set *test; + int empty; + + if (!build) + return -1; + + aff = isl_aff_copy(aff); + test = isl_set_from_basic_set(isl_aff_neg_basic_set(aff)); + test = isl_set_intersect(test, isl_set_copy(build->domain)); + empty = isl_set_is_empty(test); + isl_set_free(test); + + return empty; +} + +/* Does the dimension at (internal) position "pos" have a non-trivial stride? + */ +isl_bool isl_ast_build_has_stride(__isl_keep isl_ast_build *build, int pos) +{ + isl_val *v; + isl_bool has_stride; + + if (!build) + return isl_bool_error; + + v = isl_vec_get_element_val(build->strides, pos); + has_stride = isl_bool_not(isl_val_is_one(v)); + isl_val_free(v); + + return has_stride; +} + +/* Given that the dimension at position "pos" takes on values + * + * f + s a + * + * with a an integer, return s through *stride. + */ +__isl_give isl_val *isl_ast_build_get_stride(__isl_keep isl_ast_build *build, + int pos) +{ + if (!build) + return NULL; + + return isl_vec_get_element_val(build->strides, pos); +} + +/* Given that the dimension at position "pos" takes on values + * + * f + s a + * + * with a an integer, return f. + */ +__isl_give isl_aff *isl_ast_build_get_offset( + __isl_keep isl_ast_build *build, int pos) +{ + if (!build) + return NULL; + + return isl_multi_aff_get_aff(build->offsets, pos); +} + +/* Is the dimension at position "pos" known to attain only a single + * value that, moreover, can be described by a single affine expression + * in terms of the outer dimensions and parameters? + * + * If not, then the corresponding affine expression in build->values + * is set to be equal to the same input dimension. + * Otherwise, it is set to the requested expression in terms of + * outer dimensions and parameters. + */ +int isl_ast_build_has_affine_value(__isl_keep isl_ast_build *build, + int pos) +{ + isl_aff *aff; + int involves; + + if (!build) + return -1; + + aff = isl_multi_aff_get_aff(build->values, pos); + involves = isl_aff_involves_dims(aff, isl_dim_in, pos, 1); + isl_aff_free(aff); + + if (involves < 0) + return -1; + + return !involves; +} + +/* Plug in the known values (fixed affine expressions in terms of + * parameters and outer loop iterators) of all loop iterators + * in the domain of "umap". + * + * We simply precompose "umap" with build->values. + */ +__isl_give isl_union_map *isl_ast_build_substitute_values_union_map_domain( + __isl_keep isl_ast_build *build, __isl_take isl_union_map *umap) +{ + isl_multi_aff *values; + + if (!build) + return isl_union_map_free(umap); + + values = isl_multi_aff_copy(build->values); + umap = isl_union_map_preimage_domain_multi_aff(umap, values); + + return umap; +} + +/* Is the current dimension known to attain only a single value? + */ +int isl_ast_build_has_value(__isl_keep isl_ast_build *build) +{ + if (!build) + return -1; + + return build->value != NULL; +} + +/* Simplify the basic set "bset" based on what we know about + * the iterators of already generated loops. + * + * "bset" is assumed to live in the (internal) schedule domain. + */ +__isl_give isl_basic_set *isl_ast_build_compute_gist_basic_set( + __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset) +{ + if (!build) + goto error; + + bset = isl_basic_set_preimage_multi_aff(bset, + isl_multi_aff_copy(build->values)); + bset = isl_basic_set_gist(bset, + isl_set_simple_hull(isl_set_copy(build->domain))); + + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Simplify the set "set" based on what we know about + * the iterators of already generated loops. + * + * "set" is assumed to live in the (internal) schedule domain. + */ +__isl_give isl_set *isl_ast_build_compute_gist( + __isl_keep isl_ast_build *build, __isl_take isl_set *set) +{ + if (!build) + goto error; + + if (!isl_set_is_params(set)) + set = isl_set_preimage_multi_aff(set, + isl_multi_aff_copy(build->values)); + set = isl_set_gist(set, isl_set_copy(build->domain)); + + return set; +error: + isl_set_free(set); + return NULL; +} + +/* Include information about what we know about the iterators of + * already generated loops to "set". + * + * We currently only plug in the known affine values of outer loop + * iterators. + * In principle we could also introduce equalities or even other + * constraints implied by the intersection of "set" and build->domain. + */ +__isl_give isl_set *isl_ast_build_specialize(__isl_keep isl_ast_build *build, + __isl_take isl_set *set) +{ + if (!build) + return isl_set_free(set); + + return isl_set_preimage_multi_aff(set, + isl_multi_aff_copy(build->values)); +} + +/* Plug in the known affine values of outer loop iterators in "bset". + */ +__isl_give isl_basic_set *isl_ast_build_specialize_basic_set( + __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset) +{ + if (!build) + return isl_basic_set_free(bset); + + return isl_basic_set_preimage_multi_aff(bset, + isl_multi_aff_copy(build->values)); +} + +/* Simplify the map "map" based on what we know about + * the iterators of already generated loops. + * + * The domain of "map" is assumed to live in the (internal) schedule domain. + */ +__isl_give isl_map *isl_ast_build_compute_gist_map_domain( + __isl_keep isl_ast_build *build, __isl_take isl_map *map) +{ + if (!build) + goto error; + + map = isl_map_gist_domain(map, isl_set_copy(build->domain)); + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Simplify the affine expression "aff" based on what we know about + * the iterators of already generated loops. + * + * The domain of "aff" is assumed to live in the (internal) schedule domain. + */ +__isl_give isl_aff *isl_ast_build_compute_gist_aff( + __isl_keep isl_ast_build *build, __isl_take isl_aff *aff) +{ + if (!build) + goto error; + + aff = isl_aff_gist(aff, isl_set_copy(build->domain)); + + return aff; +error: + isl_aff_free(aff); + return NULL; +} + +/* Simplify the piecewise affine expression "aff" based on what we know about + * the iterators of already generated loops. + * + * The domain of "pa" is assumed to live in the (internal) schedule domain. + */ +__isl_give isl_pw_aff *isl_ast_build_compute_gist_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa) +{ + if (!build) + goto error; + + if (!isl_set_is_params(build->domain)) + pa = isl_pw_aff_pullback_multi_aff(pa, + isl_multi_aff_copy(build->values)); + pa = isl_pw_aff_gist(pa, isl_set_copy(build->domain)); + + return pa; +error: + isl_pw_aff_free(pa); + return NULL; +} + +/* Simplify the piecewise multi-affine expression "aff" based on what + * we know about the iterators of already generated loops. + * + * The domain of "pma" is assumed to live in the (internal) schedule domain. + */ +__isl_give isl_pw_multi_aff *isl_ast_build_compute_gist_pw_multi_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma) +{ + if (!build) + goto error; + + pma = isl_pw_multi_aff_pullback_multi_aff(pma, + isl_multi_aff_copy(build->values)); + pma = isl_pw_multi_aff_gist(pma, isl_set_copy(build->domain)); + + return pma; +error: + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Extract the schedule domain of the given type from build->options + * at the current depth. + * + * In particular, find the subset of build->options that is of + * the following form + * + * schedule_domain -> type[depth] + * + * and return the corresponding domain, after eliminating inner dimensions + * and divs that depend on the current dimension. + * + * Note that the domain of build->options has been reformulated + * in terms of the internal build space in embed_options, + * but the position is still that within the current code generation. + */ +__isl_give isl_set *isl_ast_build_get_option_domain( + __isl_keep isl_ast_build *build, enum isl_ast_loop_type type) +{ + const char *name; + isl_space *space; + isl_map *option; + isl_set *domain; + int local_pos; + + if (!build) + return NULL; + + name = option_str[type]; + local_pos = build->depth - build->outer_pos; + + space = isl_ast_build_get_space(build, 1); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, 1); + space = isl_space_set_tuple_name(space, isl_dim_out, name); + + option = isl_union_map_extract_map(build->options, space); + option = isl_map_fix_si(option, isl_dim_out, 0, local_pos); + + domain = isl_map_domain(option); + domain = isl_ast_build_eliminate(build, domain); + + return domain; +} + +/* How does the user want the current schedule dimension to be generated? + * These choices have been extracted from the schedule node + * in extract_loop_types and stored in build->loop_type. + * They have been updated to reflect any dimension insertion in + * node_insert_dim. + * Return isl_ast_domain_error on error. + * + * If "isolated" is set, then we get the loop AST generation type + * directly from the band node since node_insert_dim cannot have been + * called on a band with the isolate option. + */ +enum isl_ast_loop_type isl_ast_build_get_loop_type( + __isl_keep isl_ast_build *build, int isolated) +{ + int local_pos; + isl_ctx *ctx; + + if (!build) + return isl_ast_loop_error; + ctx = isl_ast_build_get_ctx(build); + if (!build->node) + isl_die(ctx, isl_error_internal, + "only works for schedule tree based AST generation", + return isl_ast_loop_error); + + local_pos = build->depth - build->outer_pos; + if (!isolated) + return build->loop_type[local_pos]; + return isl_schedule_node_band_member_get_isolate_ast_loop_type( + build->node, local_pos); +} + +/* Extract the isolated set from the isolate option, if any, + * and store in the build. + * If there is no isolate option, then the isolated set is + * set to the empty set. + * + * The isolate option is of the form + * + * isolate[[outer bands] -> current_band] + * + * We flatten this set and then map it back to the internal + * schedule space. + * + * If we have already extracted the isolated set + * or if internal2input is no longer set, then we do not + * need to do anything. In the latter case, we know + * that the current band cannot have any isolate option. + */ +__isl_give isl_ast_build *isl_ast_build_extract_isolated( + __isl_take isl_ast_build *build) +{ + isl_set *isolated; + + if (!build) + return NULL; + if (!build->internal2input) + return build; + if (build->isolated) + return build; + + build = isl_ast_build_cow(build); + if (!build) + return NULL; + + isolated = isl_schedule_node_band_get_ast_isolate_option(build->node); + isolated = isl_set_flatten(isolated); + isolated = isl_set_preimage_multi_aff(isolated, + isl_multi_aff_copy(build->internal2input)); + + build->isolated = isolated; + if (!build->isolated) + return isl_ast_build_free(build); + + return build; +} + +/* Does "build" have a non-empty isolated set? + * + * The caller is assumed to have called isl_ast_build_extract_isolated first. + */ +int isl_ast_build_has_isolated(__isl_keep isl_ast_build *build) +{ + int empty; + + if (!build) + return -1; + if (!build->internal2input) + return 0; + if (!build->isolated) + isl_die(isl_ast_build_get_ctx(build), isl_error_internal, + "isolated set not extracted yet", return -1); + + empty = isl_set_plain_is_empty(build->isolated); + return empty < 0 ? -1 : !empty; +} + +/* Return a copy of the isolated set of "build". + * + * The caller is assume to have called isl_ast_build_has_isolated first, + * with this function returning true. + * In particular, this function should not be called if we are no + * longer keeping track of internal2input (and there therefore could + * not possibly be any isolated set). + */ +__isl_give isl_set *isl_ast_build_get_isolated(__isl_keep isl_ast_build *build) +{ + if (!build) + return NULL; + if (!build->internal2input) + isl_die(isl_ast_build_get_ctx(build), isl_error_internal, + "build cannot have isolated set", return NULL); + + return isl_set_copy(build->isolated); +} + +/* Extract the separation class mapping at the current depth. + * + * In particular, find and return the subset of build->options that is of + * the following form + * + * schedule_domain -> separation_class[[depth] -> [class]] + * + * The caller is expected to eliminate inner dimensions from the domain. + * + * Note that the domain of build->options has been reformulated + * in terms of the internal build space in embed_options, + * but the position is still that within the current code generation. + */ +__isl_give isl_map *isl_ast_build_get_separation_class( + __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_space *space_sep, *space; + isl_map *res; + int local_pos; + + if (!build) + return NULL; + + local_pos = build->depth - build->outer_pos; + ctx = isl_ast_build_get_ctx(build); + space_sep = isl_space_alloc(ctx, 0, 1, 1); + space_sep = isl_space_wrap(space_sep); + space_sep = isl_space_set_tuple_name(space_sep, isl_dim_set, + "separation_class"); + space = isl_ast_build_get_space(build, 1); + space_sep = isl_space_align_params(space_sep, isl_space_copy(space)); + space = isl_space_map_from_domain_and_range(space, space_sep); + + res = isl_union_map_extract_map(build->options, space); + res = isl_map_fix_si(res, isl_dim_out, 0, local_pos); + res = isl_map_coalesce(res); + + return res; +} + +/* Eliminate dimensions inner to the current dimension. + */ +__isl_give isl_set *isl_ast_build_eliminate_inner( + __isl_keep isl_ast_build *build, __isl_take isl_set *set) +{ + int dim; + int depth; + + if (!build) + return isl_set_free(set); + + dim = isl_set_dim(set, isl_dim_set); + depth = build->depth; + set = isl_set_detect_equalities(set); + set = isl_set_eliminate(set, isl_dim_set, depth + 1, dim - (depth + 1)); + + return set; +} + +/* Eliminate unknown divs and divs that depend on the current dimension. + * + * Note that during the elimination of unknown divs, we may discover + * an explicit representation of some other unknown divs, which may + * depend on the current dimension. We therefore need to eliminate + * unknown divs first. + */ +__isl_give isl_set *isl_ast_build_eliminate_divs( + __isl_keep isl_ast_build *build, __isl_take isl_set *set) +{ + int depth; + + if (!build) + return isl_set_free(set); + + set = isl_set_remove_unknown_divs(set); + depth = build->depth; + set = isl_set_remove_divs_involving_dims(set, isl_dim_set, depth, 1); + + return set; +} + +/* Eliminate dimensions inner to the current dimension as well as + * unknown divs and divs that depend on the current dimension. + * The result then consists only of constraints that are independent + * of the current dimension and upper and lower bounds on the current + * dimension. + */ +__isl_give isl_set *isl_ast_build_eliminate( + __isl_keep isl_ast_build *build, __isl_take isl_set *domain) +{ + domain = isl_ast_build_eliminate_inner(build, domain); + domain = isl_ast_build_eliminate_divs(build, domain); + return domain; +} + +/* Replace build->single_valued by "sv". + */ +__isl_give isl_ast_build *isl_ast_build_set_single_valued( + __isl_take isl_ast_build *build, int sv) +{ + if (!build) + return build; + if (build->single_valued == sv) + return build; + build = isl_ast_build_cow(build); + if (!build) + return build; + build->single_valued = sv; + + return build; +} Index: lib/Analysis/isl/isl_ast_build_expr.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_build_expr.h @@ -0,0 +1,22 @@ +#ifndef ISL_AST_BUILD_EXPR_PRIVATE_H +#define ISL_AST_BUILD_EXPR_PRIVATE_H + +#include +#include + +__isl_give isl_ast_expr *isl_ast_build_expr_from_basic_set( + __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset); +__isl_give isl_ast_expr *isl_ast_build_expr_from_set_internal( + __isl_keep isl_ast_build *build, __isl_take isl_set *set); + +__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff_internal( + __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa); +__isl_give isl_ast_expr *isl_ast_expr_from_aff(__isl_take isl_aff *aff, + __isl_keep isl_ast_build *build); +__isl_give isl_ast_expr *isl_ast_expr_set_op_arg(__isl_take isl_ast_expr *expr, + int pos, __isl_take isl_ast_expr *arg); + +__isl_give isl_ast_node *isl_ast_build_call_from_executed( + __isl_keep isl_ast_build *build, __isl_take isl_map *executed); + +#endif Index: lib/Analysis/isl/isl_ast_build_expr.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_build_expr.c @@ -0,0 +1,2446 @@ +/* + * Copyright 2012-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include + +/* Compute the "opposite" of the (numerator of the) argument of a div + * with denominator "d". + * + * In particular, compute + * + * -aff + (d - 1) + */ +static __isl_give isl_aff *oppose_div_arg(__isl_take isl_aff *aff, + __isl_take isl_val *d) +{ + aff = isl_aff_neg(aff); + aff = isl_aff_add_constant_val(aff, d); + aff = isl_aff_add_constant_si(aff, -1); + + return aff; +} + +/* Internal data structure used inside isl_ast_expr_add_term. + * The domain of "build" is used to simplify the expressions. + * "build" needs to be set by the caller of isl_ast_expr_add_term. + * "cst" is the constant term of the expression in which the added term + * appears. It may be modified by isl_ast_expr_add_term. + * + * "v" is the coefficient of the term that is being constructed and + * is set internally by isl_ast_expr_add_term. + */ +struct isl_ast_add_term_data { + isl_ast_build *build; + isl_val *cst; + isl_val *v; +}; + +/* Given the numerator "aff" of the argument of an integer division + * with denominator "d", check if it can be made non-negative over + * data->build->domain by stealing part of the constant term of + * the expression in which the integer division appears. + * + * In particular, the outer expression is of the form + * + * v * floor(aff/d) + cst + * + * We already know that "aff" itself may attain negative values. + * Here we check if aff + d*floor(cst/v) is non-negative, such + * that we could rewrite the expression to + * + * v * floor((aff + d*floor(cst/v))/d) + cst - v*floor(cst/v) + * + * Note that aff + d*floor(cst/v) can only possibly be non-negative + * if data->cst and data->v have the same sign. + * Similarly, if floor(cst/v) is zero, then there is no point in + * checking again. + */ +static int is_non_neg_after_stealing(__isl_keep isl_aff *aff, + __isl_keep isl_val *d, struct isl_ast_add_term_data *data) +{ + isl_aff *shifted; + isl_val *shift; + int is_zero; + int non_neg; + + if (isl_val_sgn(data->cst) != isl_val_sgn(data->v)) + return 0; + + shift = isl_val_div(isl_val_copy(data->cst), isl_val_copy(data->v)); + shift = isl_val_floor(shift); + is_zero = isl_val_is_zero(shift); + if (is_zero < 0 || is_zero) { + isl_val_free(shift); + return is_zero < 0 ? -1 : 0; + } + shift = isl_val_mul(shift, isl_val_copy(d)); + shifted = isl_aff_copy(aff); + shifted = isl_aff_add_constant_val(shifted, shift); + non_neg = isl_ast_build_aff_is_nonneg(data->build, shifted); + isl_aff_free(shifted); + + return non_neg; +} + +/* Given the numerator "aff' of the argument of an integer division + * with denominator "d", steal part of the constant term of + * the expression in which the integer division appears to make it + * non-negative over data->build->domain. + * + * In particular, the outer expression is of the form + * + * v * floor(aff/d) + cst + * + * We know that "aff" itself may attain negative values, + * but that aff + d*floor(cst/v) is non-negative. + * Find the minimal positive value that we need to add to "aff" + * to make it positive and adjust data->cst accordingly. + * That is, compute the minimal value "m" of "aff" over + * data->build->domain and take + * + * s = ceil(m/d) + * + * such that + * + * aff + d * s >= 0 + * + * and rewrite the expression to + * + * v * floor((aff + s*d)/d) + (cst - v*s) + */ +static __isl_give isl_aff *steal_from_cst(__isl_take isl_aff *aff, + __isl_keep isl_val *d, struct isl_ast_add_term_data *data) +{ + isl_set *domain; + isl_val *shift, *t; + + domain = isl_ast_build_get_domain(data->build); + shift = isl_set_min_val(domain, aff); + isl_set_free(domain); + + shift = isl_val_neg(shift); + shift = isl_val_div(shift, isl_val_copy(d)); + shift = isl_val_ceil(shift); + + t = isl_val_copy(shift); + t = isl_val_mul(t, isl_val_copy(data->v)); + data->cst = isl_val_sub(data->cst, t); + + shift = isl_val_mul(shift, isl_val_copy(d)); + return isl_aff_add_constant_val(aff, shift); +} + +/* Create an isl_ast_expr evaluating the div at position "pos" in "ls". + * The result is simplified in terms of data->build->domain. + * This function may change (the sign of) data->v. + * + * "ls" is known to be non-NULL. + * + * Let the div be of the form floor(e/d). + * If the ast_build_prefer_pdiv option is set then we check if "e" + * is non-negative, so that we can generate + * + * (pdiv_q, expr(e), expr(d)) + * + * instead of + * + * (fdiv_q, expr(e), expr(d)) + * + * If the ast_build_prefer_pdiv option is set and + * if "e" is not non-negative, then we check if "-e + d - 1" is non-negative. + * If so, we can rewrite + * + * floor(e/d) = -ceil(-e/d) = -floor((-e + d - 1)/d) + * + * and still use pdiv_q, while changing the sign of data->v. + * + * Otherwise, we check if + * + * e + d*floor(cst/v) + * + * is non-negative and if so, replace floor(e/d) by + * + * floor((e + s*d)/d) - s + * + * with s the minimal shift that makes the argument non-negative. + */ +static __isl_give isl_ast_expr *var_div(struct isl_ast_add_term_data *data, + __isl_keep isl_local_space *ls, int pos) +{ + isl_ctx *ctx = isl_local_space_get_ctx(ls); + isl_aff *aff; + isl_ast_expr *num, *den; + isl_val *d; + enum isl_ast_op_type type; + + aff = isl_local_space_get_div(ls, pos); + d = isl_aff_get_denominator_val(aff); + aff = isl_aff_scale_val(aff, isl_val_copy(d)); + den = isl_ast_expr_from_val(isl_val_copy(d)); + + type = isl_ast_op_fdiv_q; + if (isl_options_get_ast_build_prefer_pdiv(ctx)) { + int non_neg = isl_ast_build_aff_is_nonneg(data->build, aff); + if (non_neg >= 0 && !non_neg) { + isl_aff *opp = oppose_div_arg(isl_aff_copy(aff), + isl_val_copy(d)); + non_neg = isl_ast_build_aff_is_nonneg(data->build, opp); + if (non_neg >= 0 && non_neg) { + data->v = isl_val_neg(data->v); + isl_aff_free(aff); + aff = opp; + } else + isl_aff_free(opp); + } + if (non_neg >= 0 && !non_neg) { + non_neg = is_non_neg_after_stealing(aff, d, data); + if (non_neg >= 0 && non_neg) + aff = steal_from_cst(aff, d, data); + } + if (non_neg < 0) + aff = isl_aff_free(aff); + else if (non_neg) + type = isl_ast_op_pdiv_q; + } + + isl_val_free(d); + num = isl_ast_expr_from_aff(aff, data->build); + return isl_ast_expr_alloc_binary(type, num, den); +} + +/* Create an isl_ast_expr evaluating the specified dimension of "ls". + * The result is simplified in terms of data->build->domain. + * This function may change (the sign of) data->v. + * + * The isl_ast_expr is constructed based on the type of the dimension. + * - divs are constructed by var_div + * - set variables are constructed from the iterator isl_ids in data->build + * - parameters are constructed from the isl_ids in "ls" + */ +static __isl_give isl_ast_expr *var(struct isl_ast_add_term_data *data, + __isl_keep isl_local_space *ls, enum isl_dim_type type, int pos) +{ + isl_ctx *ctx = isl_local_space_get_ctx(ls); + isl_id *id; + + if (type == isl_dim_div) + return var_div(data, ls, pos); + + if (type == isl_dim_set) { + id = isl_ast_build_get_iterator_id(data->build, pos); + return isl_ast_expr_from_id(id); + } + + if (!isl_local_space_has_dim_id(ls, type, pos)) + isl_die(ctx, isl_error_internal, "unnamed dimension", + return NULL); + id = isl_local_space_get_dim_id(ls, type, pos); + return isl_ast_expr_from_id(id); +} + +/* Does "expr" represent the zero integer? + */ +static int ast_expr_is_zero(__isl_keep isl_ast_expr *expr) +{ + if (!expr) + return -1; + if (expr->type != isl_ast_expr_int) + return 0; + return isl_val_is_zero(expr->u.v); +} + +/* Create an expression representing the sum of "expr1" and "expr2", + * provided neither of the two expressions is identically zero. + */ +static __isl_give isl_ast_expr *ast_expr_add(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + if (!expr1 || !expr2) + goto error; + + if (ast_expr_is_zero(expr1)) { + isl_ast_expr_free(expr1); + return expr2; + } + + if (ast_expr_is_zero(expr2)) { + isl_ast_expr_free(expr2); + return expr1; + } + + return isl_ast_expr_add(expr1, expr2); +error: + isl_ast_expr_free(expr1); + isl_ast_expr_free(expr2); + return NULL; +} + +/* Subtract expr2 from expr1. + * + * If expr2 is zero, we simply return expr1. + * If expr1 is zero, we return + * + * (isl_ast_op_minus, expr2) + * + * Otherwise, we return + * + * (isl_ast_op_sub, expr1, expr2) + */ +static __isl_give isl_ast_expr *ast_expr_sub(__isl_take isl_ast_expr *expr1, + __isl_take isl_ast_expr *expr2) +{ + if (!expr1 || !expr2) + goto error; + + if (ast_expr_is_zero(expr2)) { + isl_ast_expr_free(expr2); + return expr1; + } + + if (ast_expr_is_zero(expr1)) { + isl_ast_expr_free(expr1); + return isl_ast_expr_neg(expr2); + } + + return isl_ast_expr_sub(expr1, expr2); +error: + isl_ast_expr_free(expr1); + isl_ast_expr_free(expr2); + return NULL; +} + +/* Return an isl_ast_expr that represents + * + * v * (aff mod d) + * + * v is assumed to be non-negative. + * The result is simplified in terms of build->domain. + */ +static __isl_give isl_ast_expr *isl_ast_expr_mod(__isl_keep isl_val *v, + __isl_keep isl_aff *aff, __isl_keep isl_val *d, + __isl_keep isl_ast_build *build) +{ + isl_ast_expr *expr; + isl_ast_expr *c; + + if (!aff) + return NULL; + + expr = isl_ast_expr_from_aff(isl_aff_copy(aff), build); + + c = isl_ast_expr_from_val(isl_val_copy(d)); + expr = isl_ast_expr_alloc_binary(isl_ast_op_pdiv_r, expr, c); + + if (!isl_val_is_one(v)) { + c = isl_ast_expr_from_val(isl_val_copy(v)); + expr = isl_ast_expr_mul(c, expr); + } + + return expr; +} + +/* Create an isl_ast_expr that scales "expr" by "v". + * + * If v is 1, we simply return expr. + * If v is -1, we return + * + * (isl_ast_op_minus, expr) + * + * Otherwise, we return + * + * (isl_ast_op_mul, expr(v), expr) + */ +static __isl_give isl_ast_expr *scale(__isl_take isl_ast_expr *expr, + __isl_take isl_val *v) +{ + isl_ast_expr *c; + + if (!expr || !v) + goto error; + if (isl_val_is_one(v)) { + isl_val_free(v); + return expr; + } + + if (isl_val_is_negone(v)) { + isl_val_free(v); + expr = isl_ast_expr_neg(expr); + } else { + c = isl_ast_expr_from_val(v); + expr = isl_ast_expr_mul(c, expr); + } + + return expr; +error: + isl_val_free(v); + isl_ast_expr_free(expr); + return NULL; +} + +/* Add an expression for "*v" times the specified dimension of "ls" + * to expr. + * If the dimension is an integer division, then this function + * may modify data->cst in order to make the numerator non-negative. + * The result is simplified in terms of data->build->domain. + * + * Let e be the expression for the specified dimension, + * multiplied by the absolute value of "*v". + * If "*v" is negative, we create + * + * (isl_ast_op_sub, expr, e) + * + * except when expr is trivially zero, in which case we create + * + * (isl_ast_op_minus, e) + * + * instead. + * + * If "*v" is positive, we simply create + * + * (isl_ast_op_add, expr, e) + * + */ +static __isl_give isl_ast_expr *isl_ast_expr_add_term( + __isl_take isl_ast_expr *expr, + __isl_keep isl_local_space *ls, enum isl_dim_type type, int pos, + __isl_take isl_val *v, struct isl_ast_add_term_data *data) +{ + isl_ast_expr *term; + + if (!expr) + return NULL; + + data->v = v; + term = var(data, ls, type, pos); + v = data->v; + + if (isl_val_is_neg(v) && !ast_expr_is_zero(expr)) { + v = isl_val_neg(v); + term = scale(term, v); + return ast_expr_sub(expr, term); + } else { + term = scale(term, v); + return ast_expr_add(expr, term); + } +} + +/* Add an expression for "v" to expr. + */ +static __isl_give isl_ast_expr *isl_ast_expr_add_int( + __isl_take isl_ast_expr *expr, __isl_take isl_val *v) +{ + isl_ast_expr *expr_int; + + if (!expr || !v) + goto error; + + if (isl_val_is_zero(v)) { + isl_val_free(v); + return expr; + } + + if (isl_val_is_neg(v) && !ast_expr_is_zero(expr)) { + v = isl_val_neg(v); + expr_int = isl_ast_expr_from_val(v); + return ast_expr_sub(expr, expr_int); + } else { + expr_int = isl_ast_expr_from_val(v); + return ast_expr_add(expr, expr_int); + } +error: + isl_ast_expr_free(expr); + isl_val_free(v); + return NULL; +} + +/* Internal data structure used inside extract_modulos. + * + * If any modulo expressions are detected in "aff", then the + * expression is removed from "aff" and added to either "pos" or "neg" + * depending on the sign of the coefficient of the modulo expression + * inside "aff". + * + * "add" is an expression that needs to be added to "aff" at the end of + * the computation. It is NULL as long as no modulos have been extracted. + * + * "i" is the position in "aff" of the div under investigation + * "v" is the coefficient in "aff" of the div + * "div" is the argument of the div, with the denominator removed + * "d" is the original denominator of the argument of the div + * + * "nonneg" is an affine expression that is non-negative over "build" + * and that can be used to extract a modulo expression from "div". + * In particular, if "sign" is 1, then the coefficients of "nonneg" + * are equal to those of "div" modulo "d". If "sign" is -1, then + * the coefficients of "nonneg" are opposite to those of "div" modulo "d". + * If "sign" is 0, then no such affine expression has been found (yet). + */ +struct isl_extract_mod_data { + isl_ast_build *build; + isl_aff *aff; + + isl_ast_expr *pos; + isl_ast_expr *neg; + + isl_aff *add; + + int i; + isl_val *v; + isl_val *d; + isl_aff *div; + + isl_aff *nonneg; + int sign; +}; + +/* Given that data->v * div_i in data->aff is equal to + * + * f * (term - (arg mod d)) + * + * with data->d * f = data->v, add + * + * f * term + * + * to data->add and + * + * abs(f) * (arg mod d) + * + * to data->neg or data->pos depending on the sign of -f. + */ +static int extract_term_and_mod(struct isl_extract_mod_data *data, + __isl_take isl_aff *term, __isl_take isl_aff *arg) +{ + isl_ast_expr *expr; + int s; + + data->v = isl_val_div(data->v, isl_val_copy(data->d)); + s = isl_val_sgn(data->v); + data->v = isl_val_abs(data->v); + expr = isl_ast_expr_mod(data->v, arg, data->d, data->build); + isl_aff_free(arg); + if (s > 0) + data->neg = ast_expr_add(data->neg, expr); + else + data->pos = ast_expr_add(data->pos, expr); + data->aff = isl_aff_set_coefficient_si(data->aff, + isl_dim_div, data->i, 0); + if (s < 0) + data->v = isl_val_neg(data->v); + term = isl_aff_scale_val(data->div, isl_val_copy(data->v)); + + if (!data->add) + data->add = term; + else + data->add = isl_aff_add(data->add, term); + if (!data->add) + return -1; + + return 0; +} + +/* Given that data->v * div_i in data->aff is of the form + * + * f * d * floor(div/d) + * + * with div nonnegative on data->build, rewrite it as + * + * f * (div - (div mod d)) = f * div - f * (div mod d) + * + * and add + * + * f * div + * + * to data->add and + * + * abs(f) * (div mod d) + * + * to data->neg or data->pos depending on the sign of -f. + */ +static int extract_mod(struct isl_extract_mod_data *data) +{ + return extract_term_and_mod(data, isl_aff_copy(data->div), + isl_aff_copy(data->div)); +} + +/* Given that data->v * div_i in data->aff is of the form + * + * f * d * floor(div/d) (1) + * + * check if div is non-negative on data->build and, if so, + * extract the corresponding modulo from data->aff. + * If not, then check if + * + * -div + d - 1 + * + * is non-negative on data->build. If so, replace (1) by + * + * -f * d * floor((-div + d - 1)/d) + * + * and extract the corresponding modulo from data->aff. + * + * This function may modify data->div. + */ +static int extract_nonneg_mod(struct isl_extract_mod_data *data) +{ + int mod; + + mod = isl_ast_build_aff_is_nonneg(data->build, data->div); + if (mod < 0) + goto error; + if (mod) + return extract_mod(data); + + data->div = oppose_div_arg(data->div, isl_val_copy(data->d)); + mod = isl_ast_build_aff_is_nonneg(data->build, data->div); + if (mod < 0) + goto error; + if (mod) { + data->v = isl_val_neg(data->v); + return extract_mod(data); + } + + return 0; +error: + data->aff = isl_aff_free(data->aff); + return -1; +} + +/* Is the affine expression of constraint "c" "simpler" than data->nonneg + * for use in extracting a modulo expression? + * + * We currently only consider the constant term of the affine expression. + * In particular, we prefer the affine expression with the smallest constant + * term. + * This means that if there are two constraints, say x >= 0 and -x + 10 >= 0, + * then we would pick x >= 0 + * + * More detailed heuristics could be used if it turns out that there is a need. + */ +static int mod_constraint_is_simpler(struct isl_extract_mod_data *data, + __isl_keep isl_constraint *c) +{ + isl_val *v1, *v2; + int simpler; + + if (!data->nonneg) + return 1; + + v1 = isl_val_abs(isl_constraint_get_constant_val(c)); + v2 = isl_val_abs(isl_aff_get_constant_val(data->nonneg)); + simpler = isl_val_lt(v1, v2); + isl_val_free(v1); + isl_val_free(v2); + + return simpler; +} + +/* Check if the coefficients of "c" are either equal or opposite to those + * of data->div modulo data->d. If so, and if "c" is "simpler" than + * data->nonneg, then replace data->nonneg by the affine expression of "c" + * and set data->sign accordingly. + * + * Both "c" and data->div are assumed not to involve any integer divisions. + * + * Before we start the actual comparison, we first quickly check if + * "c" and data->div have the same non-zero coefficients. + * If not, then we assume that "c" is not of the desired form. + * Note that while the coefficients of data->div can be reasonably expected + * not to involve any coefficients that are multiples of d, "c" may + * very well involve such coefficients. This means that we may actually + * miss some cases. + * + * If the constant term is "too large", then the constraint is rejected, + * where "too large" is fairly arbitrarily set to 1 << 15. + * We do this to avoid picking up constraints that bound a variable + * by a very large number, say the largest or smallest possible + * variable in the representation of some integer type. + */ +static isl_stat check_parallel_or_opposite(__isl_take isl_constraint *c, + void *user) +{ + struct isl_extract_mod_data *data = user; + enum isl_dim_type c_type[2] = { isl_dim_param, isl_dim_set }; + enum isl_dim_type a_type[2] = { isl_dim_param, isl_dim_in }; + int i, t; + int n[2]; + int parallel = 1, opposite = 1; + + for (t = 0; t < 2; ++t) { + n[t] = isl_constraint_dim(c, c_type[t]); + for (i = 0; i < n[t]; ++i) { + int a, b; + + a = isl_constraint_involves_dims(c, c_type[t], i, 1); + b = isl_aff_involves_dims(data->div, a_type[t], i, 1); + if (a != b) + parallel = opposite = 0; + } + } + + if (parallel || opposite) { + isl_val *v; + + v = isl_val_abs(isl_constraint_get_constant_val(c)); + if (isl_val_cmp_si(v, 1 << 15) > 0) + parallel = opposite = 0; + isl_val_free(v); + } + + for (t = 0; t < 2; ++t) { + for (i = 0; i < n[t]; ++i) { + isl_val *v1, *v2; + + if (!parallel && !opposite) + break; + v1 = isl_constraint_get_coefficient_val(c, + c_type[t], i); + v2 = isl_aff_get_coefficient_val(data->div, + a_type[t], i); + if (parallel) { + v1 = isl_val_sub(v1, isl_val_copy(v2)); + parallel = isl_val_is_divisible_by(v1, data->d); + v1 = isl_val_add(v1, isl_val_copy(v2)); + } + if (opposite) { + v1 = isl_val_add(v1, isl_val_copy(v2)); + opposite = isl_val_is_divisible_by(v1, data->d); + } + isl_val_free(v1); + isl_val_free(v2); + } + } + + if ((parallel || opposite) && mod_constraint_is_simpler(data, c)) { + isl_aff_free(data->nonneg); + data->nonneg = isl_constraint_get_aff(c); + data->sign = parallel ? 1 : -1; + } + + isl_constraint_free(c); + + if (data->sign != 0 && data->nonneg == NULL) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Given that data->v * div_i in data->aff is of the form + * + * f * d * floor(div/d) (1) + * + * see if we can find an expression div' that is non-negative over data->build + * and that is related to div through + * + * div' = div + d * e + * + * or + * + * div' = -div + d - 1 + d * e + * + * with e some affine expression. + * If so, we write (1) as + * + * f * div + f * (div' mod d) + * + * or + * + * -f * (-div + d - 1) - f * (div' mod d) + * + * exploiting (in the second case) the fact that + * + * f * d * floor(div/d) = -f * d * floor((-div + d - 1)/d) + * + * + * We first try to find an appropriate expression for div' + * from the constraints of data->build->domain (which is therefore + * guaranteed to be non-negative on data->build), where we remove + * any integer divisions from the constraints and skip this step + * if "div" itself involves any integer divisions. + * If we cannot find an appropriate expression this way, then + * we pass control to extract_nonneg_mod where check + * if div or "-div + d -1" themselves happen to be + * non-negative on data->build. + * + * While looking for an appropriate constraint in data->build->domain, + * we ignore the constant term, so after finding such a constraint, + * we still need to fix up the constant term. + * In particular, if a is the constant term of "div" + * (or d - 1 - the constant term of "div" if data->sign < 0) + * and b is the constant term of the constraint, then we need to find + * a non-negative constant c such that + * + * b + c \equiv a mod d + * + * We therefore take + * + * c = (a - b) mod d + * + * and add it to b to obtain the constant term of div'. + * If this constant term is "too negative", then we add an appropriate + * multiple of d to make it positive. + * + * + * Note that the above is a only a very simple heuristic for finding an + * appropriate expression. We could try a bit harder by also considering + * sums of constraints that involve disjoint sets of variables or + * we could consider arbitrary linear combinations of constraints, + * although that could potentially be much more expensive as it involves + * the solution of an LP problem. + * + * In particular, if v_i is a column vector representing constraint i, + * w represents div and e_i is the i-th unit vector, then we are looking + * for a solution of the constraints + * + * \sum_i lambda_i v_i = w + \sum_i alpha_i d e_i + * + * with \lambda_i >= 0 and alpha_i of unrestricted sign. + * If we are not just interested in a non-negative expression, but + * also in one with a minimal range, then we don't just want + * c = \sum_i lambda_i v_i to be non-negative over the domain, + * but also beta - c = \sum_i mu_i v_i, where beta is a scalar + * that we want to minimize and we now also have to take into account + * the constant terms of the constraints. + * Alternatively, we could first compute the dual of the domain + * and plug in the constraints on the coefficients. + */ +static int try_extract_mod(struct isl_extract_mod_data *data) +{ + isl_basic_set *hull; + isl_val *v1, *v2; + int r, n; + + if (!data->build) + goto error; + + n = isl_aff_dim(data->div, isl_dim_div); + + if (isl_aff_involves_dims(data->div, isl_dim_div, 0, n)) + return extract_nonneg_mod(data); + + hull = isl_set_simple_hull(isl_set_copy(data->build->domain)); + hull = isl_basic_set_remove_divs(hull); + data->sign = 0; + data->nonneg = NULL; + r = isl_basic_set_foreach_constraint(hull, &check_parallel_or_opposite, + data); + isl_basic_set_free(hull); + + if (!data->sign || r < 0) { + isl_aff_free(data->nonneg); + if (r < 0) + goto error; + return extract_nonneg_mod(data); + } + + v1 = isl_aff_get_constant_val(data->div); + v2 = isl_aff_get_constant_val(data->nonneg); + if (data->sign < 0) { + v1 = isl_val_neg(v1); + v1 = isl_val_add(v1, isl_val_copy(data->d)); + v1 = isl_val_sub_ui(v1, 1); + } + v1 = isl_val_sub(v1, isl_val_copy(v2)); + v1 = isl_val_mod(v1, isl_val_copy(data->d)); + v1 = isl_val_add(v1, v2); + v2 = isl_val_div(isl_val_copy(v1), isl_val_copy(data->d)); + v2 = isl_val_ceil(v2); + if (isl_val_is_neg(v2)) { + v2 = isl_val_mul(v2, isl_val_copy(data->d)); + v1 = isl_val_sub(v1, isl_val_copy(v2)); + } + data->nonneg = isl_aff_set_constant_val(data->nonneg, v1); + isl_val_free(v2); + + if (data->sign < 0) { + data->div = oppose_div_arg(data->div, isl_val_copy(data->d)); + data->v = isl_val_neg(data->v); + } + + return extract_term_and_mod(data, + isl_aff_copy(data->div), data->nonneg); +error: + data->aff = isl_aff_free(data->aff); + return -1; +} + +/* Check if "data->aff" involves any (implicit) modulo computations based + * on div "data->i". + * If so, remove them from aff and add expressions corresponding + * to those modulo computations to data->pos and/or data->neg. + * + * "aff" is assumed to be an integer affine expression. + * + * In particular, check if (v * div_j) is of the form + * + * f * m * floor(a / m) + * + * and, if so, rewrite it as + * + * f * (a - (a mod m)) = f * a - f * (a mod m) + * + * and extract out -f * (a mod m). + * In particular, if f > 0, we add (f * (a mod m)) to *neg. + * If f < 0, we add ((-f) * (a mod m)) to *pos. + * + * Note that in order to represent "a mod m" as + * + * (isl_ast_op_pdiv_r, a, m) + * + * we need to make sure that a is non-negative. + * If not, we check if "-a + m - 1" is non-negative. + * If so, we can rewrite + * + * floor(a/m) = -ceil(-a/m) = -floor((-a + m - 1)/m) + * + * and still extract a modulo. + */ +static int extract_modulo(struct isl_extract_mod_data *data) +{ + data->div = isl_aff_get_div(data->aff, data->i); + data->d = isl_aff_get_denominator_val(data->div); + if (isl_val_is_divisible_by(data->v, data->d)) { + data->div = isl_aff_scale_val(data->div, isl_val_copy(data->d)); + if (try_extract_mod(data) < 0) + data->aff = isl_aff_free(data->aff); + } + isl_aff_free(data->div); + isl_val_free(data->d); + return 0; +} + +/* Check if "aff" involves any (implicit) modulo computations. + * If so, remove them from aff and add expressions corresponding + * to those modulo computations to *pos and/or *neg. + * We only do this if the option ast_build_prefer_pdiv is set. + * + * "aff" is assumed to be an integer affine expression. + * + * A modulo expression is of the form + * + * a mod m = a - m * floor(a / m) + * + * To detect them in aff, we look for terms of the form + * + * f * m * floor(a / m) + * + * rewrite them as + * + * f * (a - (a mod m)) = f * a - f * (a mod m) + * + * and extract out -f * (a mod m). + * In particular, if f > 0, we add (f * (a mod m)) to *neg. + * If f < 0, we add ((-f) * (a mod m)) to *pos. + */ +static __isl_give isl_aff *extract_modulos(__isl_take isl_aff *aff, + __isl_keep isl_ast_expr **pos, __isl_keep isl_ast_expr **neg, + __isl_keep isl_ast_build *build) +{ + struct isl_extract_mod_data data = { build, aff, *pos, *neg }; + isl_ctx *ctx; + int n; + + if (!aff) + return NULL; + + ctx = isl_aff_get_ctx(aff); + if (!isl_options_get_ast_build_prefer_pdiv(ctx)) + return aff; + + n = isl_aff_dim(data.aff, isl_dim_div); + for (data.i = 0; data.i < n; ++data.i) { + data.v = isl_aff_get_coefficient_val(data.aff, + isl_dim_div, data.i); + if (!data.v) + return isl_aff_free(aff); + if (isl_val_is_zero(data.v) || + isl_val_is_one(data.v) || isl_val_is_negone(data.v)) { + isl_val_free(data.v); + continue; + } + if (extract_modulo(&data) < 0) + data.aff = isl_aff_free(data.aff); + isl_val_free(data.v); + if (!data.aff) + break; + } + + if (data.add) + data.aff = isl_aff_add(data.aff, data.add); + + *pos = data.pos; + *neg = data.neg; + return data.aff; +} + +/* Check if aff involves any non-integer coefficients. + * If so, split aff into + * + * aff = aff1 + (aff2 / d) + * + * with both aff1 and aff2 having only integer coefficients. + * Return aff1 and add (aff2 / d) to *expr. + */ +static __isl_give isl_aff *extract_rational(__isl_take isl_aff *aff, + __isl_keep isl_ast_expr **expr, __isl_keep isl_ast_build *build) +{ + int i, j, n; + isl_aff *rat = NULL; + isl_local_space *ls = NULL; + isl_ast_expr *rat_expr; + isl_val *v, *d; + enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_div }; + enum isl_dim_type l[] = { isl_dim_param, isl_dim_set, isl_dim_div }; + + if (!aff) + return NULL; + d = isl_aff_get_denominator_val(aff); + if (!d) + goto error; + if (isl_val_is_one(d)) { + isl_val_free(d); + return aff; + } + + aff = isl_aff_scale_val(aff, isl_val_copy(d)); + + ls = isl_aff_get_domain_local_space(aff); + rat = isl_aff_zero_on_domain(isl_local_space_copy(ls)); + + for (i = 0; i < 3; ++i) { + n = isl_aff_dim(aff, t[i]); + for (j = 0; j < n; ++j) { + isl_aff *rat_j; + + v = isl_aff_get_coefficient_val(aff, t[i], j); + if (!v) + goto error; + if (isl_val_is_divisible_by(v, d)) { + isl_val_free(v); + continue; + } + rat_j = isl_aff_var_on_domain(isl_local_space_copy(ls), + l[i], j); + rat_j = isl_aff_scale_val(rat_j, v); + rat = isl_aff_add(rat, rat_j); + } + } + + v = isl_aff_get_constant_val(aff); + if (isl_val_is_divisible_by(v, d)) { + isl_val_free(v); + } else { + isl_aff *rat_0; + + rat_0 = isl_aff_val_on_domain(isl_local_space_copy(ls), v); + rat = isl_aff_add(rat, rat_0); + } + + isl_local_space_free(ls); + + aff = isl_aff_sub(aff, isl_aff_copy(rat)); + aff = isl_aff_scale_down_val(aff, isl_val_copy(d)); + + rat_expr = isl_ast_expr_from_aff(rat, build); + rat_expr = isl_ast_expr_div(rat_expr, isl_ast_expr_from_val(d)); + *expr = ast_expr_add(*expr, rat_expr); + + return aff; +error: + isl_aff_free(rat); + isl_local_space_free(ls); + isl_aff_free(aff); + isl_val_free(d); + return NULL; +} + +/* Construct an isl_ast_expr that evaluates the affine expression "aff", + * The result is simplified in terms of build->domain. + * + * We first extract hidden modulo computations from the affine expression + * and then add terms for each variable with a non-zero coefficient. + * Finally, if the affine expression has a non-trivial denominator, + * we divide the resulting isl_ast_expr by this denominator. + */ +__isl_give isl_ast_expr *isl_ast_expr_from_aff(__isl_take isl_aff *aff, + __isl_keep isl_ast_build *build) +{ + int i, j; + int n; + isl_val *v; + isl_ctx *ctx = isl_aff_get_ctx(aff); + isl_ast_expr *expr, *expr_neg; + enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_div }; + enum isl_dim_type l[] = { isl_dim_param, isl_dim_set, isl_dim_div }; + isl_local_space *ls; + struct isl_ast_add_term_data data; + + if (!aff) + return NULL; + + expr = isl_ast_expr_alloc_int_si(ctx, 0); + expr_neg = isl_ast_expr_alloc_int_si(ctx, 0); + + aff = extract_rational(aff, &expr, build); + + aff = extract_modulos(aff, &expr, &expr_neg, build); + expr = ast_expr_sub(expr, expr_neg); + + ls = isl_aff_get_domain_local_space(aff); + + data.build = build; + data.cst = isl_aff_get_constant_val(aff); + for (i = 0; i < 3; ++i) { + n = isl_aff_dim(aff, t[i]); + for (j = 0; j < n; ++j) { + v = isl_aff_get_coefficient_val(aff, t[i], j); + if (!v) + expr = isl_ast_expr_free(expr); + if (isl_val_is_zero(v)) { + isl_val_free(v); + continue; + } + expr = isl_ast_expr_add_term(expr, + ls, l[i], j, v, &data); + } + } + + expr = isl_ast_expr_add_int(expr, data.cst); + + isl_local_space_free(ls); + isl_aff_free(aff); + return expr; +} + +/* Add terms to "expr" for each variable in "aff" with a coefficient + * with sign equal to "sign". + * The result is simplified in terms of data->build->domain. + */ +static __isl_give isl_ast_expr *add_signed_terms(__isl_take isl_ast_expr *expr, + __isl_keep isl_aff *aff, int sign, struct isl_ast_add_term_data *data) +{ + int i, j; + isl_val *v; + enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_div }; + enum isl_dim_type l[] = { isl_dim_param, isl_dim_set, isl_dim_div }; + isl_local_space *ls; + + ls = isl_aff_get_domain_local_space(aff); + + for (i = 0; i < 3; ++i) { + int n = isl_aff_dim(aff, t[i]); + for (j = 0; j < n; ++j) { + v = isl_aff_get_coefficient_val(aff, t[i], j); + if (sign * isl_val_sgn(v) <= 0) { + isl_val_free(v); + continue; + } + v = isl_val_abs(v); + expr = isl_ast_expr_add_term(expr, + ls, l[i], j, v, data); + } + } + + isl_local_space_free(ls); + + return expr; +} + +/* Should the constant term "v" be considered positive? + * + * A positive constant will be added to "pos" by the caller, + * while a negative constant will be added to "neg". + * If either "pos" or "neg" is exactly zero, then we prefer + * to add the constant "v" to that side, irrespective of the sign of "v". + * This results in slightly shorter expressions and may reduce the risk + * of overflows. + */ +static int constant_is_considered_positive(__isl_keep isl_val *v, + __isl_keep isl_ast_expr *pos, __isl_keep isl_ast_expr *neg) +{ + if (ast_expr_is_zero(pos)) + return 1; + if (ast_expr_is_zero(neg)) + return 0; + return isl_val_is_pos(v); +} + +/* Check if the equality + * + * aff = 0 + * + * represents a stride constraint on the integer division "pos". + * + * In particular, if the integer division "pos" is equal to + * + * floor(e/d) + * + * then check if aff is equal to + * + * e - d floor(e/d) + * + * or its opposite. + * + * If so, the equality is exactly + * + * e mod d = 0 + * + * Note that in principle we could also accept + * + * e - d floor(e'/d) + * + * where e and e' differ by a constant. + */ +static int is_stride_constraint(__isl_keep isl_aff *aff, int pos) +{ + isl_aff *div; + isl_val *c, *d; + int eq; + + div = isl_aff_get_div(aff, pos); + c = isl_aff_get_coefficient_val(aff, isl_dim_div, pos); + d = isl_aff_get_denominator_val(div); + eq = isl_val_abs_eq(c, d); + if (eq >= 0 && eq) { + aff = isl_aff_copy(aff); + aff = isl_aff_set_coefficient_si(aff, isl_dim_div, pos, 0); + div = isl_aff_scale_val(div, d); + if (isl_val_is_pos(c)) + div = isl_aff_neg(div); + eq = isl_aff_plain_is_equal(div, aff); + isl_aff_free(aff); + } else + isl_val_free(d); + isl_val_free(c); + isl_aff_free(div); + + return eq; +} + +/* Are all coefficients of "aff" (zero or) negative? + */ +static int all_negative_coefficients(__isl_keep isl_aff *aff) +{ + int i, n; + + if (!aff) + return 0; + + n = isl_aff_dim(aff, isl_dim_param); + for (i = 0; i < n; ++i) + if (isl_aff_coefficient_sgn(aff, isl_dim_param, i) > 0) + return 0; + + n = isl_aff_dim(aff, isl_dim_in); + for (i = 0; i < n; ++i) + if (isl_aff_coefficient_sgn(aff, isl_dim_in, i) > 0) + return 0; + + return 1; +} + +/* Give an equality of the form + * + * aff = e - d floor(e/d) = 0 + * + * or + * + * aff = -e + d floor(e/d) = 0 + * + * with the integer division "pos" equal to floor(e/d), + * construct the AST expression + * + * (isl_ast_op_eq, (isl_ast_op_zdiv_r, expr(e), expr(d)), expr(0)) + * + * If e only has negative coefficients, then construct + * + * (isl_ast_op_eq, (isl_ast_op_zdiv_r, expr(-e), expr(d)), expr(0)) + * + * instead. + */ +static __isl_give isl_ast_expr *extract_stride_constraint( + __isl_take isl_aff *aff, int pos, __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_val *c; + isl_ast_expr *expr, *cst; + + if (!aff) + return NULL; + + ctx = isl_aff_get_ctx(aff); + + c = isl_aff_get_coefficient_val(aff, isl_dim_div, pos); + aff = isl_aff_set_coefficient_si(aff, isl_dim_div, pos, 0); + + if (all_negative_coefficients(aff)) + aff = isl_aff_neg(aff); + + cst = isl_ast_expr_from_val(isl_val_abs(c)); + expr = isl_ast_expr_from_aff(aff, build); + + expr = isl_ast_expr_alloc_binary(isl_ast_op_zdiv_r, expr, cst); + cst = isl_ast_expr_alloc_int_si(ctx, 0); + expr = isl_ast_expr_alloc_binary(isl_ast_op_eq, expr, cst); + + return expr; +} + +/* Construct an isl_ast_expr that evaluates the condition "constraint", + * The result is simplified in terms of build->domain. + * + * We first check if the constraint is an equality of the form + * + * e - d floor(e/d) = 0 + * + * i.e., + * + * e mod d = 0 + * + * If so, we convert it to + * + * (isl_ast_op_eq, (isl_ast_op_zdiv_r, expr(e), expr(d)), expr(0)) + * + * Otherwise, let the constraint by either "a >= 0" or "a == 0". + * We first extract hidden modulo computations from "a" + * and then collect all the terms with a positive coefficient in cons_pos + * and the terms with a negative coefficient in cons_neg. + * + * The result is then of the form + * + * (isl_ast_op_ge, expr(pos), expr(-neg))) + * + * or + * + * (isl_ast_op_eq, expr(pos), expr(-neg))) + * + * However, if the first expression is an integer constant (and the second + * is not), then we swap the two expressions. This ensures that we construct, + * e.g., "i <= 5" rather than "5 >= i". + * + * Furthermore, is there are no terms with positive coefficients (or no terms + * with negative coefficients), then the constant term is added to "pos" + * (or "neg"), ignoring the sign of the constant term. + */ +static __isl_give isl_ast_expr *isl_ast_expr_from_constraint( + __isl_take isl_constraint *constraint, __isl_keep isl_ast_build *build) +{ + int i, n; + isl_ctx *ctx; + isl_ast_expr *expr_pos; + isl_ast_expr *expr_neg; + isl_ast_expr *expr; + isl_aff *aff; + int eq; + enum isl_ast_op_type type; + struct isl_ast_add_term_data data; + + if (!constraint) + return NULL; + + aff = isl_constraint_get_aff(constraint); + eq = isl_constraint_is_equality(constraint); + isl_constraint_free(constraint); + + n = isl_aff_dim(aff, isl_dim_div); + if (eq && n > 0) + for (i = 0; i < n; ++i) { + int is_stride; + is_stride = is_stride_constraint(aff, i); + if (is_stride < 0) + goto error; + if (is_stride) + return extract_stride_constraint(aff, i, build); + } + + ctx = isl_aff_get_ctx(aff); + expr_pos = isl_ast_expr_alloc_int_si(ctx, 0); + expr_neg = isl_ast_expr_alloc_int_si(ctx, 0); + + aff = extract_modulos(aff, &expr_pos, &expr_neg, build); + + data.build = build; + data.cst = isl_aff_get_constant_val(aff); + expr_pos = add_signed_terms(expr_pos, aff, 1, &data); + data.cst = isl_val_neg(data.cst); + expr_neg = add_signed_terms(expr_neg, aff, -1, &data); + data.cst = isl_val_neg(data.cst); + + if (constant_is_considered_positive(data.cst, expr_pos, expr_neg)) { + expr_pos = isl_ast_expr_add_int(expr_pos, data.cst); + } else { + data.cst = isl_val_neg(data.cst); + expr_neg = isl_ast_expr_add_int(expr_neg, data.cst); + } + + if (isl_ast_expr_get_type(expr_pos) == isl_ast_expr_int && + isl_ast_expr_get_type(expr_neg) != isl_ast_expr_int) { + type = eq ? isl_ast_op_eq : isl_ast_op_le; + expr = isl_ast_expr_alloc_binary(type, expr_neg, expr_pos); + } else { + type = eq ? isl_ast_op_eq : isl_ast_op_ge; + expr = isl_ast_expr_alloc_binary(type, expr_pos, expr_neg); + } + + isl_aff_free(aff); + return expr; +error: + isl_aff_free(aff); + return NULL; +} + +/* Wrapper around isl_constraint_cmp_last_non_zero for use + * as a callback to isl_constraint_list_sort. + * If isl_constraint_cmp_last_non_zero cannot tell the constraints + * apart, then use isl_constraint_plain_cmp instead. + */ +static int cmp_constraint(__isl_keep isl_constraint *a, + __isl_keep isl_constraint *b, void *user) +{ + int cmp; + + cmp = isl_constraint_cmp_last_non_zero(a, b); + if (cmp != 0) + return cmp; + return isl_constraint_plain_cmp(a, b); +} + +/* Construct an isl_ast_expr that evaluates the conditions defining "bset". + * The result is simplified in terms of build->domain. + * + * If "bset" is not bounded by any constraint, then we contruct + * the expression "1", i.e., "true". + * + * Otherwise, we sort the constraints, putting constraints that involve + * integer divisions after those that do not, and construct an "and" + * of the ast expressions of the individual constraints. + * + * Each constraint is added to the generated constraints of the build + * after it has been converted to an AST expression so that it can be used + * to simplify the following constraints. This may change the truth value + * of subsequent constraints that do not satisfy the earlier constraints, + * but this does not affect the outcome of the conjunction as it is + * only true if all the conjuncts are true (no matter in what order + * they are evaluated). In particular, the constraints that do not + * involve integer divisions may serve to simplify some constraints + * that do involve integer divisions. + */ +__isl_give isl_ast_expr *isl_ast_build_expr_from_basic_set( + __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset) +{ + int i, n; + isl_constraint *c; + isl_constraint_list *list; + isl_ast_expr *res; + isl_set *set; + + list = isl_basic_set_get_constraint_list(bset); + isl_basic_set_free(bset); + list = isl_constraint_list_sort(list, &cmp_constraint, NULL); + if (!list) + return NULL; + n = isl_constraint_list_n_constraint(list); + if (n == 0) { + isl_ctx *ctx = isl_constraint_list_get_ctx(list); + isl_constraint_list_free(list); + return isl_ast_expr_alloc_int_si(ctx, 1); + } + + build = isl_ast_build_copy(build); + + c = isl_constraint_list_get_constraint(list, 0); + bset = isl_basic_set_from_constraint(isl_constraint_copy(c)); + set = isl_set_from_basic_set(bset); + res = isl_ast_expr_from_constraint(c, build); + build = isl_ast_build_restrict_generated(build, set); + + for (i = 1; i < n; ++i) { + isl_ast_expr *expr; + + c = isl_constraint_list_get_constraint(list, i); + bset = isl_basic_set_from_constraint(isl_constraint_copy(c)); + set = isl_set_from_basic_set(bset); + expr = isl_ast_expr_from_constraint(c, build); + build = isl_ast_build_restrict_generated(build, set); + res = isl_ast_expr_and(res, expr); + } + + isl_constraint_list_free(list); + isl_ast_build_free(build); + return res; +} + +/* Construct an isl_ast_expr that evaluates the conditions defining "set". + * The result is simplified in terms of build->domain. + * + * If "set" is an (obviously) empty set, then return the expression "0". + * + * If there are multiple disjuncts in the description of the set, + * then subsequent disjuncts are simplified in a context where + * the previous disjuncts have been removed from build->domain. + * In particular, constraints that ensure that there is no overlap + * with these previous disjuncts, can be removed. + * This is mostly useful for disjuncts that are only defined by + * a single constraint (relative to the build domain) as the opposite + * of that single constraint can then be removed from the other disjuncts. + * In order not to increase the number of disjuncts in the build domain + * after subtracting the previous disjuncts of "set", the simple hull + * is computed after taking the difference with each of these disjuncts. + * This means that constraints that prevent overlap with a union + * of multiple previous disjuncts are not removed. + * + * "set" lives in the internal schedule space. + */ +__isl_give isl_ast_expr *isl_ast_build_expr_from_set_internal( + __isl_keep isl_ast_build *build, __isl_take isl_set *set) +{ + int i, n; + isl_basic_set *bset; + isl_basic_set_list *list; + isl_set *domain; + isl_ast_expr *res; + + list = isl_set_get_basic_set_list(set); + isl_set_free(set); + + if (!list) + return NULL; + n = isl_basic_set_list_n_basic_set(list); + if (n == 0) { + isl_ctx *ctx = isl_ast_build_get_ctx(build); + isl_basic_set_list_free(list); + return isl_ast_expr_from_val(isl_val_zero(ctx)); + } + + domain = isl_ast_build_get_domain(build); + + bset = isl_basic_set_list_get_basic_set(list, 0); + set = isl_set_from_basic_set(isl_basic_set_copy(bset)); + res = isl_ast_build_expr_from_basic_set(build, bset); + + for (i = 1; i < n; ++i) { + isl_ast_expr *expr; + isl_set *rest; + + rest = isl_set_subtract(isl_set_copy(domain), set); + rest = isl_set_from_basic_set(isl_set_simple_hull(rest)); + domain = isl_set_intersect(domain, rest); + bset = isl_basic_set_list_get_basic_set(list, i); + set = isl_set_from_basic_set(isl_basic_set_copy(bset)); + bset = isl_basic_set_gist(bset, + isl_set_simple_hull(isl_set_copy(domain))); + expr = isl_ast_build_expr_from_basic_set(build, bset); + res = isl_ast_expr_or(res, expr); + } + + isl_set_free(domain); + isl_set_free(set); + isl_basic_set_list_free(list); + return res; +} + +/* Construct an isl_ast_expr that evaluates the conditions defining "set". + * The result is simplified in terms of build->domain. + * + * If "set" is an (obviously) empty set, then return the expression "0". + * + * "set" lives in the external schedule space. + * + * The internal AST expression generation assumes that there are + * no unknown divs, so make sure an explicit representation is available. + * Since the set comes from the outside, it may have constraints that + * are redundant with respect to the build domain. Remove them first. + */ +__isl_give isl_ast_expr *isl_ast_build_expr_from_set( + __isl_keep isl_ast_build *build, __isl_take isl_set *set) +{ + if (isl_ast_build_need_schedule_map(build)) { + isl_multi_aff *ma; + ma = isl_ast_build_get_schedule_map_multi_aff(build); + set = isl_set_preimage_multi_aff(set, ma); + } + + set = isl_set_compute_divs(set); + set = isl_ast_build_compute_gist(build, set); + return isl_ast_build_expr_from_set_internal(build, set); +} + +/* State of data about previous pieces in + * isl_ast_build_expr_from_pw_aff_internal. + * + * isl_state_none: no data about previous pieces + * isl_state_single: data about a single previous piece + * isl_state_min: data represents minimum of several pieces + * isl_state_max: data represents maximum of several pieces + */ +enum isl_from_pw_aff_state { + isl_state_none, + isl_state_single, + isl_state_min, + isl_state_max +}; + +/* Internal date structure representing a single piece in the input of + * isl_ast_build_expr_from_pw_aff_internal. + * + * If "state" is isl_state_none, then "set_list" and "aff_list" are not used. + * If "state" is isl_state_single, then "set_list" and "aff_list" contain the + * single previous subpiece. + * If "state" is isl_state_min, then "set_list" and "aff_list" contain + * a sequence of several previous subpieces that are equal to the minimum + * of the entries in "aff_list" over the union of "set_list" + * If "state" is isl_state_max, then "set_list" and "aff_list" contain + * a sequence of several previous subpieces that are equal to the maximum + * of the entries in "aff_list" over the union of "set_list" + * + * During the construction of the pieces, "set" is NULL. + * After the construction, "set" is set to the union of the elements + * in "set_list", at which point "set_list" is set to NULL. + */ +struct isl_from_pw_aff_piece { + enum isl_from_pw_aff_state state; + isl_set *set; + isl_set_list *set_list; + isl_aff_list *aff_list; +}; + +/* Internal data structure for isl_ast_build_expr_from_pw_aff_internal. + * + * "build" specifies the domain against which the result is simplified. + * "dom" is the domain of the entire isl_pw_aff. + * + * "n" is the number of pieces constructed already. + * In particular, during the construction of the pieces, "n" points to + * the piece that is being constructed. After the construction of the + * pieces, "n" is set to the total number of pieces. + * "max" is the total number of allocated entries. + * "p" contains the individual pieces. + */ +struct isl_from_pw_aff_data { + isl_ast_build *build; + isl_set *dom; + + int n; + int max; + struct isl_from_pw_aff_piece *p; +}; + +/* Initialize "data" based on "build" and "pa". + */ +static isl_stat isl_from_pw_aff_data_init(struct isl_from_pw_aff_data *data, + __isl_keep isl_ast_build *build, __isl_keep isl_pw_aff *pa) +{ + int n; + isl_ctx *ctx; + + ctx = isl_pw_aff_get_ctx(pa); + n = isl_pw_aff_n_piece(pa); + if (n == 0) + isl_die(ctx, isl_error_invalid, + "cannot handle void expression", return isl_stat_error); + data->max = n; + data->p = isl_calloc_array(ctx, struct isl_from_pw_aff_piece, n); + if (!data->p) + return isl_stat_error; + data->build = build; + data->dom = isl_pw_aff_domain(isl_pw_aff_copy(pa)); + data->n = 0; + + return isl_stat_ok; +} + +/* Free all memory allocated for "data". + */ +static void isl_from_pw_aff_data_clear(struct isl_from_pw_aff_data *data) +{ + int i; + + isl_set_free(data->dom); + if (!data->p) + return; + + for (i = 0; i < data->max; ++i) { + isl_set_free(data->p[i].set); + isl_set_list_free(data->p[i].set_list); + isl_aff_list_free(data->p[i].aff_list); + } + free(data->p); +} + +/* Initialize the current entry of "data" to an unused piece. + */ +static void set_none(struct isl_from_pw_aff_data *data) +{ + data->p[data->n].state = isl_state_none; + data->p[data->n].set_list = NULL; + data->p[data->n].aff_list = NULL; +} + +/* Store "set" and "aff" in the current entry of "data" as a single subpiece. + */ +static void set_single(struct isl_from_pw_aff_data *data, + __isl_take isl_set *set, __isl_take isl_aff *aff) +{ + data->p[data->n].state = isl_state_single; + data->p[data->n].set_list = isl_set_list_from_set(set); + data->p[data->n].aff_list = isl_aff_list_from_aff(aff); +} + +/* Extend the current entry of "data" with "set" and "aff" + * as a minimum expression. + */ +static isl_stat extend_min(struct isl_from_pw_aff_data *data, + __isl_take isl_set *set, __isl_take isl_aff *aff) +{ + int n = data->n; + data->p[n].state = isl_state_min; + data->p[n].set_list = isl_set_list_add(data->p[n].set_list, set); + data->p[n].aff_list = isl_aff_list_add(data->p[n].aff_list, aff); + + if (!data->p[n].set_list || !data->p[n].aff_list) + return isl_stat_error; + return isl_stat_ok; +} + +/* Extend the current entry of "data" with "set" and "aff" + * as a maximum expression. + */ +static isl_stat extend_max(struct isl_from_pw_aff_data *data, + __isl_take isl_set *set, __isl_take isl_aff *aff) +{ + int n = data->n; + data->p[n].state = isl_state_max; + data->p[n].set_list = isl_set_list_add(data->p[n].set_list, set); + data->p[n].aff_list = isl_aff_list_add(data->p[n].aff_list, aff); + + if (!data->p[n].set_list || !data->p[n].aff_list) + return isl_stat_error; + return isl_stat_ok; +} + +/* Extend the domain of the current entry of "data", which is assumed + * to contain a single subpiece, with "set". If "replace" is set, + * then also replace the affine function by "aff". Otherwise, + * simply free "aff". + */ +static isl_stat extend_domain(struct isl_from_pw_aff_data *data, + __isl_take isl_set *set, __isl_take isl_aff *aff, int replace) +{ + int n = data->n; + isl_set *set_n; + + set_n = isl_set_list_get_set(data->p[n].set_list, 0); + set_n = isl_set_union(set_n, set); + data->p[n].set_list = + isl_set_list_set_set(data->p[n].set_list, 0, set_n); + + if (replace) + data->p[n].aff_list = + isl_aff_list_set_aff(data->p[n].aff_list, 0, aff); + else + isl_aff_free(aff); + + if (!data->p[n].set_list || !data->p[n].aff_list) + return isl_stat_error; + return isl_stat_ok; +} + +/* Construct an isl_ast_expr from "list" within "build". + * If "state" is isl_state_single, then "list" contains a single entry and + * an isl_ast_expr is constructed for that entry. + * Otherwise a min or max expression is constructed from "list" + * depending on "state". + */ +static __isl_give isl_ast_expr *ast_expr_from_aff_list( + __isl_take isl_aff_list *list, enum isl_from_pw_aff_state state, + __isl_keep isl_ast_build *build) +{ + int i, n; + isl_aff *aff; + isl_ast_expr *expr; + enum isl_ast_op_type op_type; + + if (state == isl_state_single) { + aff = isl_aff_list_get_aff(list, 0); + isl_aff_list_free(list); + return isl_ast_expr_from_aff(aff, build); + } + n = isl_aff_list_n_aff(list); + op_type = state == isl_state_min ? isl_ast_op_min : isl_ast_op_max; + expr = isl_ast_expr_alloc_op(isl_ast_build_get_ctx(build), op_type, n); + if (!expr) + goto error; + + for (i = 0; i < n; ++i) { + isl_ast_expr *expr_i; + + aff = isl_aff_list_get_aff(list, i); + expr_i = isl_ast_expr_from_aff(aff, build); + if (!expr_i) + goto error; + expr->u.op.args[i] = expr_i; + } + + isl_aff_list_free(list); + return expr; +error: + isl_aff_list_free(list); + isl_ast_expr_free(expr); + return NULL; +} + +/* Extend the expression in "next" to take into account + * the piece at position "pos" in "data", allowing for a further extension + * for the next piece(s). + * In particular, "next" is set to a select operation that selects + * an isl_ast_expr corresponding to data->aff_list on data->set and + * to an expression that will be filled in by later calls. + * Return a pointer to this location. + * Afterwards, the state of "data" is set to isl_state_none. + * + * The constraints of data->set are added to the generated + * constraints of the build such that they can be exploited to simplify + * the AST expression constructed from data->aff_list. + */ +static isl_ast_expr **add_intermediate_piece(struct isl_from_pw_aff_data *data, + int pos, isl_ast_expr **next) +{ + isl_ctx *ctx; + isl_ast_build *build; + isl_ast_expr *ternary, *arg; + isl_set *set, *gist; + + set = data->p[pos].set; + data->p[pos].set = NULL; + ctx = isl_ast_build_get_ctx(data->build); + ternary = isl_ast_expr_alloc_op(ctx, isl_ast_op_select, 3); + gist = isl_set_gist(isl_set_copy(set), isl_set_copy(data->dom)); + arg = isl_ast_build_expr_from_set_internal(data->build, gist); + ternary = isl_ast_expr_set_op_arg(ternary, 0, arg); + build = isl_ast_build_copy(data->build); + build = isl_ast_build_restrict_generated(build, set); + arg = ast_expr_from_aff_list(data->p[pos].aff_list, + data->p[pos].state, build); + data->p[pos].aff_list = NULL; + isl_ast_build_free(build); + ternary = isl_ast_expr_set_op_arg(ternary, 1, arg); + data->p[pos].state = isl_state_none; + if (!ternary) + return NULL; + + *next = ternary; + return &ternary->u.op.args[2]; +} + +/* Extend the expression in "next" to take into account + * the final piece, located at position "pos" in "data". + * In particular, "next" is set to evaluate data->aff_list + * and the domain is ignored. + * Return isl_stat_ok on success and isl_stat_error on failure. + * + * The constraints of data->set are however added to the generated + * constraints of the build such that they can be exploited to simplify + * the AST expression constructed from data->aff_list. + */ +static isl_stat add_last_piece(struct isl_from_pw_aff_data *data, + int pos, isl_ast_expr **next) +{ + isl_ast_build *build; + + if (data->p[pos].state == isl_state_none) + isl_die(isl_ast_build_get_ctx(data->build), isl_error_invalid, + "cannot handle void expression", return isl_stat_error); + + build = isl_ast_build_copy(data->build); + build = isl_ast_build_restrict_generated(build, data->p[pos].set); + data->p[pos].set = NULL; + *next = ast_expr_from_aff_list(data->p[pos].aff_list, + data->p[pos].state, build); + data->p[pos].aff_list = NULL; + isl_ast_build_free(build); + data->p[pos].state = isl_state_none; + if (!*next) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Return -1 if the piece "p1" should be sorted before "p2" + * and 1 if it should be sorted after "p2". + * Return 0 if they do not need to be sorted in a specific order. + * + * Pieces are sorted according to the number of disjuncts + * in their domains. + */ +static int sort_pieces_cmp(const void *p1, const void *p2, void *arg) +{ + const struct isl_from_pw_aff_piece *piece1 = p1; + const struct isl_from_pw_aff_piece *piece2 = p2; + int n1, n2; + + n1 = isl_set_n_basic_set(piece1->set); + n2 = isl_set_n_basic_set(piece2->set); + + return n1 - n2; +} + +/* Construct an isl_ast_expr from the pieces in "data". + * Return the result or NULL on failure. + * + * When this function is called, data->n points to the current piece. + * If this is an effective piece, then first increment data->n such + * that data->n contains the number of pieces. + * The "set_list" fields are subsequently replaced by the corresponding + * "set" fields, after which the pieces are sorted according to + * the number of disjuncts in these "set" fields. + * + * Construct intermediate AST expressions for the initial pieces and + * finish off with the final pieces. + */ +static isl_ast_expr *build_pieces(struct isl_from_pw_aff_data *data) +{ + int i; + isl_ast_expr *res = NULL; + isl_ast_expr **next = &res; + + if (data->p[data->n].state != isl_state_none) + data->n++; + if (data->n == 0) + isl_die(isl_ast_build_get_ctx(data->build), isl_error_invalid, + "cannot handle void expression", return NULL); + + for (i = 0; i < data->n; ++i) { + data->p[i].set = isl_set_list_union(data->p[i].set_list); + if (data->p[i].state != isl_state_single) + data->p[i].set = isl_set_coalesce(data->p[i].set); + data->p[i].set_list = NULL; + } + + if (isl_sort(data->p, data->n, sizeof(data->p[0]), + &sort_pieces_cmp, NULL) < 0) + return isl_ast_expr_free(res); + + for (i = 0; i + 1 < data->n; ++i) { + next = add_intermediate_piece(data, i, next); + if (!next) + return isl_ast_expr_free(res); + } + + if (add_last_piece(data, data->n - 1, next) < 0) + return isl_ast_expr_free(res); + + return res; +} + +/* Is the domain of the current entry of "data", which is assumed + * to contain a single subpiece, a subset of "set"? + */ +static isl_bool single_is_subset(struct isl_from_pw_aff_data *data, + __isl_keep isl_set *set) +{ + isl_bool subset; + isl_set *set_n; + + set_n = isl_set_list_get_set(data->p[data->n].set_list, 0); + subset = isl_set_is_subset(set_n, set); + isl_set_free(set_n); + + return subset; +} + +/* Can the list of subpieces in the last piece of "data" be extended with + * "set" and "aff" based on "test"? + * In particular, is it the case for each entry (set_i, aff_i) that + * + * test(aff, aff_i) holds on set_i, and + * test(aff_i, aff) holds on set? + * + * "test" returns the set of elements where the tests holds, meaning + * that test(aff_i, aff) holds on set if set is a subset of test(aff_i, aff). + * + * This function is used to detect min/max expressions. + * If the ast_build_detect_min_max option is turned off, then + * do not even try and perform any detection and return false instead. + */ +static isl_bool extends(struct isl_from_pw_aff_data *data, + __isl_keep isl_set *set, __isl_keep isl_aff *aff, + __isl_give isl_basic_set *(*test)(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2)) +{ + int i, n; + isl_ctx *ctx; + isl_set *dom; + + ctx = isl_ast_build_get_ctx(data->build); + if (!isl_options_get_ast_build_detect_min_max(ctx)) + return isl_bool_false; + + dom = isl_ast_build_get_domain(data->build); + set = isl_set_intersect(dom, isl_set_copy(set)); + + n = isl_set_list_n_set(data->p[data->n].set_list); + for (i = 0; i < n ; ++i) { + isl_aff *aff_i; + isl_set *valid; + isl_set *dom, *required; + isl_bool is_valid; + + aff_i = isl_aff_list_get_aff(data->p[data->n].aff_list, i); + valid = isl_set_from_basic_set(test(isl_aff_copy(aff), aff_i)); + required = isl_set_list_get_set(data->p[data->n].set_list, i); + dom = isl_ast_build_get_domain(data->build); + required = isl_set_intersect(dom, required); + is_valid = isl_set_is_subset(required, valid); + isl_set_free(required); + isl_set_free(valid); + if (is_valid < 0 || !is_valid) { + isl_set_free(set); + return is_valid; + } + + aff_i = isl_aff_list_get_aff(data->p[data->n].aff_list, i); + valid = isl_set_from_basic_set(test(aff_i, isl_aff_copy(aff))); + is_valid = isl_set_is_subset(set, valid); + isl_set_free(valid); + if (is_valid < 0 || !is_valid) { + isl_set_free(set); + return is_valid; + } + } + + isl_set_free(set); + return isl_bool_true; +} + +/* Can the list of pieces in "data" be extended with "set" and "aff" + * to form/preserve a minimum expression? + * In particular, is it the case for each entry (set_i, aff_i) that + * + * aff >= aff_i on set_i, and + * aff_i >= aff on set? + */ +static isl_bool extends_min(struct isl_from_pw_aff_data *data, + __isl_keep isl_set *set, __isl_keep isl_aff *aff) +{ + return extends(data, set, aff, &isl_aff_ge_basic_set); +} + +/* Can the list of pieces in "data" be extended with "set" and "aff" + * to form/preserve a maximum expression? + * In particular, is it the case for each entry (set_i, aff_i) that + * + * aff <= aff_i on set_i, and + * aff_i <= aff on set? + */ +static isl_bool extends_max(struct isl_from_pw_aff_data *data, + __isl_keep isl_set *set, __isl_keep isl_aff *aff) +{ + return extends(data, set, aff, &isl_aff_le_basic_set); +} + +/* This function is called during the construction of an isl_ast_expr + * that evaluates an isl_pw_aff. + * If the last piece of "data" contains a single subpiece and + * if its affine function is equal to "aff" on a part of the domain + * that includes either "set" or the domain of that single subpiece, + * then extend the domain of that single subpiece with "set". + * If it was the original domain of the single subpiece where + * the two affine functions are equal, then also replace + * the affine function of the single subpiece by "aff". + * If the last piece of "data" contains either a single subpiece + * or a minimum, then check if this minimum expression can be extended + * with (set, aff). + * If so, extend the sequence and return. + * Perform the same operation for maximum expressions. + * If no such extension can be performed, then move to the next piece + * in "data" (if the current piece contains any data), and then store + * the current subpiece in the current piece of "data" for later handling. + */ +static isl_stat ast_expr_from_pw_aff(__isl_take isl_set *set, + __isl_take isl_aff *aff, void *user) +{ + struct isl_from_pw_aff_data *data = user; + isl_bool test; + enum isl_from_pw_aff_state state; + + state = data->p[data->n].state; + if (state == isl_state_single) { + isl_aff *aff0; + isl_set *eq; + isl_bool subset1, subset2 = isl_bool_false; + aff0 = isl_aff_list_get_aff(data->p[data->n].aff_list, 0); + eq = isl_aff_eq_set(isl_aff_copy(aff), aff0); + subset1 = isl_set_is_subset(set, eq); + if (subset1 >= 0 && !subset1) + subset2 = single_is_subset(data, eq); + isl_set_free(eq); + if (subset1 < 0 || subset2 < 0) + goto error; + if (subset1) + return extend_domain(data, set, aff, 0); + if (subset2) + return extend_domain(data, set, aff, 1); + } + if (state == isl_state_single || state == isl_state_min) { + test = extends_min(data, set, aff); + if (test < 0) + goto error; + if (test) + return extend_min(data, set, aff); + } + if (state == isl_state_single || state == isl_state_max) { + test = extends_max(data, set, aff); + if (test < 0) + goto error; + if (test) + return extend_max(data, set, aff); + } + if (state != isl_state_none) + data->n++; + set_single(data, set, aff); + + return isl_stat_ok; +error: + isl_set_free(set); + isl_aff_free(aff); + return isl_stat_error; +} + +/* Construct an isl_ast_expr that evaluates "pa". + * The result is simplified in terms of build->domain. + * + * The domain of "pa" lives in the internal schedule space. + */ +__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff_internal( + __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa) +{ + struct isl_from_pw_aff_data data = { NULL }; + isl_ast_expr *res = NULL; + + pa = isl_ast_build_compute_gist_pw_aff(build, pa); + pa = isl_pw_aff_coalesce(pa); + if (!pa) + return NULL; + + if (isl_from_pw_aff_data_init(&data, build, pa) < 0) + goto error; + set_none(&data); + + if (isl_pw_aff_foreach_piece(pa, &ast_expr_from_pw_aff, &data) >= 0) + res = build_pieces(&data); + + isl_pw_aff_free(pa); + isl_from_pw_aff_data_clear(&data); + return res; +error: + isl_pw_aff_free(pa); + isl_from_pw_aff_data_clear(&data); + return NULL; +} + +/* Construct an isl_ast_expr that evaluates "pa". + * The result is simplified in terms of build->domain. + * + * The domain of "pa" lives in the external schedule space. + */ +__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa) +{ + isl_ast_expr *expr; + + if (isl_ast_build_need_schedule_map(build)) { + isl_multi_aff *ma; + ma = isl_ast_build_get_schedule_map_multi_aff(build); + pa = isl_pw_aff_pullback_multi_aff(pa, ma); + } + expr = isl_ast_build_expr_from_pw_aff_internal(build, pa); + return expr; +} + +/* Set the ids of the input dimensions of "mpa" to the iterator ids + * of "build". + * + * The domain of "mpa" is assumed to live in the internal schedule domain. + */ +static __isl_give isl_multi_pw_aff *set_iterator_names( + __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa) +{ + int i, n; + + n = isl_multi_pw_aff_dim(mpa, isl_dim_in); + for (i = 0; i < n; ++i) { + isl_id *id; + + id = isl_ast_build_get_iterator_id(build, i); + mpa = isl_multi_pw_aff_set_dim_id(mpa, isl_dim_in, i, id); + } + + return mpa; +} + +/* Construct an isl_ast_expr of type "type" with as first argument "arg0" and + * the remaining arguments derived from "mpa". + * That is, construct a call or access expression that calls/accesses "arg0" + * with arguments/indices specified by "mpa". + */ +static __isl_give isl_ast_expr *isl_ast_build_with_arguments( + __isl_keep isl_ast_build *build, enum isl_ast_op_type type, + __isl_take isl_ast_expr *arg0, __isl_take isl_multi_pw_aff *mpa) +{ + int i, n; + isl_ctx *ctx; + isl_ast_expr *expr; + + ctx = isl_ast_build_get_ctx(build); + + n = isl_multi_pw_aff_dim(mpa, isl_dim_out); + expr = isl_ast_expr_alloc_op(ctx, type, 1 + n); + expr = isl_ast_expr_set_op_arg(expr, 0, arg0); + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + isl_ast_expr *arg; + + pa = isl_multi_pw_aff_get_pw_aff(mpa, i); + arg = isl_ast_build_expr_from_pw_aff_internal(build, pa); + expr = isl_ast_expr_set_op_arg(expr, 1 + i, arg); + } + + isl_multi_pw_aff_free(mpa); + return expr; +} + +static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff_internal( + __isl_keep isl_ast_build *build, enum isl_ast_op_type type, + __isl_take isl_multi_pw_aff *mpa); + +/* Construct an isl_ast_expr that accesses the member specified by "mpa". + * The range of "mpa" is assumed to be wrapped relation. + * The domain of this wrapped relation specifies the structure being + * accessed, while the range of this wrapped relation spacifies the + * member of the structure being accessed. + * + * The domain of "mpa" is assumed to live in the internal schedule domain. + */ +static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff_member( + __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa) +{ + isl_id *id; + isl_multi_pw_aff *domain; + isl_ast_expr *domain_expr, *expr; + enum isl_ast_op_type type = isl_ast_op_access; + + domain = isl_multi_pw_aff_copy(mpa); + domain = isl_multi_pw_aff_range_factor_domain(domain); + domain_expr = isl_ast_build_from_multi_pw_aff_internal(build, + type, domain); + mpa = isl_multi_pw_aff_range_factor_range(mpa); + if (!isl_multi_pw_aff_has_tuple_id(mpa, isl_dim_out)) + isl_die(isl_ast_build_get_ctx(build), isl_error_invalid, + "missing field name", goto error); + id = isl_multi_pw_aff_get_tuple_id(mpa, isl_dim_out); + expr = isl_ast_expr_from_id(id); + expr = isl_ast_expr_alloc_binary(isl_ast_op_member, domain_expr, expr); + return isl_ast_build_with_arguments(build, type, expr, mpa); +error: + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Construct an isl_ast_expr of type "type" that calls or accesses + * the element specified by "mpa". + * The first argument is obtained from the output tuple name. + * The remaining arguments are given by the piecewise affine expressions. + * + * If the range of "mpa" is a mapped relation, then we assume it + * represents an access to a member of a structure. + * + * The domain of "mpa" is assumed to live in the internal schedule domain. + */ +static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff_internal( + __isl_keep isl_ast_build *build, enum isl_ast_op_type type, + __isl_take isl_multi_pw_aff *mpa) +{ + isl_ctx *ctx; + isl_id *id; + isl_ast_expr *expr; + + if (!mpa) + goto error; + + if (type == isl_ast_op_access && + isl_multi_pw_aff_range_is_wrapping(mpa)) + return isl_ast_build_from_multi_pw_aff_member(build, mpa); + + mpa = set_iterator_names(build, mpa); + if (!build || !mpa) + goto error; + + ctx = isl_ast_build_get_ctx(build); + + if (isl_multi_pw_aff_has_tuple_id(mpa, isl_dim_out)) + id = isl_multi_pw_aff_get_tuple_id(mpa, isl_dim_out); + else + id = isl_id_alloc(ctx, "", NULL); + + expr = isl_ast_expr_from_id(id); + return isl_ast_build_with_arguments(build, type, expr, mpa); +error: + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Construct an isl_ast_expr of type "type" that calls or accesses + * the element specified by "pma". + * The first argument is obtained from the output tuple name. + * The remaining arguments are given by the piecewise affine expressions. + * + * The domain of "pma" is assumed to live in the internal schedule domain. + */ +static __isl_give isl_ast_expr *isl_ast_build_from_pw_multi_aff_internal( + __isl_keep isl_ast_build *build, enum isl_ast_op_type type, + __isl_take isl_pw_multi_aff *pma) +{ + isl_multi_pw_aff *mpa; + + mpa = isl_multi_pw_aff_from_pw_multi_aff(pma); + return isl_ast_build_from_multi_pw_aff_internal(build, type, mpa); +} + +/* Construct an isl_ast_expr of type "type" that calls or accesses + * the element specified by "mpa". + * The first argument is obtained from the output tuple name. + * The remaining arguments are given by the piecewise affine expressions. + * + * The domain of "mpa" is assumed to live in the external schedule domain. + */ +static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff( + __isl_keep isl_ast_build *build, enum isl_ast_op_type type, + __isl_take isl_multi_pw_aff *mpa) +{ + int is_domain; + isl_ast_expr *expr; + isl_space *space_build, *space_mpa; + + space_build = isl_ast_build_get_space(build, 0); + space_mpa = isl_multi_pw_aff_get_space(mpa); + is_domain = isl_space_tuple_is_equal(space_build, isl_dim_set, + space_mpa, isl_dim_in); + isl_space_free(space_build); + isl_space_free(space_mpa); + if (is_domain < 0) + goto error; + if (!is_domain) + isl_die(isl_ast_build_get_ctx(build), isl_error_invalid, + "spaces don't match", goto error); + + if (isl_ast_build_need_schedule_map(build)) { + isl_multi_aff *ma; + ma = isl_ast_build_get_schedule_map_multi_aff(build); + mpa = isl_multi_pw_aff_pullback_multi_aff(mpa, ma); + } + + expr = isl_ast_build_from_multi_pw_aff_internal(build, type, mpa); + return expr; +error: + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Construct an isl_ast_expr that calls the domain element specified by "mpa". + * The name of the function is obtained from the output tuple name. + * The arguments are given by the piecewise affine expressions. + * + * The domain of "mpa" is assumed to live in the external schedule domain. + */ +__isl_give isl_ast_expr *isl_ast_build_call_from_multi_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa) +{ + return isl_ast_build_from_multi_pw_aff(build, isl_ast_op_call, mpa); +} + +/* Construct an isl_ast_expr that accesses the array element specified by "mpa". + * The name of the array is obtained from the output tuple name. + * The index expressions are given by the piecewise affine expressions. + * + * The domain of "mpa" is assumed to live in the external schedule domain. + */ +__isl_give isl_ast_expr *isl_ast_build_access_from_multi_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa) +{ + return isl_ast_build_from_multi_pw_aff(build, isl_ast_op_access, mpa); +} + +/* Construct an isl_ast_expr of type "type" that calls or accesses + * the element specified by "pma". + * The first argument is obtained from the output tuple name. + * The remaining arguments are given by the piecewise affine expressions. + * + * The domain of "pma" is assumed to live in the external schedule domain. + */ +static __isl_give isl_ast_expr *isl_ast_build_from_pw_multi_aff( + __isl_keep isl_ast_build *build, enum isl_ast_op_type type, + __isl_take isl_pw_multi_aff *pma) +{ + isl_multi_pw_aff *mpa; + + mpa = isl_multi_pw_aff_from_pw_multi_aff(pma); + return isl_ast_build_from_multi_pw_aff(build, type, mpa); +} + +/* Construct an isl_ast_expr that calls the domain element specified by "pma". + * The name of the function is obtained from the output tuple name. + * The arguments are given by the piecewise affine expressions. + * + * The domain of "pma" is assumed to live in the external schedule domain. + */ +__isl_give isl_ast_expr *isl_ast_build_call_from_pw_multi_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma) +{ + return isl_ast_build_from_pw_multi_aff(build, isl_ast_op_call, pma); +} + +/* Construct an isl_ast_expr that accesses the array element specified by "pma". + * The name of the array is obtained from the output tuple name. + * The index expressions are given by the piecewise affine expressions. + * + * The domain of "pma" is assumed to live in the external schedule domain. + */ +__isl_give isl_ast_expr *isl_ast_build_access_from_pw_multi_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma) +{ + return isl_ast_build_from_pw_multi_aff(build, isl_ast_op_access, pma); +} + +/* Construct an isl_ast_expr that calls the domain element + * specified by "executed". + * + * "executed" is assumed to be single-valued, with a domain that lives + * in the internal schedule space. + */ +__isl_give isl_ast_node *isl_ast_build_call_from_executed( + __isl_keep isl_ast_build *build, __isl_take isl_map *executed) +{ + isl_pw_multi_aff *iteration; + isl_ast_expr *expr; + + iteration = isl_pw_multi_aff_from_map(executed); + iteration = isl_ast_build_compute_gist_pw_multi_aff(build, iteration); + iteration = isl_pw_multi_aff_intersect_domain(iteration, + isl_ast_build_get_domain(build)); + expr = isl_ast_build_from_pw_multi_aff_internal(build, isl_ast_op_call, + iteration); + return isl_ast_node_alloc_user(expr); +} Index: lib/Analysis/isl/isl_ast_build_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_build_private.h @@ -0,0 +1,327 @@ +#ifndef ISL_AST_BUILD_PRIVATE_H +#define ISL_AST_BUILD_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +/* An isl_ast_build represents the context in which AST is being + * generated. That is, it (mostly) contains information about outer + * loops that can be used to simplify inner loops. + * + * "domain" represents constraints on the internal schedule domain, + * corresponding to the context of the AST generation and the constraints + * implied by the loops that have already been generated. + * When an isl_ast_build is first created, outside any AST generation, + * the domain is typically a parameter set. It is only when a AST + * generation phase is initiated that the domain of the isl_ast_build + * is changed to refer to the internal schedule domain. + * The domain then lives in a space of the form + * + * S + * + * or + * + * [O -> S] + * + * O represents the loops generated in outer AST generations. + * S represents the loops (both generated and to be generated) + * of the current AST generation. + * Both include eliminated loops. + * "domain" is expected not to have any unknown divs because + * it is used as the context argument in a call to isl_basic_set_gist + * in isl_ast_build_compute_gist_basic_set. + * + * "depth" is equal to the number of loops that have already + * been generated (including those in outer AST generations). + * "outer_pos" is equal to the number of loops in outer AST generations. + * + * "generated" is a superset of "domain" corresponding to those + * constraints that were either given by the user or that have + * effectively been generated (as bounds on a for loop). + * + * "pending" is a superset of "domain" corresponding to the constraints + * that still need to be generated (as guards), but that may end up + * not getting generated if they are implied by any constraints + * enforced by inner loops. + * + * "strides" contains the stride of each loop. The number of elements + * is equal to the number of dimensions in "domain". + * "offsets" constains the offsets of strided loops. If s is the stride + * for a given dimension and f is the corresponding offset, then the + * dimension takes on values + * + * f + s a + * + * with a an integer. For non-strided loops, the offset is zero. + * + * "iterators" contains the loop iterators of both generated and + * to be generated loops. The number of elements is at least as + * large as the dimension of the internal schedule domain. The + * number may be larger, in which case the additional ids can be + * used in a nested AST generation should the schedule be non-injective. + * + * "values" lives in the space + * + * [O -> S] -> [O -> S] (or S -> S) + * + * and expresses (if possible) loop iterators in terms of parameters + * and outer loop iterators. If the value of a given loop iterator + * cannot be expressed as an affine expression (either because the iterator + * attains multiple values or because the single value is a piecewise + * affine expression), then it is expressed in "values" as being equal + * to itself. + * + * "value" is the value of the loop iterator at the current depth. + * It is NULL if it has not been computed yet or if the value of the + * given loop iterator cannot be expressed as a piecewise affine expression + * (because the iterator attains multiple values). + * + * "schedule_map" maps the internal schedule domain to the external schedule + * domain. It may be NULL if it hasn't been computed yet. + * See isl_ast_build_get_schedule_map_multi_aff. + * + * "internal2input" maps the internal schedule domain to the original + * input schedule domain. In case of a schedule tree input, the original + * input schedule domain consist of the flat product of all outer + * band node spaces, including the current band node. + * It may be NULL if there no longer is such a uniform mapping + * (because different iterations have been rescheduled differently). + * + * "options" contains the AST build options in case we are generating + * an AST from a flat schedule map. When creating an AST from a schedule + * tree, this field is ignored. + * + * The "create_leaf" callback is called for every leaf in the generated AST. + * The callback is responsible for creating the node to be placed at those + * leaves. If this callback is not set, then isl will generated user + * nodes with call expressions corresponding to an element of the domain. + * + * The "at_each_domain" callback is called on every node created to represent + * an element of the domain. Each of these nodes is a user node + * with as expression a call expression. + * + * The "before_each_for" callback is called on each for node before + * its children have been created. + * + * The "after_each_for" callback is called on each for node after + * its children have been created. + * + * The "before_each_mark" callback is called before we handle the subtree + * of an isl_schedule_node_mark node. + * + * The "after_each_mark" callback is called after we have handled the subtree + * of an isl_schedule_node_mark node. + * + * "executed" contains the inverse schedule at this point + * of the AST generation. + * It is currently only used in isl_ast_build_get_schedule, which is + * in turn only used by user code from within a callback. + * The value is set right before we may be calling such a callback. + * + * "single_valued" is set if the current inverse schedule (which may or may + * not be stored in "executed") is known to be single valued, specifically + * an inverse schedule that was not (appeared not to be) single valued + * is extended to a single valued inverse schedule. This is mainly used + * to avoid an infinite recursion when we fail to detect later on that + * the extended inverse schedule is single valued. + * + * "node" points to the current band node in case we are generating + * an AST from a schedule tree. It may be NULL if we are not generating + * an AST from a schedule tree or if we are not inside a band node. + * + * "loop_type" originally constains loop AST generation types for + * the "n" members of "node" and it is updated (along with "n") when + * a schedule dimension is inserted. + * It is NULL if "node" is NULL. + * + * "isolated" is the piece of the schedule domain isolated by the isolate + * option on the current band. This set may be NULL if we have not checked + * for the isolate option yet. + */ +struct isl_ast_build { + int ref; + + int outer_pos; + int depth; + + isl_id_list *iterators; + + isl_set *domain; + isl_set *generated; + isl_set *pending; + isl_multi_aff *values; + + isl_pw_aff *value; + + isl_vec *strides; + isl_multi_aff *offsets; + + isl_multi_aff *schedule_map; + isl_multi_aff *internal2input; + + isl_union_map *options; + + __isl_give isl_ast_node *(*at_each_domain)( + __isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user); + void *at_each_domain_user; + + __isl_give isl_id *(*before_each_for)( + __isl_keep isl_ast_build *context, void *user); + void *before_each_for_user; + __isl_give isl_ast_node *(*after_each_for)( + __isl_take isl_ast_node *node, + __isl_keep isl_ast_build *context, void *user); + void *after_each_for_user; + + isl_stat (*before_each_mark)(__isl_keep isl_id *mark, + __isl_keep isl_ast_build *build, void *user); + void *before_each_mark_user; + __isl_give isl_ast_node *(*after_each_mark)( + __isl_take isl_ast_node *node, + __isl_keep isl_ast_build *context, void *user); + void *after_each_mark_user; + + __isl_give isl_ast_node *(*create_leaf)( + __isl_take isl_ast_build *build, void *user); + void *create_leaf_user; + + isl_union_map *executed; + int single_valued; + + isl_schedule_node *node; + int n; + enum isl_ast_loop_type *loop_type; + isl_set *isolated; +}; + +__isl_give isl_ast_build *isl_ast_build_clear_local_info( + __isl_take isl_ast_build *build); +__isl_give isl_ast_build *isl_ast_build_increase_depth( + __isl_take isl_ast_build *build); +int isl_ast_build_get_depth(__isl_keep isl_ast_build *build); +unsigned isl_ast_build_dim(__isl_keep isl_ast_build *build, + enum isl_dim_type type); +__isl_give isl_space *isl_ast_build_get_space( + __isl_keep isl_ast_build *build, int internal); +__isl_give isl_ast_build *isl_ast_build_align_params( + __isl_take isl_ast_build *build, __isl_take isl_space *model); +__isl_give isl_ast_build *isl_ast_build_cow( + __isl_take isl_ast_build *build); +__isl_give isl_ast_build *isl_ast_build_insert_dim( + __isl_take isl_ast_build *build, int pos); +__isl_give isl_ast_build *isl_ast_build_scale_down( + __isl_take isl_ast_build *build, __isl_take isl_val *m, + __isl_take isl_union_map *umap); +__isl_give isl_ast_build *isl_ast_build_product( + __isl_take isl_ast_build *build, __isl_take isl_space *embedding); +__isl_give isl_ast_build *isl_ast_build_set_loop_bounds( + __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds); +__isl_give isl_ast_build *isl_ast_build_set_pending_generated( + __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds); +__isl_give isl_ast_build *isl_ast_build_detect_strides( + __isl_take isl_ast_build *build, __isl_take isl_set *set); +__isl_give isl_ast_build *isl_ast_build_include_stride( + __isl_take isl_ast_build *build); +__isl_give isl_ast_build *isl_ast_build_set_executed( + __isl_take isl_ast_build *build, + __isl_take isl_union_map *executed); +__isl_give isl_ast_build *isl_ast_build_set_single_valued( + __isl_take isl_ast_build *build, int sv); +__isl_give isl_multi_aff *isl_ast_build_get_internal2input( + __isl_keep isl_ast_build *build); +__isl_give isl_set *isl_ast_build_get_domain( + __isl_keep isl_ast_build *build); +__isl_give isl_set *isl_ast_build_get_pending( + __isl_keep isl_ast_build *build); +__isl_give isl_set *isl_ast_build_get_generated( + __isl_keep isl_ast_build *build); +__isl_give isl_ast_build *isl_ast_build_restrict_generated( + __isl_take isl_ast_build *build, __isl_take isl_set *set); +__isl_give isl_ast_build *isl_ast_build_replace_pending_by_guard( + __isl_take isl_ast_build *build, __isl_take isl_set *guard); +__isl_give int isl_ast_build_need_schedule_map( + __isl_keep isl_ast_build *build); +__isl_give isl_multi_aff *isl_ast_build_get_schedule_map_multi_aff( + __isl_keep isl_ast_build *build); +__isl_give isl_map *isl_ast_build_get_schedule_map( + __isl_keep isl_ast_build *build); +int isl_ast_build_has_affine_value(__isl_keep isl_ast_build *build, int pos); +int isl_ast_build_has_value(__isl_keep isl_ast_build *build); +__isl_give isl_id *isl_ast_build_get_iterator_id( + __isl_keep isl_ast_build *build, int pos); + +int isl_ast_build_has_schedule_node(__isl_keep isl_ast_build *build); +__isl_give isl_schedule_node *isl_ast_build_get_schedule_node( + __isl_keep isl_ast_build *build); +__isl_give isl_ast_build *isl_ast_build_set_schedule_node( + __isl_take isl_ast_build *build, + __isl_take isl_schedule_node *node); +__isl_give isl_ast_build *isl_ast_build_reset_schedule_node( + __isl_take isl_ast_build *build); + +__isl_give isl_ast_build *isl_ast_build_extract_isolated( + __isl_take isl_ast_build *build); +int isl_ast_build_has_isolated(__isl_keep isl_ast_build *build); +__isl_give isl_set *isl_ast_build_get_isolated( + __isl_keep isl_ast_build *build); + +__isl_give isl_basic_set *isl_ast_build_specialize_basic_set( + __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_ast_build_compute_gist_basic_set( + __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset); +__isl_give isl_set *isl_ast_build_specialize(__isl_keep isl_ast_build *build, + __isl_take isl_set *set); +__isl_give isl_set *isl_ast_build_compute_gist( + __isl_keep isl_ast_build *build, __isl_take isl_set *set); +__isl_give isl_map *isl_ast_build_compute_gist_map_domain( + __isl_keep isl_ast_build *build, __isl_take isl_map *map); +__isl_give isl_aff *isl_ast_build_compute_gist_aff( + __isl_keep isl_ast_build *build, __isl_take isl_aff *aff); +__isl_give isl_pw_aff *isl_ast_build_compute_gist_pw_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa); +__isl_give isl_pw_multi_aff *isl_ast_build_compute_gist_pw_multi_aff( + __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma); + +__isl_give isl_union_map *isl_ast_build_substitute_values_union_map_domain( + __isl_keep isl_ast_build *build, __isl_take isl_union_map *umap); + +int isl_ast_build_aff_is_nonneg(__isl_keep isl_ast_build *build, + __isl_keep isl_aff *aff); + +isl_bool isl_ast_build_has_stride(__isl_keep isl_ast_build *build, int pos); +__isl_give isl_aff *isl_ast_build_get_offset(__isl_keep isl_ast_build *build, + int pos); +__isl_give isl_val *isl_ast_build_get_stride(__isl_keep isl_ast_build *build, + int pos); +__isl_give isl_set *isl_ast_build_get_stride_constraint( + __isl_keep isl_ast_build *build); +__isl_give isl_multi_aff *isl_ast_build_get_stride_expansion( + __isl_keep isl_ast_build *build); + +void isl_ast_build_dump(__isl_keep isl_ast_build *build); + +__isl_give isl_set *isl_ast_build_get_option_domain( + __isl_keep isl_ast_build *build, enum isl_ast_loop_type type); +__isl_give isl_map *isl_ast_build_get_separation_class( + __isl_keep isl_ast_build *build); +__isl_give isl_set *isl_ast_build_eliminate( + __isl_keep isl_ast_build *build, __isl_take isl_set *domain); +__isl_give isl_set *isl_ast_build_eliminate_inner( + __isl_keep isl_ast_build *build, __isl_take isl_set *set); +__isl_give isl_set *isl_ast_build_eliminate_divs( + __isl_keep isl_ast_build *build, __isl_take isl_set *set); + +enum isl_ast_loop_type isl_ast_build_get_loop_type( + __isl_keep isl_ast_build *build, int isolated); + +__isl_give isl_map *isl_ast_build_map_to_iterator( + __isl_keep isl_ast_build *build, __isl_take isl_set *set); + +int isl_ast_build_options_involve_depth(__isl_keep isl_ast_build *build); + +#endif Index: lib/Analysis/isl/isl_ast_codegen.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_codegen.c @@ -0,0 +1,5750 @@ +/* + * Copyright 2012-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Data used in generate_domain. + * + * "build" is the input build. + * "list" collects the results. + */ +struct isl_generate_domain_data { + isl_ast_build *build; + + isl_ast_graft_list *list; +}; + +static __isl_give isl_ast_graft_list *generate_next_level( + __isl_take isl_union_map *executed, + __isl_take isl_ast_build *build); +static __isl_give isl_ast_graft_list *generate_code( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build, + int internal); + +/* Generate an AST for a single domain based on + * the (non single valued) inverse schedule "executed". + * + * We extend the schedule with the iteration domain + * and continue generating through a call to generate_code. + * + * In particular, if executed has the form + * + * S -> D + * + * then we continue generating code on + * + * [S -> D] -> D + * + * The extended inverse schedule is clearly single valued + * ensuring that the nested generate_code will not reach this function, + * but will instead create calls to all elements of D that need + * to be executed from the current schedule domain. + */ +static isl_stat generate_non_single_valued(__isl_take isl_map *executed, + struct isl_generate_domain_data *data) +{ + isl_map *identity; + isl_ast_build *build; + isl_ast_graft_list *list; + + build = isl_ast_build_copy(data->build); + + identity = isl_set_identity(isl_map_range(isl_map_copy(executed))); + executed = isl_map_domain_product(executed, identity); + build = isl_ast_build_set_single_valued(build, 1); + + list = generate_code(isl_union_map_from_map(executed), build, 1); + + data->list = isl_ast_graft_list_concat(data->list, list); + + return isl_stat_ok; +} + +/* Call the at_each_domain callback, if requested by the user, + * after recording the current inverse schedule in the build. + */ +static __isl_give isl_ast_graft *at_each_domain(__isl_take isl_ast_graft *graft, + __isl_keep isl_map *executed, __isl_keep isl_ast_build *build) +{ + if (!graft || !build) + return isl_ast_graft_free(graft); + if (!build->at_each_domain) + return graft; + + build = isl_ast_build_copy(build); + build = isl_ast_build_set_executed(build, + isl_union_map_from_map(isl_map_copy(executed))); + if (!build) + return isl_ast_graft_free(graft); + + graft->node = build->at_each_domain(graft->node, + build, build->at_each_domain_user); + isl_ast_build_free(build); + + if (!graft->node) + graft = isl_ast_graft_free(graft); + + return graft; +} + +/* Generate a call expression for the single executed + * domain element "map" and put a guard around it based its (simplified) + * domain. "executed" is the original inverse schedule from which "map" + * has been derived. In particular, "map" is either identical to "executed" + * or it is the result of gisting "executed" with respect to the build domain. + * "executed" is only used if there is an at_each_domain callback. + * + * At this stage, any pending constraints in the build can no longer + * be simplified with respect to any enforced constraints since + * the call node does not have any enforced constraints. + * Since all pending constraints not covered by any enforced constraints + * will be added as a guard to the graft in create_node_scaled, + * even in the eliminated case, the pending constraints + * can be considered to have been generated by outer constructs. + * + * If the user has set an at_each_domain callback, it is called + * on the constructed call expression node. + */ +static isl_stat add_domain(__isl_take isl_map *executed, + __isl_take isl_map *map, struct isl_generate_domain_data *data) +{ + isl_ast_build *build; + isl_ast_graft *graft; + isl_ast_graft_list *list; + isl_set *guard, *pending; + + build = isl_ast_build_copy(data->build); + pending = isl_ast_build_get_pending(build); + build = isl_ast_build_replace_pending_by_guard(build, pending); + + guard = isl_map_domain(isl_map_copy(map)); + guard = isl_set_compute_divs(guard); + guard = isl_set_coalesce(guard); + guard = isl_set_gist(guard, isl_ast_build_get_generated(build)); + guard = isl_ast_build_specialize(build, guard); + + graft = isl_ast_graft_alloc_domain(map, build); + graft = at_each_domain(graft, executed, build); + isl_ast_build_free(build); + isl_map_free(executed); + graft = isl_ast_graft_add_guard(graft, guard, data->build); + + list = isl_ast_graft_list_from_ast_graft(graft); + data->list = isl_ast_graft_list_concat(data->list, list); + + return isl_stat_ok; +} + +/* Generate an AST for a single domain based on + * the inverse schedule "executed" and add it to data->list. + * + * If there is more than one domain element associated to the current + * schedule "time", then we need to continue the generation process + * in generate_non_single_valued. + * Note that the inverse schedule being single-valued may depend + * on constraints that are only available in the original context + * domain specified by the user. We therefore first introduce + * some of the constraints of data->build->domain. In particular, + * we intersect with a single-disjunct approximation of this set. + * We perform this approximation to avoid further splitting up + * the executed relation, possibly introducing a disjunctive guard + * on the statement. + * + * On the other hand, we only perform the test after having taken the gist + * of the domain as the resulting map is the one from which the call + * expression is constructed. Using this map to construct the call + * expression usually yields simpler results in cases where the original + * map is not obviously single-valued. + * If the original map is obviously single-valued, then the gist + * operation is skipped. + * + * Because we perform the single-valuedness test on the gisted map, + * we may in rare cases fail to recognize that the inverse schedule + * is single-valued. This becomes problematic if this happens + * from the recursive call through generate_non_single_valued + * as we would then end up in an infinite recursion. + * We therefore check if we are inside a call to generate_non_single_valued + * and revert to the ungisted map if the gisted map turns out not to be + * single-valued. + * + * Otherwise, call add_domain to generate a call expression (with guard) and + * to call the at_each_domain callback, if any. + */ +static isl_stat generate_domain(__isl_take isl_map *executed, void *user) +{ + struct isl_generate_domain_data *data = user; + isl_set *domain; + isl_map *map = NULL; + int empty, sv; + + domain = isl_ast_build_get_domain(data->build); + domain = isl_set_from_basic_set(isl_set_simple_hull(domain)); + executed = isl_map_intersect_domain(executed, domain); + empty = isl_map_is_empty(executed); + if (empty < 0) + goto error; + if (empty) { + isl_map_free(executed); + return isl_stat_ok; + } + + sv = isl_map_plain_is_single_valued(executed); + if (sv < 0) + goto error; + if (sv) + return add_domain(executed, isl_map_copy(executed), data); + + executed = isl_map_coalesce(executed); + map = isl_map_copy(executed); + map = isl_ast_build_compute_gist_map_domain(data->build, map); + sv = isl_map_is_single_valued(map); + if (sv < 0) + goto error; + if (!sv) { + isl_map_free(map); + if (data->build->single_valued) + map = isl_map_copy(executed); + else + return generate_non_single_valued(executed, data); + } + + return add_domain(executed, map, data); +error: + isl_map_free(map); + isl_map_free(executed); + return isl_stat_error; +} + +/* Call build->create_leaf to a create "leaf" node in the AST, + * encapsulate the result in an isl_ast_graft and return the result + * as a 1-element list. + * + * Note that the node returned by the user may be an entire tree. + * + * Since the node itself cannot enforce any constraints, we turn + * all pending constraints into guards and add them to the resulting + * graft to ensure that they will be generated. + * + * Before we pass control to the user, we first clear some information + * from the build that is (presumbably) only meaningful + * for the current code generation. + * This includes the create_leaf callback itself, so we make a copy + * of the build first. + */ +static __isl_give isl_ast_graft_list *call_create_leaf( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + isl_set *guard; + isl_ast_node *node; + isl_ast_graft *graft; + isl_ast_build *user_build; + + guard = isl_ast_build_get_pending(build); + user_build = isl_ast_build_copy(build); + user_build = isl_ast_build_replace_pending_by_guard(user_build, + isl_set_copy(guard)); + user_build = isl_ast_build_set_executed(user_build, executed); + user_build = isl_ast_build_clear_local_info(user_build); + if (!user_build) + node = NULL; + else + node = build->create_leaf(user_build, build->create_leaf_user); + graft = isl_ast_graft_alloc(node, build); + graft = isl_ast_graft_add_guard(graft, guard, build); + isl_ast_build_free(build); + return isl_ast_graft_list_from_ast_graft(graft); +} + +static __isl_give isl_ast_graft_list *build_ast_from_child( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed); + +/* Generate an AST after having handled the complete schedule + * of this call to the code generator or the complete band + * if we are generating an AST from a schedule tree. + * + * If we are inside a band node, then move on to the child of the band. + * + * If the user has specified a create_leaf callback, control + * is passed to the user in call_create_leaf. + * + * Otherwise, we generate one or more calls for each individual + * domain in generate_domain. + */ +static __isl_give isl_ast_graft_list *generate_inner_level( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + isl_ctx *ctx; + struct isl_generate_domain_data data = { build }; + + if (!build || !executed) + goto error; + + if (isl_ast_build_has_schedule_node(build)) { + isl_schedule_node *node; + node = isl_ast_build_get_schedule_node(build); + build = isl_ast_build_reset_schedule_node(build); + return build_ast_from_child(build, node, executed); + } + + if (build->create_leaf) + return call_create_leaf(executed, build); + + ctx = isl_union_map_get_ctx(executed); + data.list = isl_ast_graft_list_alloc(ctx, 0); + if (isl_union_map_foreach_map(executed, &generate_domain, &data) < 0) + data.list = isl_ast_graft_list_free(data.list); + + if (0) +error: data.list = NULL; + isl_ast_build_free(build); + isl_union_map_free(executed); + return data.list; +} + +/* Call the before_each_for callback, if requested by the user. + */ +static __isl_give isl_ast_node *before_each_for(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build) +{ + isl_id *id; + + if (!node || !build) + return isl_ast_node_free(node); + if (!build->before_each_for) + return node; + id = build->before_each_for(build, build->before_each_for_user); + node = isl_ast_node_set_annotation(node, id); + return node; +} + +/* Call the after_each_for callback, if requested by the user. + */ +static __isl_give isl_ast_graft *after_each_for(__isl_take isl_ast_graft *graft, + __isl_keep isl_ast_build *build) +{ + if (!graft || !build) + return isl_ast_graft_free(graft); + if (!build->after_each_for) + return graft; + graft->node = build->after_each_for(graft->node, build, + build->after_each_for_user); + if (!graft->node) + return isl_ast_graft_free(graft); + return graft; +} + +/* Plug in all the know values of the current and outer dimensions + * in the domain of "executed". In principle, we only need to plug + * in the known value of the current dimension since the values of + * outer dimensions have been plugged in already. + * However, it turns out to be easier to just plug in all known values. + */ +static __isl_give isl_union_map *plug_in_values( + __isl_take isl_union_map *executed, __isl_keep isl_ast_build *build) +{ + return isl_ast_build_substitute_values_union_map_domain(build, + executed); +} + +/* Check if the constraint "c" is a lower bound on dimension "pos", + * an upper bound, or independent of dimension "pos". + */ +static int constraint_type(isl_constraint *c, int pos) +{ + if (isl_constraint_is_lower_bound(c, isl_dim_set, pos)) + return 1; + if (isl_constraint_is_upper_bound(c, isl_dim_set, pos)) + return 2; + return 0; +} + +/* Compare the types of the constraints "a" and "b", + * resulting in constraints that are independent of "depth" + * to be sorted before the lower bounds on "depth", which in + * turn are sorted before the upper bounds on "depth". + */ +static int cmp_constraint(__isl_keep isl_constraint *a, + __isl_keep isl_constraint *b, void *user) +{ + int *depth = user; + int t1 = constraint_type(a, *depth); + int t2 = constraint_type(b, *depth); + + return t1 - t2; +} + +/* Extract a lower bound on dimension "pos" from constraint "c". + * + * If the constraint is of the form + * + * a x + f(...) >= 0 + * + * then we essentially return + * + * l = ceil(-f(...)/a) + * + * However, if the current dimension is strided, then we need to make + * sure that the lower bound we construct is of the form + * + * f + s a + * + * with f the offset and s the stride. + * We therefore compute + * + * f + s * ceil((l - f)/s) + */ +static __isl_give isl_aff *lower_bound(__isl_keep isl_constraint *c, + int pos, __isl_keep isl_ast_build *build) +{ + isl_aff *aff; + + aff = isl_constraint_get_bound(c, isl_dim_set, pos); + aff = isl_aff_ceil(aff); + + if (isl_ast_build_has_stride(build, pos)) { + isl_aff *offset; + isl_val *stride; + + offset = isl_ast_build_get_offset(build, pos); + stride = isl_ast_build_get_stride(build, pos); + + aff = isl_aff_sub(aff, isl_aff_copy(offset)); + aff = isl_aff_scale_down_val(aff, isl_val_copy(stride)); + aff = isl_aff_ceil(aff); + aff = isl_aff_scale_val(aff, stride); + aff = isl_aff_add(aff, offset); + } + + aff = isl_ast_build_compute_gist_aff(build, aff); + + return aff; +} + +/* Return the exact lower bound (or upper bound if "upper" is set) + * of "domain" as a piecewise affine expression. + * + * If we are computing a lower bound (of a strided dimension), then + * we need to make sure it is of the form + * + * f + s a + * + * where f is the offset and s is the stride. + * We therefore need to include the stride constraint before computing + * the minimum. + */ +static __isl_give isl_pw_aff *exact_bound(__isl_keep isl_set *domain, + __isl_keep isl_ast_build *build, int upper) +{ + isl_set *stride; + isl_map *it_map; + isl_pw_aff *pa; + isl_pw_multi_aff *pma; + + domain = isl_set_copy(domain); + if (!upper) { + stride = isl_ast_build_get_stride_constraint(build); + domain = isl_set_intersect(domain, stride); + } + it_map = isl_ast_build_map_to_iterator(build, domain); + if (upper) + pma = isl_map_lexmax_pw_multi_aff(it_map); + else + pma = isl_map_lexmin_pw_multi_aff(it_map); + pa = isl_pw_multi_aff_get_pw_aff(pma, 0); + isl_pw_multi_aff_free(pma); + pa = isl_ast_build_compute_gist_pw_aff(build, pa); + pa = isl_pw_aff_coalesce(pa); + + return pa; +} + +/* Callback for sorting the isl_pw_aff_list passed to reduce_list and + * remove_redundant_lower_bounds. + */ +static int reduce_list_cmp(__isl_keep isl_pw_aff *a, __isl_keep isl_pw_aff *b, + void *user) +{ + return isl_pw_aff_plain_cmp(a, b); +} + +/* Given a list of lower bounds "list", remove those that are redundant + * with respect to the other bounds in "list" and the domain of "build". + * + * We first sort the bounds in the same way as they would be sorted + * by set_for_node_expressions so that we can try and remove the last + * bounds first. + * + * For a lower bound to be effective, there needs to be at least + * one domain element for which it is larger than all other lower bounds. + * For each lower bound we therefore intersect the domain with + * the conditions that it is larger than all other bounds and + * check whether the result is empty. If so, the bound can be removed. + */ +static __isl_give isl_pw_aff_list *remove_redundant_lower_bounds( + __isl_take isl_pw_aff_list *list, __isl_keep isl_ast_build *build) +{ + int i, j, n; + isl_set *domain; + + list = isl_pw_aff_list_sort(list, &reduce_list_cmp, NULL); + if (!list) + return NULL; + + n = isl_pw_aff_list_n_pw_aff(list); + if (n <= 1) + return list; + + domain = isl_ast_build_get_domain(build); + + for (i = n - 1; i >= 0; --i) { + isl_pw_aff *pa_i; + isl_set *domain_i; + int empty; + + domain_i = isl_set_copy(domain); + pa_i = isl_pw_aff_list_get_pw_aff(list, i); + + for (j = 0; j < n; ++j) { + isl_pw_aff *pa_j; + isl_set *better; + + if (j == i) + continue; + + pa_j = isl_pw_aff_list_get_pw_aff(list, j); + better = isl_pw_aff_gt_set(isl_pw_aff_copy(pa_i), pa_j); + domain_i = isl_set_intersect(domain_i, better); + } + + empty = isl_set_is_empty(domain_i); + + isl_set_free(domain_i); + isl_pw_aff_free(pa_i); + + if (empty < 0) + goto error; + if (!empty) + continue; + list = isl_pw_aff_list_drop(list, i, 1); + n--; + } + + isl_set_free(domain); + + return list; +error: + isl_set_free(domain); + return isl_pw_aff_list_free(list); +} + +/* Extract a lower bound on dimension "pos" from each constraint + * in "constraints" and return the list of lower bounds. + * If "constraints" has zero elements, then we extract a lower bound + * from "domain" instead. + * + * If the current dimension is strided, then the lower bound + * is adjusted by lower_bound to match the stride information. + * This modification may make one or more lower bounds redundant + * with respect to the other lower bounds. We therefore check + * for this condition and remove the redundant lower bounds. + */ +static __isl_give isl_pw_aff_list *lower_bounds( + __isl_keep isl_constraint_list *constraints, int pos, + __isl_keep isl_set *domain, __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_pw_aff_list *list; + int i, n; + + if (!build) + return NULL; + + n = isl_constraint_list_n_constraint(constraints); + if (n == 0) { + isl_pw_aff *pa; + pa = exact_bound(domain, build, 0); + return isl_pw_aff_list_from_pw_aff(pa); + } + + ctx = isl_ast_build_get_ctx(build); + list = isl_pw_aff_list_alloc(ctx,n); + + for (i = 0; i < n; ++i) { + isl_aff *aff; + isl_constraint *c; + + c = isl_constraint_list_get_constraint(constraints, i); + aff = lower_bound(c, pos, build); + isl_constraint_free(c); + list = isl_pw_aff_list_add(list, isl_pw_aff_from_aff(aff)); + } + + if (isl_ast_build_has_stride(build, pos)) + list = remove_redundant_lower_bounds(list, build); + + return list; +} + +/* Extract an upper bound on dimension "pos" from each constraint + * in "constraints" and return the list of upper bounds. + * If "constraints" has zero elements, then we extract an upper bound + * from "domain" instead. + */ +static __isl_give isl_pw_aff_list *upper_bounds( + __isl_keep isl_constraint_list *constraints, int pos, + __isl_keep isl_set *domain, __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_pw_aff_list *list; + int i, n; + + n = isl_constraint_list_n_constraint(constraints); + if (n == 0) { + isl_pw_aff *pa; + pa = exact_bound(domain, build, 1); + return isl_pw_aff_list_from_pw_aff(pa); + } + + ctx = isl_ast_build_get_ctx(build); + list = isl_pw_aff_list_alloc(ctx,n); + + for (i = 0; i < n; ++i) { + isl_aff *aff; + isl_constraint *c; + + c = isl_constraint_list_get_constraint(constraints, i); + aff = isl_constraint_get_bound(c, isl_dim_set, pos); + isl_constraint_free(c); + aff = isl_aff_floor(aff); + list = isl_pw_aff_list_add(list, isl_pw_aff_from_aff(aff)); + } + + return list; +} + +/* Return an isl_ast_expr that performs the reduction of type "type" + * on AST expressions corresponding to the elements in "list". + * + * The list is assumed to contain at least one element. + * If the list contains exactly one element, then the returned isl_ast_expr + * simply computes that affine expression. + * If the list contains more than one element, then we sort it + * using a fairly abitrary but hopefully reasonably stable order. + */ +static __isl_give isl_ast_expr *reduce_list(enum isl_ast_op_type type, + __isl_keep isl_pw_aff_list *list, __isl_keep isl_ast_build *build) +{ + int i, n; + isl_ctx *ctx; + isl_ast_expr *expr; + + if (!list) + return NULL; + + n = isl_pw_aff_list_n_pw_aff(list); + + if (n == 1) + return isl_ast_build_expr_from_pw_aff_internal(build, + isl_pw_aff_list_get_pw_aff(list, 0)); + + ctx = isl_pw_aff_list_get_ctx(list); + expr = isl_ast_expr_alloc_op(ctx, type, n); + if (!expr) + return NULL; + + list = isl_pw_aff_list_copy(list); + list = isl_pw_aff_list_sort(list, &reduce_list_cmp, NULL); + if (!list) + return isl_ast_expr_free(expr); + + for (i = 0; i < n; ++i) { + isl_ast_expr *expr_i; + + expr_i = isl_ast_build_expr_from_pw_aff_internal(build, + isl_pw_aff_list_get_pw_aff(list, i)); + if (!expr_i) + goto error; + expr->u.op.args[i] = expr_i; + } + + isl_pw_aff_list_free(list); + return expr; +error: + isl_pw_aff_list_free(list); + isl_ast_expr_free(expr); + return NULL; +} + +/* Add guards implied by the "generated constraints", + * but not (necessarily) enforced by the generated AST to "guard". + * In particular, if there is any stride constraints, + * then add the guard implied by those constraints. + * If we have generated a degenerate loop, then add the guard + * implied by "bounds" on the outer dimensions, i.e., the guard + * that ensures that the single value actually exists. + * Since there may also be guards implied by a combination + * of these constraints, we first combine them before + * deriving the implied constraints. + */ +static __isl_give isl_set *add_implied_guards(__isl_take isl_set *guard, + int degenerate, __isl_keep isl_basic_set *bounds, + __isl_keep isl_ast_build *build) +{ + int depth, has_stride; + isl_space *space; + isl_set *dom, *set; + + depth = isl_ast_build_get_depth(build); + has_stride = isl_ast_build_has_stride(build, depth); + if (!has_stride && !degenerate) + return guard; + + space = isl_basic_set_get_space(bounds); + dom = isl_set_universe(space); + + if (degenerate) { + bounds = isl_basic_set_copy(bounds); + bounds = isl_basic_set_drop_constraints_not_involving_dims( + bounds, isl_dim_set, depth, 1); + set = isl_set_from_basic_set(bounds); + dom = isl_set_intersect(dom, set); + } + + if (has_stride) { + set = isl_ast_build_get_stride_constraint(build); + dom = isl_set_intersect(dom, set); + } + + dom = isl_set_eliminate(dom, isl_dim_set, depth, 1); + dom = isl_ast_build_compute_gist(build, dom); + guard = isl_set_intersect(guard, dom); + + return guard; +} + +/* Update "graft" based on "sub_build" for the degenerate case. + * + * "build" is the build in which graft->node was created + * "sub_build" contains information about the current level itself, + * including the single value attained. + * + * We set the initialization part of the for loop to the single + * value attained by the current dimension. + * The increment and condition are not strictly needed as the are known + * to be "1" and "iterator <= value" respectively. + */ +static __isl_give isl_ast_graft *refine_degenerate( + __isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build, + __isl_keep isl_ast_build *sub_build) +{ + isl_pw_aff *value; + + if (!graft || !sub_build) + return isl_ast_graft_free(graft); + + value = isl_pw_aff_copy(sub_build->value); + + graft->node->u.f.init = isl_ast_build_expr_from_pw_aff_internal(build, + value); + if (!graft->node->u.f.init) + return isl_ast_graft_free(graft); + + return graft; +} + +/* Return the intersection of constraints in "list" as a set. + */ +static __isl_give isl_set *intersect_constraints( + __isl_keep isl_constraint_list *list) +{ + int i, n; + isl_basic_set *bset; + + n = isl_constraint_list_n_constraint(list); + if (n < 1) + isl_die(isl_constraint_list_get_ctx(list), isl_error_internal, + "expecting at least one constraint", return NULL); + + bset = isl_basic_set_from_constraint( + isl_constraint_list_get_constraint(list, 0)); + for (i = 1; i < n; ++i) { + isl_basic_set *bset_i; + + bset_i = isl_basic_set_from_constraint( + isl_constraint_list_get_constraint(list, i)); + bset = isl_basic_set_intersect(bset, bset_i); + } + + return isl_set_from_basic_set(bset); +} + +/* Compute the constraints on the outer dimensions enforced by + * graft->node and add those constraints to graft->enforced, + * in case the upper bound is expressed as a set "upper". + * + * In particular, if l(...) is a lower bound in "lower", and + * + * -a i + f(...) >= 0 or a i <= f(...) + * + * is an upper bound ocnstraint on the current dimension i, + * then the for loop enforces the constraint + * + * -a l(...) + f(...) >= 0 or a l(...) <= f(...) + * + * We therefore simply take each lower bound in turn, plug it into + * the upper bounds and compute the intersection over all lower bounds. + * + * If a lower bound is a rational expression, then + * isl_basic_set_preimage_multi_aff will force this rational + * expression to have only integer values. However, the loop + * itself does not enforce this integrality constraint. We therefore + * use the ceil of the lower bounds instead of the lower bounds themselves. + * Other constraints will make sure that the for loop is only executed + * when each of the lower bounds attains an integral value. + * In particular, potentially rational values only occur in + * lower_bound if the offset is a (seemingly) rational expression, + * but then outer conditions will make sure that this rational expression + * only attains integer values. + */ +static __isl_give isl_ast_graft *set_enforced_from_set( + __isl_take isl_ast_graft *graft, + __isl_keep isl_pw_aff_list *lower, int pos, __isl_keep isl_set *upper) +{ + isl_space *space; + isl_basic_set *enforced; + isl_pw_multi_aff *pma; + int i, n; + + if (!graft || !lower) + return isl_ast_graft_free(graft); + + space = isl_set_get_space(upper); + enforced = isl_basic_set_universe(isl_space_copy(space)); + + space = isl_space_map_from_set(space); + pma = isl_pw_multi_aff_identity(space); + + n = isl_pw_aff_list_n_pw_aff(lower); + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + isl_set *enforced_i; + isl_basic_set *hull; + isl_pw_multi_aff *pma_i; + + pa = isl_pw_aff_list_get_pw_aff(lower, i); + pa = isl_pw_aff_ceil(pa); + pma_i = isl_pw_multi_aff_copy(pma); + pma_i = isl_pw_multi_aff_set_pw_aff(pma_i, pos, pa); + enforced_i = isl_set_copy(upper); + enforced_i = isl_set_preimage_pw_multi_aff(enforced_i, pma_i); + hull = isl_set_simple_hull(enforced_i); + enforced = isl_basic_set_intersect(enforced, hull); + } + + isl_pw_multi_aff_free(pma); + + graft = isl_ast_graft_enforce(graft, enforced); + + return graft; +} + +/* Compute the constraints on the outer dimensions enforced by + * graft->node and add those constraints to graft->enforced, + * in case the upper bound is expressed as + * a list of affine expressions "upper". + * + * The enforced condition is that each lower bound expression is less + * than or equal to each upper bound expression. + */ +static __isl_give isl_ast_graft *set_enforced_from_list( + __isl_take isl_ast_graft *graft, + __isl_keep isl_pw_aff_list *lower, __isl_keep isl_pw_aff_list *upper) +{ + isl_set *cond; + isl_basic_set *enforced; + + lower = isl_pw_aff_list_copy(lower); + upper = isl_pw_aff_list_copy(upper); + cond = isl_pw_aff_list_le_set(lower, upper); + enforced = isl_set_simple_hull(cond); + graft = isl_ast_graft_enforce(graft, enforced); + + return graft; +} + +/* Does "aff" have a negative constant term? + */ +static isl_stat aff_constant_is_negative(__isl_take isl_set *set, + __isl_take isl_aff *aff, void *user) +{ + int *neg = user; + isl_val *v; + + v = isl_aff_get_constant_val(aff); + *neg = isl_val_is_neg(v); + isl_val_free(v); + isl_set_free(set); + isl_aff_free(aff); + + return *neg ? isl_stat_ok : isl_stat_error; +} + +/* Does "pa" have a negative constant term over its entire domain? + */ +static isl_stat pw_aff_constant_is_negative(__isl_take isl_pw_aff *pa, + void *user) +{ + isl_stat r; + int *neg = user; + + r = isl_pw_aff_foreach_piece(pa, &aff_constant_is_negative, user); + isl_pw_aff_free(pa); + + return (*neg && r >= 0) ? isl_stat_ok : isl_stat_error; +} + +/* Does each element in "list" have a negative constant term? + * + * The callback terminates the iteration as soon an element has been + * found that does not have a negative constant term. + */ +static int list_constant_is_negative(__isl_keep isl_pw_aff_list *list) +{ + int neg = 1; + + if (isl_pw_aff_list_foreach(list, + &pw_aff_constant_is_negative, &neg) < 0 && neg) + return -1; + + return neg; +} + +/* Add 1 to each of the elements in "list", where each of these elements + * is defined over the internal schedule space of "build". + */ +static __isl_give isl_pw_aff_list *list_add_one( + __isl_take isl_pw_aff_list *list, __isl_keep isl_ast_build *build) +{ + int i, n; + isl_space *space; + isl_aff *aff; + isl_pw_aff *one; + + space = isl_ast_build_get_space(build, 1); + aff = isl_aff_zero_on_domain(isl_local_space_from_space(space)); + aff = isl_aff_add_constant_si(aff, 1); + one = isl_pw_aff_from_aff(aff); + + n = isl_pw_aff_list_n_pw_aff(list); + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + pa = isl_pw_aff_list_get_pw_aff(list, i); + pa = isl_pw_aff_add(pa, isl_pw_aff_copy(one)); + list = isl_pw_aff_list_set_pw_aff(list, i, pa); + } + + isl_pw_aff_free(one); + + return list; +} + +/* Set the condition part of the for node graft->node in case + * the upper bound is represented as a list of piecewise affine expressions. + * + * In particular, set the condition to + * + * iterator <= min(list of upper bounds) + * + * If each of the upper bounds has a negative constant term, then + * set the condition to + * + * iterator < min(list of (upper bound + 1)s) + * + */ +static __isl_give isl_ast_graft *set_for_cond_from_list( + __isl_take isl_ast_graft *graft, __isl_keep isl_pw_aff_list *list, + __isl_keep isl_ast_build *build) +{ + int neg; + isl_ast_expr *bound, *iterator, *cond; + enum isl_ast_op_type type = isl_ast_op_le; + + if (!graft || !list) + return isl_ast_graft_free(graft); + + neg = list_constant_is_negative(list); + if (neg < 0) + return isl_ast_graft_free(graft); + list = isl_pw_aff_list_copy(list); + if (neg) { + list = list_add_one(list, build); + type = isl_ast_op_lt; + } + + bound = reduce_list(isl_ast_op_min, list, build); + iterator = isl_ast_expr_copy(graft->node->u.f.iterator); + cond = isl_ast_expr_alloc_binary(type, iterator, bound); + graft->node->u.f.cond = cond; + + isl_pw_aff_list_free(list); + if (!graft->node->u.f.cond) + return isl_ast_graft_free(graft); + return graft; +} + +/* Set the condition part of the for node graft->node in case + * the upper bound is represented as a set. + */ +static __isl_give isl_ast_graft *set_for_cond_from_set( + __isl_take isl_ast_graft *graft, __isl_keep isl_set *set, + __isl_keep isl_ast_build *build) +{ + isl_ast_expr *cond; + + if (!graft) + return NULL; + + cond = isl_ast_build_expr_from_set_internal(build, isl_set_copy(set)); + graft->node->u.f.cond = cond; + if (!graft->node->u.f.cond) + return isl_ast_graft_free(graft); + return graft; +} + +/* Construct an isl_ast_expr for the increment (i.e., stride) of + * the current dimension. + */ +static __isl_give isl_ast_expr *for_inc(__isl_keep isl_ast_build *build) +{ + int depth; + isl_val *v; + isl_ctx *ctx; + + if (!build) + return NULL; + ctx = isl_ast_build_get_ctx(build); + depth = isl_ast_build_get_depth(build); + + if (!isl_ast_build_has_stride(build, depth)) + return isl_ast_expr_alloc_int_si(ctx, 1); + + v = isl_ast_build_get_stride(build, depth); + return isl_ast_expr_from_val(v); +} + +/* Should we express the loop condition as + * + * iterator <= min(list of upper bounds) + * + * or as a conjunction of constraints? + * + * The first is constructed from a list of upper bounds. + * The second is constructed from a set. + * + * If there are no upper bounds in "constraints", then this could mean + * that "domain" simply doesn't have an upper bound or that we didn't + * pick any upper bound. In the first case, we want to generate the + * loop condition as a(n empty) conjunction of constraints + * In the second case, we will compute + * a single upper bound from "domain" and so we use the list form. + * + * If there are upper bounds in "constraints", + * then we use the list form iff the atomic_upper_bound option is set. + */ +static int use_upper_bound_list(isl_ctx *ctx, int n_upper, + __isl_keep isl_set *domain, int depth) +{ + if (n_upper > 0) + return isl_options_get_ast_build_atomic_upper_bound(ctx); + else + return isl_set_dim_has_upper_bound(domain, isl_dim_set, depth); +} + +/* Fill in the expressions of the for node in graft->node. + * + * In particular, + * - set the initialization part of the loop to the maximum of the lower bounds + * - extract the increment from the stride of the current dimension + * - construct the for condition either based on a list of upper bounds + * or on a set of upper bound constraints. + */ +static __isl_give isl_ast_graft *set_for_node_expressions( + __isl_take isl_ast_graft *graft, __isl_keep isl_pw_aff_list *lower, + int use_list, __isl_keep isl_pw_aff_list *upper_list, + __isl_keep isl_set *upper_set, __isl_keep isl_ast_build *build) +{ + isl_ast_node *node; + + if (!graft) + return NULL; + + build = isl_ast_build_copy(build); + + node = graft->node; + node->u.f.init = reduce_list(isl_ast_op_max, lower, build); + node->u.f.inc = for_inc(build); + + if (!node->u.f.init || !node->u.f.inc) + graft = isl_ast_graft_free(graft); + + if (use_list) + graft = set_for_cond_from_list(graft, upper_list, build); + else + graft = set_for_cond_from_set(graft, upper_set, build); + + isl_ast_build_free(build); + + return graft; +} + +/* Update "graft" based on "bounds" and "domain" for the generic, + * non-degenerate, case. + * + * "c_lower" and "c_upper" contain the lower and upper bounds + * that the loop node should express. + * "domain" is the subset of the intersection of the constraints + * for which some code is executed. + * + * There may be zero lower bounds or zero upper bounds in "constraints" + * in case the list of constraints was created + * based on the atomic option or based on separation with explicit bounds. + * In that case, we use "domain" to derive lower and/or upper bounds. + * + * We first compute a list of one or more lower bounds. + * + * Then we decide if we want to express the condition as + * + * iterator <= min(list of upper bounds) + * + * or as a conjunction of constraints. + * + * The set of enforced constraints is then computed either based on + * a list of upper bounds or on a set of upper bound constraints. + * We do not compute any enforced constraints if we were forced + * to compute a lower or upper bound using exact_bound. The domains + * of the resulting expressions may imply some bounds on outer dimensions + * that we do not want to appear in the enforced constraints since + * they are not actually enforced by the corresponding code. + * + * Finally, we fill in the expressions of the for node. + */ +static __isl_give isl_ast_graft *refine_generic_bounds( + __isl_take isl_ast_graft *graft, + __isl_take isl_constraint_list *c_lower, + __isl_take isl_constraint_list *c_upper, + __isl_keep isl_set *domain, __isl_keep isl_ast_build *build) +{ + int depth; + isl_ctx *ctx; + isl_pw_aff_list *lower; + int use_list; + isl_set *upper_set = NULL; + isl_pw_aff_list *upper_list = NULL; + int n_lower, n_upper; + + if (!graft || !c_lower || !c_upper || !build) + goto error; + + depth = isl_ast_build_get_depth(build); + ctx = isl_ast_graft_get_ctx(graft); + + n_lower = isl_constraint_list_n_constraint(c_lower); + n_upper = isl_constraint_list_n_constraint(c_upper); + + use_list = use_upper_bound_list(ctx, n_upper, domain, depth); + + lower = lower_bounds(c_lower, depth, domain, build); + + if (use_list) + upper_list = upper_bounds(c_upper, depth, domain, build); + else if (n_upper > 0) + upper_set = intersect_constraints(c_upper); + else + upper_set = isl_set_universe(isl_set_get_space(domain)); + + if (n_lower == 0 || n_upper == 0) + ; + else if (use_list) + graft = set_enforced_from_list(graft, lower, upper_list); + else + graft = set_enforced_from_set(graft, lower, depth, upper_set); + + graft = set_for_node_expressions(graft, lower, use_list, upper_list, + upper_set, build); + + isl_pw_aff_list_free(lower); + isl_pw_aff_list_free(upper_list); + isl_set_free(upper_set); + isl_constraint_list_free(c_lower); + isl_constraint_list_free(c_upper); + + return graft; +error: + isl_constraint_list_free(c_lower); + isl_constraint_list_free(c_upper); + return isl_ast_graft_free(graft); +} + +/* Internal data structure used inside count_constraints to keep + * track of the number of constraints that are independent of dimension "pos", + * the lower bounds in "pos" and the upper bounds in "pos". + */ +struct isl_ast_count_constraints_data { + int pos; + + int n_indep; + int n_lower; + int n_upper; +}; + +/* Increment data->n_indep, data->lower or data->upper depending + * on whether "c" is independenct of dimensions data->pos, + * a lower bound or an upper bound. + */ +static isl_stat count_constraints(__isl_take isl_constraint *c, void *user) +{ + struct isl_ast_count_constraints_data *data = user; + + if (isl_constraint_is_lower_bound(c, isl_dim_set, data->pos)) + data->n_lower++; + else if (isl_constraint_is_upper_bound(c, isl_dim_set, data->pos)) + data->n_upper++; + else + data->n_indep++; + + isl_constraint_free(c); + + return isl_stat_ok; +} + +/* Update "graft" based on "bounds" and "domain" for the generic, + * non-degenerate, case. + * + * "list" respresent the list of bounds that need to be encoded by + * the for loop. Only the constraints that involve the iterator + * are relevant here. The other constraints are taken care of by + * the caller and are included in the generated constraints of "build". + * "domain" is the subset of the intersection of the constraints + * for which some code is executed. + * "build" is the build in which graft->node was created. + * + * We separate lower bounds, upper bounds and constraints that + * are independent of the loop iterator. + * + * The actual for loop bounds are generated in refine_generic_bounds. + */ +static __isl_give isl_ast_graft *refine_generic_split( + __isl_take isl_ast_graft *graft, __isl_take isl_constraint_list *list, + __isl_keep isl_set *domain, __isl_keep isl_ast_build *build) +{ + struct isl_ast_count_constraints_data data; + isl_constraint_list *lower; + isl_constraint_list *upper; + + if (!list) + return isl_ast_graft_free(graft); + + data.pos = isl_ast_build_get_depth(build); + + list = isl_constraint_list_sort(list, &cmp_constraint, &data.pos); + if (!list) + return isl_ast_graft_free(graft); + + data.n_indep = data.n_lower = data.n_upper = 0; + if (isl_constraint_list_foreach(list, &count_constraints, &data) < 0) { + isl_constraint_list_free(list); + return isl_ast_graft_free(graft); + } + + lower = isl_constraint_list_drop(list, 0, data.n_indep); + upper = isl_constraint_list_copy(lower); + lower = isl_constraint_list_drop(lower, data.n_lower, data.n_upper); + upper = isl_constraint_list_drop(upper, 0, data.n_lower); + + return refine_generic_bounds(graft, lower, upper, domain, build); +} + +/* Update "graft" based on "bounds" and "domain" for the generic, + * non-degenerate, case. + * + * "bounds" respresent the bounds that need to be encoded by + * the for loop (or a guard around the for loop). + * "domain" is the subset of "bounds" for which some code is executed. + * "build" is the build in which graft->node was created. + * + * We break up "bounds" into a list of constraints and continue with + * refine_generic_split. + */ +static __isl_give isl_ast_graft *refine_generic( + __isl_take isl_ast_graft *graft, + __isl_keep isl_basic_set *bounds, __isl_keep isl_set *domain, + __isl_keep isl_ast_build *build) +{ + isl_constraint_list *list; + + if (!build || !graft) + return isl_ast_graft_free(graft); + + list = isl_basic_set_get_constraint_list(bounds); + + graft = refine_generic_split(graft, list, domain, build); + + return graft; +} + +/* Create a for node for the current level. + * + * Mark the for node degenerate if "degenerate" is set. + */ +static __isl_give isl_ast_node *create_for(__isl_keep isl_ast_build *build, + int degenerate) +{ + int depth; + isl_id *id; + isl_ast_node *node; + + if (!build) + return NULL; + + depth = isl_ast_build_get_depth(build); + id = isl_ast_build_get_iterator_id(build, depth); + node = isl_ast_node_alloc_for(id); + if (degenerate) + node = isl_ast_node_for_mark_degenerate(node); + + return node; +} + +/* If the ast_build_exploit_nested_bounds option is set, then return + * the constraints enforced by all elements in "list". + * Otherwise, return the universe. + */ +static __isl_give isl_basic_set *extract_shared_enforced( + __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_space *space; + + if (!list) + return NULL; + + ctx = isl_ast_graft_list_get_ctx(list); + if (isl_options_get_ast_build_exploit_nested_bounds(ctx)) + return isl_ast_graft_list_extract_shared_enforced(list, build); + + space = isl_ast_build_get_space(build, 1); + return isl_basic_set_universe(space); +} + +/* Return the pending constraints of "build" that are not already taken + * care of (by a combination of "enforced" and the generated constraints + * of "build"). + */ +static __isl_give isl_set *extract_pending(__isl_keep isl_ast_build *build, + __isl_keep isl_basic_set *enforced) +{ + isl_set *guard, *context; + + guard = isl_ast_build_get_pending(build); + context = isl_set_from_basic_set(isl_basic_set_copy(enforced)); + context = isl_set_intersect(context, + isl_ast_build_get_generated(build)); + return isl_set_gist(guard, context); +} + +/* Create an AST node for the current dimension based on + * the schedule domain "bounds" and return the node encapsulated + * in an isl_ast_graft. + * + * "executed" is the current inverse schedule, taking into account + * the bounds in "bounds" + * "domain" is the domain of "executed", with inner dimensions projected out. + * It may be a strict subset of "bounds" in case "bounds" was created + * based on the atomic option or based on separation with explicit bounds. + * + * "domain" may satisfy additional equalities that result + * from intersecting "executed" with "bounds" in add_node. + * It may also satisfy some global constraints that were dropped out because + * we performed separation with explicit bounds. + * The very first step is then to copy these constraints to "bounds". + * + * Since we may be calling before_each_for and after_each_for + * callbacks, we record the current inverse schedule in the build. + * + * We consider three builds, + * "build" is the one in which the current level is created, + * "body_build" is the build in which the next level is created, + * "sub_build" is essentially the same as "body_build", except that + * the depth has not been increased yet. + * + * "build" already contains information (in strides and offsets) + * about the strides at the current level, but this information is not + * reflected in the build->domain. + * We first add this information and the "bounds" to the sub_build->domain. + * isl_ast_build_set_loop_bounds adds the stride information and + * checks whether the current dimension attains + * only a single value and whether this single value can be represented using + * a single affine expression. + * In the first case, the current level is considered "degenerate". + * In the second, sub-case, the current level is considered "eliminated". + * Eliminated levels don't need to be reflected in the AST since we can + * simply plug in the affine expression. For degenerate, but non-eliminated, + * levels, we do introduce a for node, but mark is as degenerate so that + * it can be printed as an assignment of the single value to the loop + * "iterator". + * + * If the current level is eliminated, we explicitly plug in the value + * for the current level found by isl_ast_build_set_loop_bounds in the + * inverse schedule. This ensures that if we are working on a slice + * of the domain based on information available in the inverse schedule + * and the build domain, that then this information is also reflected + * in the inverse schedule. This operation also eliminates the current + * dimension from the inverse schedule making sure no inner dimensions depend + * on the current dimension. Otherwise, we create a for node, marking + * it degenerate if appropriate. The initial for node is still incomplete + * and will be completed in either refine_degenerate or refine_generic. + * + * We then generate a sequence of grafts for the next level, + * create a surrounding graft for the current level and insert + * the for node we created (if the current level is not eliminated). + * Before creating a graft for the current level, we first extract + * hoistable constraints from the child guards and combine them + * with the pending constraints in the build. These constraints + * are used to simplify the child guards and then added to the guard + * of the current graft to ensure that they will be generated. + * If the hoisted guard is a disjunction, then we use it directly + * to gist the guards on the children before intersect it with the + * pending constraints. We do so because this disjunction is typically + * identical to the guards on the children such that these guards + * can be effectively removed completely. After the intersection, + * the gist operation would have a harder time figuring this out. + * + * Finally, we set the bounds of the for loop in either + * refine_degenerate or refine_generic. + * We do so in a context where the pending constraints of the build + * have been replaced by the guard of the current graft. + */ +static __isl_give isl_ast_graft *create_node_scaled( + __isl_take isl_union_map *executed, + __isl_take isl_basic_set *bounds, __isl_take isl_set *domain, + __isl_take isl_ast_build *build) +{ + int depth; + int degenerate, eliminated; + isl_basic_set *hull; + isl_basic_set *enforced; + isl_set *guard, *hoisted; + isl_ast_node *node = NULL; + isl_ast_graft *graft; + isl_ast_graft_list *children; + isl_ast_build *sub_build; + isl_ast_build *body_build; + + domain = isl_ast_build_eliminate_divs(build, domain); + domain = isl_set_detect_equalities(domain); + hull = isl_set_unshifted_simple_hull(isl_set_copy(domain)); + bounds = isl_basic_set_intersect(bounds, hull); + build = isl_ast_build_set_executed(build, isl_union_map_copy(executed)); + + depth = isl_ast_build_get_depth(build); + sub_build = isl_ast_build_copy(build); + bounds = isl_basic_set_remove_redundancies(bounds); + bounds = isl_ast_build_specialize_basic_set(sub_build, bounds); + sub_build = isl_ast_build_set_loop_bounds(sub_build, + isl_basic_set_copy(bounds)); + degenerate = isl_ast_build_has_value(sub_build); + eliminated = isl_ast_build_has_affine_value(sub_build, depth); + if (degenerate < 0 || eliminated < 0) + executed = isl_union_map_free(executed); + if (!degenerate) + bounds = isl_ast_build_compute_gist_basic_set(build, bounds); + sub_build = isl_ast_build_set_pending_generated(sub_build, + isl_basic_set_copy(bounds)); + if (eliminated) + executed = plug_in_values(executed, sub_build); + else + node = create_for(build, degenerate); + + body_build = isl_ast_build_copy(sub_build); + body_build = isl_ast_build_increase_depth(body_build); + if (!eliminated) + node = before_each_for(node, body_build); + children = generate_next_level(executed, + isl_ast_build_copy(body_build)); + + enforced = extract_shared_enforced(children, build); + guard = extract_pending(sub_build, enforced); + hoisted = isl_ast_graft_list_extract_hoistable_guard(children, build); + if (isl_set_n_basic_set(hoisted) > 1) + children = isl_ast_graft_list_gist_guards(children, + isl_set_copy(hoisted)); + guard = isl_set_intersect(guard, hoisted); + if (!eliminated) + guard = add_implied_guards(guard, degenerate, bounds, build); + + graft = isl_ast_graft_alloc_from_children(children, + isl_set_copy(guard), enforced, build, sub_build); + + if (!eliminated) { + isl_ast_build *for_build; + + graft = isl_ast_graft_insert_for(graft, node); + for_build = isl_ast_build_copy(build); + for_build = isl_ast_build_replace_pending_by_guard(for_build, + isl_set_copy(guard)); + if (degenerate) + graft = refine_degenerate(graft, for_build, sub_build); + else + graft = refine_generic(graft, bounds, + domain, for_build); + isl_ast_build_free(for_build); + } + isl_set_free(guard); + if (!eliminated) + graft = after_each_for(graft, body_build); + + isl_ast_build_free(body_build); + isl_ast_build_free(sub_build); + isl_ast_build_free(build); + isl_basic_set_free(bounds); + isl_set_free(domain); + + return graft; +} + +/* Internal data structure for checking if all constraints involving + * the input dimension "depth" are such that the other coefficients + * are multiples of "m", reducing "m" if they are not. + * If "m" is reduced all the way down to "1", then the check has failed + * and we break out of the iteration. + */ +struct isl_check_scaled_data { + int depth; + isl_val *m; +}; + +/* If constraint "c" involves the input dimension data->depth, + * then make sure that all the other coefficients are multiples of data->m, + * reducing data->m if needed. + * Break out of the iteration if data->m has become equal to "1". + */ +static isl_stat constraint_check_scaled(__isl_take isl_constraint *c, + void *user) +{ + struct isl_check_scaled_data *data = user; + int i, j, n; + enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_out, + isl_dim_div }; + + if (!isl_constraint_involves_dims(c, isl_dim_in, data->depth, 1)) { + isl_constraint_free(c); + return isl_stat_ok; + } + + for (i = 0; i < 4; ++i) { + n = isl_constraint_dim(c, t[i]); + for (j = 0; j < n; ++j) { + isl_val *d; + + if (t[i] == isl_dim_in && j == data->depth) + continue; + if (!isl_constraint_involves_dims(c, t[i], j, 1)) + continue; + d = isl_constraint_get_coefficient_val(c, t[i], j); + data->m = isl_val_gcd(data->m, d); + if (isl_val_is_one(data->m)) + break; + } + if (j < n) + break; + } + + isl_constraint_free(c); + + return i < 4 ? isl_stat_error : isl_stat_ok; +} + +/* For each constraint of "bmap" that involves the input dimension data->depth, + * make sure that all the other coefficients are multiples of data->m, + * reducing data->m if needed. + * Break out of the iteration if data->m has become equal to "1". + */ +static isl_stat basic_map_check_scaled(__isl_take isl_basic_map *bmap, + void *user) +{ + isl_stat r; + + r = isl_basic_map_foreach_constraint(bmap, + &constraint_check_scaled, user); + isl_basic_map_free(bmap); + + return r; +} + +/* For each constraint of "map" that involves the input dimension data->depth, + * make sure that all the other coefficients are multiples of data->m, + * reducing data->m if needed. + * Break out of the iteration if data->m has become equal to "1". + */ +static isl_stat map_check_scaled(__isl_take isl_map *map, void *user) +{ + isl_stat r; + + r = isl_map_foreach_basic_map(map, &basic_map_check_scaled, user); + isl_map_free(map); + + return r; +} + +/* Create an AST node for the current dimension based on + * the schedule domain "bounds" and return the node encapsulated + * in an isl_ast_graft. + * + * "executed" is the current inverse schedule, taking into account + * the bounds in "bounds" + * "domain" is the domain of "executed", with inner dimensions projected out. + * + * + * Before moving on to the actual AST node construction in create_node_scaled, + * we first check if the current dimension is strided and if we can scale + * down this stride. Note that we only do this if the ast_build_scale_strides + * option is set. + * + * In particular, let the current dimension take on values + * + * f + s a + * + * with a an integer. We check if we can find an integer m that (obviously) + * divides both f and s. + * + * If so, we check if the current dimension only appears in constraints + * where the coefficients of the other variables are multiples of m. + * We perform this extra check to avoid the risk of introducing + * divisions by scaling down the current dimension. + * + * If so, we scale the current dimension down by a factor of m. + * That is, we plug in + * + * i = m i' (1) + * + * Note that in principle we could always scale down strided loops + * by plugging in + * + * i = f + s i' + * + * but this may result in i' taking on larger values than the original i, + * due to the shift by "f". + * By constrast, the scaling in (1) can only reduce the (absolute) value "i". + */ +static __isl_give isl_ast_graft *create_node(__isl_take isl_union_map *executed, + __isl_take isl_basic_set *bounds, __isl_take isl_set *domain, + __isl_take isl_ast_build *build) +{ + struct isl_check_scaled_data data; + isl_ctx *ctx; + isl_aff *offset; + isl_val *d; + + ctx = isl_ast_build_get_ctx(build); + if (!isl_options_get_ast_build_scale_strides(ctx)) + return create_node_scaled(executed, bounds, domain, build); + + data.depth = isl_ast_build_get_depth(build); + if (!isl_ast_build_has_stride(build, data.depth)) + return create_node_scaled(executed, bounds, domain, build); + + offset = isl_ast_build_get_offset(build, data.depth); + data.m = isl_ast_build_get_stride(build, data.depth); + if (!data.m) + offset = isl_aff_free(offset); + offset = isl_aff_scale_down_val(offset, isl_val_copy(data.m)); + d = isl_aff_get_denominator_val(offset); + if (!d) + executed = isl_union_map_free(executed); + + if (executed && isl_val_is_divisible_by(data.m, d)) + data.m = isl_val_div(data.m, d); + else { + data.m = isl_val_set_si(data.m, 1); + isl_val_free(d); + } + + if (!isl_val_is_one(data.m)) { + if (isl_union_map_foreach_map(executed, &map_check_scaled, + &data) < 0 && + !isl_val_is_one(data.m)) + executed = isl_union_map_free(executed); + } + + if (!isl_val_is_one(data.m)) { + isl_space *space; + isl_multi_aff *ma; + isl_aff *aff; + isl_map *map; + isl_union_map *umap; + + space = isl_ast_build_get_space(build, 1); + space = isl_space_map_from_set(space); + ma = isl_multi_aff_identity(space); + aff = isl_multi_aff_get_aff(ma, data.depth); + aff = isl_aff_scale_val(aff, isl_val_copy(data.m)); + ma = isl_multi_aff_set_aff(ma, data.depth, aff); + + bounds = isl_basic_set_preimage_multi_aff(bounds, + isl_multi_aff_copy(ma)); + domain = isl_set_preimage_multi_aff(domain, + isl_multi_aff_copy(ma)); + map = isl_map_reverse(isl_map_from_multi_aff(ma)); + umap = isl_union_map_from_map(map); + executed = isl_union_map_apply_domain(executed, + isl_union_map_copy(umap)); + build = isl_ast_build_scale_down(build, isl_val_copy(data.m), + umap); + } + isl_aff_free(offset); + isl_val_free(data.m); + + return create_node_scaled(executed, bounds, domain, build); +} + +/* Add the basic set to the list that "user" points to. + */ +static isl_stat collect_basic_set(__isl_take isl_basic_set *bset, void *user) +{ + isl_basic_set_list **list = user; + + *list = isl_basic_set_list_add(*list, bset); + + return isl_stat_ok; +} + +/* Extract the basic sets of "set" and collect them in an isl_basic_set_list. + */ +static __isl_give isl_basic_set_list *isl_basic_set_list_from_set( + __isl_take isl_set *set) +{ + int n; + isl_ctx *ctx; + isl_basic_set_list *list; + + if (!set) + return NULL; + + ctx = isl_set_get_ctx(set); + + n = isl_set_n_basic_set(set); + list = isl_basic_set_list_alloc(ctx, n); + if (isl_set_foreach_basic_set(set, &collect_basic_set, &list) < 0) + list = isl_basic_set_list_free(list); + + isl_set_free(set); + return list; +} + +/* Generate code for the schedule domain "bounds" + * and add the result to "list". + * + * We mainly detect strides here and check if the bounds do not + * conflict with the current build domain + * and then pass over control to create_node. + * + * "bounds" reflects the bounds on the current dimension and possibly + * some extra conditions on outer dimensions. + * It does not, however, include any divs involving the current dimension, + * so it does not capture any stride constraints. + * We therefore need to compute that part of the schedule domain that + * intersects with "bounds" and derive the strides from the result. + */ +static __isl_give isl_ast_graft_list *add_node( + __isl_take isl_ast_graft_list *list, __isl_take isl_union_map *executed, + __isl_take isl_basic_set *bounds, __isl_take isl_ast_build *build) +{ + isl_ast_graft *graft; + isl_set *domain = NULL; + isl_union_set *uset; + int empty, disjoint; + + uset = isl_union_set_from_basic_set(isl_basic_set_copy(bounds)); + executed = isl_union_map_intersect_domain(executed, uset); + empty = isl_union_map_is_empty(executed); + if (empty < 0) + goto error; + if (empty) + goto done; + + uset = isl_union_map_domain(isl_union_map_copy(executed)); + domain = isl_set_from_union_set(uset); + domain = isl_ast_build_specialize(build, domain); + + domain = isl_set_compute_divs(domain); + domain = isl_ast_build_eliminate_inner(build, domain); + disjoint = isl_set_is_disjoint(domain, build->domain); + if (disjoint < 0) + goto error; + if (disjoint) + goto done; + + build = isl_ast_build_detect_strides(build, isl_set_copy(domain)); + + graft = create_node(executed, bounds, domain, + isl_ast_build_copy(build)); + list = isl_ast_graft_list_add(list, graft); + isl_ast_build_free(build); + return list; +error: + list = isl_ast_graft_list_free(list); +done: + isl_set_free(domain); + isl_basic_set_free(bounds); + isl_union_map_free(executed); + isl_ast_build_free(build); + return list; +} + +/* Does any element of i follow or coincide with any element of j + * at the current depth for equal values of the outer dimensions? + */ +static isl_bool domain_follows_at_depth(__isl_keep isl_basic_set *i, + __isl_keep isl_basic_set *j, void *user) +{ + int depth = *(int *) user; + isl_basic_map *test; + isl_bool empty; + int l; + + test = isl_basic_map_from_domain_and_range(isl_basic_set_copy(i), + isl_basic_set_copy(j)); + for (l = 0; l < depth; ++l) + test = isl_basic_map_equate(test, isl_dim_in, l, + isl_dim_out, l); + test = isl_basic_map_order_ge(test, isl_dim_in, depth, + isl_dim_out, depth); + empty = isl_basic_map_is_empty(test); + isl_basic_map_free(test); + + return empty < 0 ? isl_bool_error : !empty; +} + +/* Split up each element of "list" into a part that is related to "bset" + * according to "gt" and a part that is not. + * Return a list that consist of "bset" and all the pieces. + */ +static __isl_give isl_basic_set_list *add_split_on( + __isl_take isl_basic_set_list *list, __isl_take isl_basic_set *bset, + __isl_keep isl_basic_map *gt) +{ + int i, n; + isl_basic_set_list *res; + + if (!list) + bset = isl_basic_set_free(bset); + + gt = isl_basic_map_copy(gt); + gt = isl_basic_map_intersect_domain(gt, isl_basic_set_copy(bset)); + n = isl_basic_set_list_n_basic_set(list); + res = isl_basic_set_list_from_basic_set(bset); + for (i = 0; res && i < n; ++i) { + isl_basic_set *bset; + isl_set *set1, *set2; + isl_basic_map *bmap; + int empty; + + bset = isl_basic_set_list_get_basic_set(list, i); + bmap = isl_basic_map_copy(gt); + bmap = isl_basic_map_intersect_range(bmap, bset); + bset = isl_basic_map_range(bmap); + empty = isl_basic_set_is_empty(bset); + if (empty < 0) + res = isl_basic_set_list_free(res); + if (empty) { + isl_basic_set_free(bset); + bset = isl_basic_set_list_get_basic_set(list, i); + res = isl_basic_set_list_add(res, bset); + continue; + } + + res = isl_basic_set_list_add(res, isl_basic_set_copy(bset)); + set1 = isl_set_from_basic_set(bset); + bset = isl_basic_set_list_get_basic_set(list, i); + set2 = isl_set_from_basic_set(bset); + set1 = isl_set_subtract(set2, set1); + set1 = isl_set_make_disjoint(set1); + + res = isl_basic_set_list_concat(res, + isl_basic_set_list_from_set(set1)); + } + isl_basic_map_free(gt); + isl_basic_set_list_free(list); + return res; +} + +static __isl_give isl_ast_graft_list *generate_sorted_domains( + __isl_keep isl_basic_set_list *domain_list, + __isl_keep isl_union_map *executed, + __isl_keep isl_ast_build *build); + +/* Internal data structure for add_nodes. + * + * "executed" and "build" are extra arguments to be passed to add_node. + * "list" collects the results. + */ +struct isl_add_nodes_data { + isl_union_map *executed; + isl_ast_build *build; + + isl_ast_graft_list *list; +}; + +/* Generate code for the schedule domains in "scc" + * and add the results to "list". + * + * The domains in "scc" form a strongly connected component in the ordering. + * If the number of domains in "scc" is larger than 1, then this means + * that we cannot determine a valid ordering for the domains in the component. + * This should be fairly rare because the individual domains + * have been made disjoint first. + * The problem is that the domains may be integrally disjoint but not + * rationally disjoint. For example, we may have domains + * + * { [i,i] : 0 <= i <= 1 } and { [i,1-i] : 0 <= i <= 1 } + * + * These two domains have an empty intersection, but their rational + * relaxations do intersect. It is impossible to order these domains + * in the second dimension because the first should be ordered before + * the second for outer dimension equal to 0, while it should be ordered + * after for outer dimension equal to 1. + * + * This may happen in particular in case of unrolling since the domain + * of each slice is replaced by its simple hull. + * + * For each basic set i in "scc" and for each of the following basic sets j, + * we split off that part of the basic set i that shares the outer dimensions + * with j and lies before j in the current dimension. + * We collect all the pieces in a new list that replaces "scc". + * + * While the elements in "scc" should be disjoint, we double-check + * this property to avoid running into an infinite recursion in case + * they intersect due to some internal error. + */ +static isl_stat add_nodes(__isl_take isl_basic_set_list *scc, void *user) +{ + struct isl_add_nodes_data *data = user; + int i, n, depth; + isl_basic_set *bset, *first; + isl_basic_set_list *list; + isl_space *space; + isl_basic_map *gt; + + n = isl_basic_set_list_n_basic_set(scc); + bset = isl_basic_set_list_get_basic_set(scc, 0); + if (n == 1) { + isl_basic_set_list_free(scc); + data->list = add_node(data->list, + isl_union_map_copy(data->executed), bset, + isl_ast_build_copy(data->build)); + return data->list ? isl_stat_ok : isl_stat_error; + } + + depth = isl_ast_build_get_depth(data->build); + space = isl_basic_set_get_space(bset); + space = isl_space_map_from_set(space); + gt = isl_basic_map_universe(space); + for (i = 0; i < depth; ++i) + gt = isl_basic_map_equate(gt, isl_dim_in, i, isl_dim_out, i); + gt = isl_basic_map_order_gt(gt, isl_dim_in, depth, isl_dim_out, depth); + + first = isl_basic_set_copy(bset); + list = isl_basic_set_list_from_basic_set(bset); + for (i = 1; i < n; ++i) { + int disjoint; + + bset = isl_basic_set_list_get_basic_set(scc, i); + + disjoint = isl_basic_set_is_disjoint(bset, first); + if (disjoint < 0) + list = isl_basic_set_list_free(list); + else if (!disjoint) + isl_die(isl_basic_set_list_get_ctx(scc), + isl_error_internal, + "basic sets in scc are assumed to be disjoint", + list = isl_basic_set_list_free(list)); + + list = add_split_on(list, bset, gt); + } + isl_basic_set_free(first); + isl_basic_map_free(gt); + isl_basic_set_list_free(scc); + scc = list; + data->list = isl_ast_graft_list_concat(data->list, + generate_sorted_domains(scc, data->executed, data->build)); + isl_basic_set_list_free(scc); + + return data->list ? isl_stat_ok : isl_stat_error; +} + +/* Sort the domains in "domain_list" according to the execution order + * at the current depth (for equal values of the outer dimensions), + * generate code for each of them, collecting the results in a list. + * If no code is generated (because the intersection of the inverse schedule + * with the domains turns out to be empty), then an empty list is returned. + * + * The caller is responsible for ensuring that the basic sets in "domain_list" + * are pair-wise disjoint. It can, however, in principle happen that + * two basic sets should be ordered one way for one value of the outer + * dimensions and the other way for some other value of the outer dimensions. + * We therefore play safe and look for strongly connected components. + * The function add_nodes takes care of handling non-trivial components. + */ +static __isl_give isl_ast_graft_list *generate_sorted_domains( + __isl_keep isl_basic_set_list *domain_list, + __isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + struct isl_add_nodes_data data; + int depth; + int n; + + if (!domain_list) + return NULL; + + ctx = isl_basic_set_list_get_ctx(domain_list); + n = isl_basic_set_list_n_basic_set(domain_list); + data.list = isl_ast_graft_list_alloc(ctx, n); + if (n == 0) + return data.list; + if (n == 1) + return add_node(data.list, isl_union_map_copy(executed), + isl_basic_set_list_get_basic_set(domain_list, 0), + isl_ast_build_copy(build)); + + depth = isl_ast_build_get_depth(build); + data.executed = executed; + data.build = build; + if (isl_basic_set_list_foreach_scc(domain_list, + &domain_follows_at_depth, &depth, + &add_nodes, &data) < 0) + data.list = isl_ast_graft_list_free(data.list); + + return data.list; +} + +/* Do i and j share any values for the outer dimensions? + */ +static isl_bool shared_outer(__isl_keep isl_basic_set *i, + __isl_keep isl_basic_set *j, void *user) +{ + int depth = *(int *) user; + isl_basic_map *test; + isl_bool empty; + int l; + + test = isl_basic_map_from_domain_and_range(isl_basic_set_copy(i), + isl_basic_set_copy(j)); + for (l = 0; l < depth; ++l) + test = isl_basic_map_equate(test, isl_dim_in, l, + isl_dim_out, l); + empty = isl_basic_map_is_empty(test); + isl_basic_map_free(test); + + return empty < 0 ? isl_bool_error : !empty; +} + +/* Internal data structure for generate_sorted_domains_wrap. + * + * "n" is the total number of basic sets + * "executed" and "build" are extra arguments to be passed + * to generate_sorted_domains. + * + * "single" is set to 1 by generate_sorted_domains_wrap if there + * is only a single component. + * "list" collects the results. + */ +struct isl_ast_generate_parallel_domains_data { + int n; + isl_union_map *executed; + isl_ast_build *build; + + int single; + isl_ast_graft_list *list; +}; + +/* Call generate_sorted_domains on "scc", fuse the result into a list + * with either zero or one graft and collect the these single element + * lists into data->list. + * + * If there is only one component, i.e., if the number of basic sets + * in the current component is equal to the total number of basic sets, + * then data->single is set to 1 and the result of generate_sorted_domains + * is not fused. + */ +static isl_stat generate_sorted_domains_wrap(__isl_take isl_basic_set_list *scc, + void *user) +{ + struct isl_ast_generate_parallel_domains_data *data = user; + isl_ast_graft_list *list; + + list = generate_sorted_domains(scc, data->executed, data->build); + data->single = isl_basic_set_list_n_basic_set(scc) == data->n; + if (!data->single) + list = isl_ast_graft_list_fuse(list, data->build); + if (!data->list) + data->list = list; + else + data->list = isl_ast_graft_list_concat(data->list, list); + + isl_basic_set_list_free(scc); + if (!data->list) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Look for any (weakly connected) components in the "domain_list" + * of domains that share some values of the outer dimensions. + * That is, domains in different components do not share any values + * of the outer dimensions. This means that these components + * can be freely reordered. + * Within each of the components, we sort the domains according + * to the execution order at the current depth. + * + * If there is more than one component, then generate_sorted_domains_wrap + * fuses the result of each call to generate_sorted_domains + * into a list with either zero or one graft and collects these (at most) + * single element lists into a bigger list. This means that the elements of the + * final list can be freely reordered. In particular, we sort them + * according to an arbitrary but fixed ordering to ease merging of + * graft lists from different components. + */ +static __isl_give isl_ast_graft_list *generate_parallel_domains( + __isl_keep isl_basic_set_list *domain_list, + __isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build) +{ + int depth; + struct isl_ast_generate_parallel_domains_data data; + + if (!domain_list) + return NULL; + + data.n = isl_basic_set_list_n_basic_set(domain_list); + if (data.n <= 1) + return generate_sorted_domains(domain_list, executed, build); + + depth = isl_ast_build_get_depth(build); + data.list = NULL; + data.executed = executed; + data.build = build; + data.single = 0; + if (isl_basic_set_list_foreach_scc(domain_list, &shared_outer, &depth, + &generate_sorted_domains_wrap, + &data) < 0) + data.list = isl_ast_graft_list_free(data.list); + + if (!data.single) + data.list = isl_ast_graft_list_sort_guard(data.list); + + return data.list; +} + +/* Internal data for separate_domain. + * + * "explicit" is set if we only want to use explicit bounds. + * + * "domain" collects the separated domains. + */ +struct isl_separate_domain_data { + isl_ast_build *build; + int explicit; + isl_set *domain; +}; + +/* Extract implicit bounds on the current dimension for the executed "map". + * + * The domain of "map" may involve inner dimensions, so we + * need to eliminate them. + */ +static __isl_give isl_set *implicit_bounds(__isl_take isl_map *map, + __isl_keep isl_ast_build *build) +{ + isl_set *domain; + + domain = isl_map_domain(map); + domain = isl_ast_build_eliminate(build, domain); + + return domain; +} + +/* Extract explicit bounds on the current dimension for the executed "map". + * + * Rather than eliminating the inner dimensions as in implicit_bounds, + * we simply drop any constraints involving those inner dimensions. + * The idea is that most bounds that are implied by constraints on the + * inner dimensions will be enforced by for loops and not by explicit guards. + * There is then no need to separate along those bounds. + */ +static __isl_give isl_set *explicit_bounds(__isl_take isl_map *map, + __isl_keep isl_ast_build *build) +{ + isl_set *domain; + int depth, dim; + + dim = isl_map_dim(map, isl_dim_out); + map = isl_map_drop_constraints_involving_dims(map, isl_dim_out, 0, dim); + + domain = isl_map_domain(map); + depth = isl_ast_build_get_depth(build); + dim = isl_set_dim(domain, isl_dim_set); + domain = isl_set_detect_equalities(domain); + domain = isl_set_drop_constraints_involving_dims(domain, + isl_dim_set, depth + 1, dim - (depth + 1)); + domain = isl_set_remove_divs_involving_dims(domain, + isl_dim_set, depth, 1); + domain = isl_set_remove_unknown_divs(domain); + + return domain; +} + +/* Split data->domain into pieces that intersect with the range of "map" + * and pieces that do not intersect with the range of "map" + * and then add that part of the range of "map" that does not intersect + * with data->domain. + */ +static isl_stat separate_domain(__isl_take isl_map *map, void *user) +{ + struct isl_separate_domain_data *data = user; + isl_set *domain; + isl_set *d1, *d2; + + if (data->explicit) + domain = explicit_bounds(map, data->build); + else + domain = implicit_bounds(map, data->build); + + domain = isl_set_coalesce(domain); + domain = isl_set_make_disjoint(domain); + d1 = isl_set_subtract(isl_set_copy(domain), isl_set_copy(data->domain)); + d2 = isl_set_subtract(isl_set_copy(data->domain), isl_set_copy(domain)); + data->domain = isl_set_intersect(data->domain, domain); + data->domain = isl_set_union(data->domain, d1); + data->domain = isl_set_union(data->domain, d2); + + return isl_stat_ok; +} + +/* Separate the schedule domains of "executed". + * + * That is, break up the domain of "executed" into basic sets, + * such that for each basic set S, every element in S is associated with + * the same domain spaces. + * + * "space" is the (single) domain space of "executed". + */ +static __isl_give isl_set *separate_schedule_domains( + __isl_take isl_space *space, __isl_take isl_union_map *executed, + __isl_keep isl_ast_build *build) +{ + struct isl_separate_domain_data data = { build }; + isl_ctx *ctx; + + ctx = isl_ast_build_get_ctx(build); + data.explicit = isl_options_get_ast_build_separation_bounds(ctx) == + ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT; + data.domain = isl_set_empty(space); + if (isl_union_map_foreach_map(executed, &separate_domain, &data) < 0) + data.domain = isl_set_free(data.domain); + + isl_union_map_free(executed); + return data.domain; +} + +/* Temporary data used during the search for a lower bound for unrolling. + * + * "build" is the build in which the unrolling will be performed + * "domain" is the original set for which to find a lower bound + * "depth" is the dimension for which to find a lower boudn + * "expansion" is the expansion that needs to be applied to "domain" + * in the unrolling that will be performed + * + * "lower" is the best lower bound found so far. It is NULL if we have not + * found any yet. + * "n" is the corresponding size. If lower is NULL, then the value of n + * is undefined. + * "n_div" is the maximal number of integer divisions in the first + * unrolled iteration (after expansion). It is set to -1 if it hasn't + * been computed yet. + */ +struct isl_find_unroll_data { + isl_ast_build *build; + isl_set *domain; + int depth; + isl_basic_map *expansion; + + isl_aff *lower; + int *n; + int n_div; +}; + +/* Return the constraint + * + * i_"depth" = aff + offset + */ +static __isl_give isl_constraint *at_offset(int depth, __isl_keep isl_aff *aff, + int offset) +{ + aff = isl_aff_copy(aff); + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, depth, -1); + aff = isl_aff_add_constant_si(aff, offset); + return isl_equality_from_aff(aff); +} + +/* Update *user to the number of integer divsions in the first element + * of "ma", if it is larger than the current value. + */ +static isl_stat update_n_div(__isl_take isl_set *set, + __isl_take isl_multi_aff *ma, void *user) +{ + isl_aff *aff; + int *n = user; + int n_div; + + aff = isl_multi_aff_get_aff(ma, 0); + n_div = isl_aff_dim(aff, isl_dim_div); + isl_aff_free(aff); + isl_multi_aff_free(ma); + isl_set_free(set); + + if (n_div > *n) + *n = n_div; + + return aff ? isl_stat_ok : isl_stat_error; +} + +/* Get the number of integer divisions in the expression for the iterator + * value at the first slice in the unrolling based on lower bound "lower", + * taking into account the expansion that needs to be performed on this slice. + */ +static int get_expanded_n_div(struct isl_find_unroll_data *data, + __isl_keep isl_aff *lower) +{ + isl_constraint *c; + isl_set *set; + isl_map *it_map, *expansion; + isl_pw_multi_aff *pma; + int n; + + c = at_offset(data->depth, lower, 0); + set = isl_set_copy(data->domain); + set = isl_set_add_constraint(set, c); + expansion = isl_map_from_basic_map(isl_basic_map_copy(data->expansion)); + set = isl_set_apply(set, expansion); + it_map = isl_ast_build_map_to_iterator(data->build, set); + pma = isl_pw_multi_aff_from_map(it_map); + n = 0; + if (isl_pw_multi_aff_foreach_piece(pma, &update_n_div, &n) < 0) + n = -1; + isl_pw_multi_aff_free(pma); + + return n; +} + +/* Is the lower bound "lower" with corresponding iteration count "n" + * better than the one stored in "data"? + * If there is no upper bound on the iteration count ("n" is infinity) or + * if the count is too large, then we cannot use this lower bound. + * Otherwise, if there was no previous lower bound or + * if the iteration count of the new lower bound is smaller than + * the iteration count of the previous lower bound, then we consider + * the new lower bound to be better. + * If the iteration count is the same, then compare the number + * of integer divisions that would be needed to express + * the iterator value at the first slice in the unrolling + * according to the lower bound. If we end up computing this + * number, then store the lowest value in data->n_div. + */ +static int is_better_lower_bound(struct isl_find_unroll_data *data, + __isl_keep isl_aff *lower, __isl_keep isl_val *n) +{ + int cmp; + int n_div; + + if (!n) + return -1; + if (isl_val_is_infty(n)) + return 0; + if (isl_val_cmp_si(n, INT_MAX) > 0) + return 0; + if (!data->lower) + return 1; + cmp = isl_val_cmp_si(n, *data->n); + if (cmp < 0) + return 1; + if (cmp > 0) + return 0; + if (data->n_div < 0) + data->n_div = get_expanded_n_div(data, data->lower); + if (data->n_div < 0) + return -1; + if (data->n_div == 0) + return 0; + n_div = get_expanded_n_div(data, lower); + if (n_div < 0) + return -1; + if (n_div >= data->n_div) + return 0; + data->n_div = n_div; + + return 1; +} + +/* Check if we can use "c" as a lower bound and if it is better than + * any previously found lower bound. + * + * If "c" does not involve the dimension at the current depth, + * then we cannot use it. + * Otherwise, let "c" be of the form + * + * i >= f(j)/a + * + * We compute the maximal value of + * + * -ceil(f(j)/a)) + i + 1 + * + * over the domain. If there is such a value "n", then we know + * + * -ceil(f(j)/a)) + i + 1 <= n + * + * or + * + * i < ceil(f(j)/a)) + n + * + * meaning that we can use ceil(f(j)/a)) as a lower bound for unrolling. + * We just need to check if we have found any lower bound before and + * if the new lower bound is better (smaller n or fewer integer divisions) + * than the previously found lower bounds. + */ +static isl_stat update_unrolling_lower_bound(struct isl_find_unroll_data *data, + __isl_keep isl_constraint *c) +{ + isl_aff *aff, *lower; + isl_val *max; + int better; + + if (!isl_constraint_is_lower_bound(c, isl_dim_set, data->depth)) + return isl_stat_ok; + + lower = isl_constraint_get_bound(c, isl_dim_set, data->depth); + lower = isl_aff_ceil(lower); + aff = isl_aff_copy(lower); + aff = isl_aff_neg(aff); + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, data->depth, 1); + aff = isl_aff_add_constant_si(aff, 1); + max = isl_set_max_val(data->domain, aff); + isl_aff_free(aff); + + better = is_better_lower_bound(data, lower, max); + if (better < 0 || !better) { + isl_val_free(max); + isl_aff_free(lower); + return better < 0 ? isl_stat_error : isl_stat_ok; + } + + isl_aff_free(data->lower); + data->lower = lower; + *data->n = isl_val_get_num_si(max); + isl_val_free(max); + + return isl_stat_ok; +} + +/* Check if we can use "c" as a lower bound and if it is better than + * any previously found lower bound. + */ +static isl_stat constraint_find_unroll(__isl_take isl_constraint *c, void *user) +{ + struct isl_find_unroll_data *data; + isl_stat r; + + data = (struct isl_find_unroll_data *) user; + r = update_unrolling_lower_bound(data, c); + isl_constraint_free(c); + + return r; +} + +/* Look for a lower bound l(i) on the dimension at "depth" + * and a size n such that "domain" is a subset of + * + * { [i] : l(i) <= i_d < l(i) + n } + * + * where d is "depth" and l(i) depends only on earlier dimensions. + * Furthermore, try and find a lower bound such that n is as small as possible. + * In particular, "n" needs to be finite. + * "build" is the build in which the unrolling will be performed. + * "expansion" is the expansion that needs to be applied to "domain" + * in the unrolling that will be performed. + * + * Inner dimensions have been eliminated from "domain" by the caller. + * + * We first construct a collection of lower bounds on the input set + * by computing its simple hull. We then iterate through them, + * discarding those that we cannot use (either because they do not + * involve the dimension at "depth" or because they have no corresponding + * upper bound, meaning that "n" would be unbounded) and pick out the + * best from the remaining ones. + * + * If we cannot find a suitable lower bound, then we consider that + * to be an error. + */ +static __isl_give isl_aff *find_unroll_lower_bound( + __isl_keep isl_ast_build *build, __isl_keep isl_set *domain, + int depth, __isl_keep isl_basic_map *expansion, int *n) +{ + struct isl_find_unroll_data data = + { build, domain, depth, expansion, NULL, n, -1 }; + isl_basic_set *hull; + + hull = isl_set_simple_hull(isl_set_copy(domain)); + + if (isl_basic_set_foreach_constraint(hull, + &constraint_find_unroll, &data) < 0) + goto error; + + isl_basic_set_free(hull); + + if (!data.lower) + isl_die(isl_set_get_ctx(domain), isl_error_invalid, + "cannot find lower bound for unrolling", return NULL); + + return data.lower; +error: + isl_basic_set_free(hull); + return isl_aff_free(data.lower); +} + +/* Call "fn" on each iteration of the current dimension of "domain". + * If "init" is not NULL, then it is called with the number of + * iterations before any call to "fn". + * Return -1 on failure. + * + * Since we are going to be iterating over the individual values, + * we first check if there are any strides on the current dimension. + * If there is, we rewrite the current dimension i as + * + * i = stride i' + offset + * + * and then iterate over individual values of i' instead. + * + * We then look for a lower bound on i' and a size such that the domain + * is a subset of + * + * { [j,i'] : l(j) <= i' < l(j) + n } + * + * and then take slices of the domain at values of i' + * between l(j) and l(j) + n - 1. + * + * We compute the unshifted simple hull of each slice to ensure that + * we have a single basic set per offset. The slicing constraint + * may get simplified away before the unshifted simple hull is taken + * and may therefore in some rare cases disappear from the result. + * We therefore explicitly add the constraint back after computing + * the unshifted simple hull to ensure that the basic sets + * remain disjoint. The constraints that are dropped by taking the hull + * will be taken into account at the next level, as in the case of the + * atomic option. + * + * Finally, we map i' back to i and call "fn". + */ +static int foreach_iteration(__isl_take isl_set *domain, + __isl_keep isl_ast_build *build, int (*init)(int n, void *user), + int (*fn)(__isl_take isl_basic_set *bset, void *user), void *user) +{ + int i, n; + int empty; + int depth; + isl_multi_aff *expansion; + isl_basic_map *bmap; + isl_aff *lower = NULL; + isl_ast_build *stride_build; + + depth = isl_ast_build_get_depth(build); + + domain = isl_ast_build_eliminate_inner(build, domain); + domain = isl_set_intersect(domain, isl_ast_build_get_domain(build)); + stride_build = isl_ast_build_copy(build); + stride_build = isl_ast_build_detect_strides(stride_build, + isl_set_copy(domain)); + expansion = isl_ast_build_get_stride_expansion(stride_build); + + domain = isl_set_preimage_multi_aff(domain, + isl_multi_aff_copy(expansion)); + domain = isl_ast_build_eliminate_divs(stride_build, domain); + isl_ast_build_free(stride_build); + + bmap = isl_basic_map_from_multi_aff(expansion); + + empty = isl_set_is_empty(domain); + if (empty < 0) { + n = -1; + } else if (empty) { + n = 0; + } else { + lower = find_unroll_lower_bound(build, domain, depth, bmap, &n); + if (!lower) + n = -1; + } + if (n >= 0 && init && init(n, user) < 0) + n = -1; + for (i = 0; i < n; ++i) { + isl_set *set; + isl_basic_set *bset; + isl_constraint *slice; + + slice = at_offset(depth, lower, i); + set = isl_set_copy(domain); + set = isl_set_add_constraint(set, isl_constraint_copy(slice)); + bset = isl_set_unshifted_simple_hull(set); + bset = isl_basic_set_add_constraint(bset, slice); + bset = isl_basic_set_apply(bset, isl_basic_map_copy(bmap)); + + if (fn(bset, user) < 0) + break; + } + + isl_aff_free(lower); + isl_set_free(domain); + isl_basic_map_free(bmap); + + return n < 0 || i < n ? -1 : 0; +} + +/* Data structure for storing the results and the intermediate objects + * of compute_domains. + * + * "list" is the main result of the function and contains a list + * of disjoint basic sets for which code should be generated. + * + * "executed" and "build" are inputs to compute_domains. + * "schedule_domain" is the domain of "executed". + * + * "option" constains the domains at the current depth that should by + * atomic, separated or unrolled. These domains are as specified by + * the user, except that inner dimensions have been eliminated and + * that they have been made pair-wise disjoint. + * + * "sep_class" contains the user-specified split into separation classes + * specialized to the current depth. + * "done" contains the union of the separation domains that have already + * been handled. + */ +struct isl_codegen_domains { + isl_basic_set_list *list; + + isl_union_map *executed; + isl_ast_build *build; + isl_set *schedule_domain; + + isl_set *option[4]; + + isl_map *sep_class; + isl_set *done; +}; + +/* Internal data structure for do_unroll. + * + * "domains" stores the results of compute_domains. + * "class_domain" is the original class domain passed to do_unroll. + * "unroll_domain" collects the unrolled iterations. + */ +struct isl_ast_unroll_data { + struct isl_codegen_domains *domains; + isl_set *class_domain; + isl_set *unroll_domain; +}; + +/* Given an iteration of an unrolled domain represented by "bset", + * add it to data->domains->list. + * Since we may have dropped some constraints, we intersect with + * the class domain again to ensure that each element in the list + * is disjoint from the other class domains. + */ +static int do_unroll_iteration(__isl_take isl_basic_set *bset, void *user) +{ + struct isl_ast_unroll_data *data = user; + isl_set *set; + isl_basic_set_list *list; + + set = isl_set_from_basic_set(bset); + data->unroll_domain = isl_set_union(data->unroll_domain, + isl_set_copy(set)); + set = isl_set_intersect(set, isl_set_copy(data->class_domain)); + set = isl_set_make_disjoint(set); + list = isl_basic_set_list_from_set(set); + data->domains->list = isl_basic_set_list_concat(data->domains->list, + list); + + return 0; +} + +/* Extend domains->list with a list of basic sets, one for each value + * of the current dimension in "domain" and remove the corresponding + * sets from the class domain. Return the updated class domain. + * The divs that involve the current dimension have not been projected out + * from this domain. + * + * We call foreach_iteration to iterate over the individual values and + * in do_unroll_iteration we collect the individual basic sets in + * domains->list and their union in data->unroll_domain, which is then + * used to update the class domain. + */ +static __isl_give isl_set *do_unroll(struct isl_codegen_domains *domains, + __isl_take isl_set *domain, __isl_take isl_set *class_domain) +{ + struct isl_ast_unroll_data data; + + if (!domain) + return isl_set_free(class_domain); + if (!class_domain) + return isl_set_free(domain); + + data.domains = domains; + data.class_domain = class_domain; + data.unroll_domain = isl_set_empty(isl_set_get_space(domain)); + + if (foreach_iteration(domain, domains->build, NULL, + &do_unroll_iteration, &data) < 0) + data.unroll_domain = isl_set_free(data.unroll_domain); + + class_domain = isl_set_subtract(class_domain, data.unroll_domain); + + return class_domain; +} + +/* Add domains to domains->list for each individual value of the current + * dimension, for that part of the schedule domain that lies in the + * intersection of the option domain and the class domain. + * Remove the corresponding sets from the class domain and + * return the updated class domain. + * + * We first break up the unroll option domain into individual pieces + * and then handle each of them separately. The unroll option domain + * has been made disjoint in compute_domains_init_options, + * + * Note that we actively want to combine different pieces of the + * schedule domain that have the same value at the current dimension. + * We therefore need to break up the unroll option domain before + * intersecting with class and schedule domain, hoping that the + * unroll option domain specified by the user is relatively simple. + */ +static __isl_give isl_set *compute_unroll_domains( + struct isl_codegen_domains *domains, __isl_take isl_set *class_domain) +{ + isl_set *unroll_domain; + isl_basic_set_list *unroll_list; + int i, n; + int empty; + + empty = isl_set_is_empty(domains->option[isl_ast_loop_unroll]); + if (empty < 0) + return isl_set_free(class_domain); + if (empty) + return class_domain; + + unroll_domain = isl_set_copy(domains->option[isl_ast_loop_unroll]); + unroll_list = isl_basic_set_list_from_set(unroll_domain); + + n = isl_basic_set_list_n_basic_set(unroll_list); + for (i = 0; i < n; ++i) { + isl_basic_set *bset; + + bset = isl_basic_set_list_get_basic_set(unroll_list, i); + unroll_domain = isl_set_from_basic_set(bset); + unroll_domain = isl_set_intersect(unroll_domain, + isl_set_copy(class_domain)); + unroll_domain = isl_set_intersect(unroll_domain, + isl_set_copy(domains->schedule_domain)); + + empty = isl_set_is_empty(unroll_domain); + if (empty >= 0 && empty) { + isl_set_free(unroll_domain); + continue; + } + + class_domain = do_unroll(domains, unroll_domain, class_domain); + } + + isl_basic_set_list_free(unroll_list); + + return class_domain; +} + +/* Try and construct a single basic set that includes the intersection of + * the schedule domain, the atomic option domain and the class domain. + * Add the resulting basic set(s) to domains->list and remove them + * from class_domain. Return the updated class domain. + * + * We construct a single domain rather than trying to combine + * the schedule domains of individual domains because we are working + * within a single component so that non-overlapping schedule domains + * should already have been separated. + * We do however need to make sure that this single domains is a subset + * of the class domain so that it would not intersect with any other + * class domains. This means that we may end up splitting up the atomic + * domain in case separation classes are being used. + * + * "domain" is the intersection of the schedule domain and the class domain, + * with inner dimensions projected out. + */ +static __isl_give isl_set *compute_atomic_domain( + struct isl_codegen_domains *domains, __isl_take isl_set *class_domain) +{ + isl_basic_set *bset; + isl_basic_set_list *list; + isl_set *domain, *atomic_domain; + int empty; + + domain = isl_set_copy(domains->option[isl_ast_loop_atomic]); + domain = isl_set_intersect(domain, isl_set_copy(class_domain)); + domain = isl_set_intersect(domain, + isl_set_copy(domains->schedule_domain)); + empty = isl_set_is_empty(domain); + if (empty < 0) + class_domain = isl_set_free(class_domain); + if (empty) { + isl_set_free(domain); + return class_domain; + } + + domain = isl_ast_build_eliminate(domains->build, domain); + domain = isl_set_coalesce(domain); + bset = isl_set_unshifted_simple_hull(domain); + domain = isl_set_from_basic_set(bset); + atomic_domain = isl_set_copy(domain); + domain = isl_set_intersect(domain, isl_set_copy(class_domain)); + class_domain = isl_set_subtract(class_domain, atomic_domain); + domain = isl_set_make_disjoint(domain); + list = isl_basic_set_list_from_set(domain); + domains->list = isl_basic_set_list_concat(domains->list, list); + + return class_domain; +} + +/* Split up the schedule domain into uniform basic sets, + * in the sense that each element in a basic set is associated to + * elements of the same domains, and add the result to domains->list. + * Do this for that part of the schedule domain that lies in the + * intersection of "class_domain" and the separate option domain. + * + * "class_domain" may or may not include the constraints + * of the schedule domain, but this does not make a difference + * since we are going to intersect it with the domain of the inverse schedule. + * If it includes schedule domain constraints, then they may involve + * inner dimensions, but we will eliminate them in separation_domain. + */ +static int compute_separate_domain(struct isl_codegen_domains *domains, + __isl_keep isl_set *class_domain) +{ + isl_space *space; + isl_set *domain; + isl_union_map *executed; + isl_basic_set_list *list; + int empty; + + domain = isl_set_copy(domains->option[isl_ast_loop_separate]); + domain = isl_set_intersect(domain, isl_set_copy(class_domain)); + executed = isl_union_map_copy(domains->executed); + executed = isl_union_map_intersect_domain(executed, + isl_union_set_from_set(domain)); + empty = isl_union_map_is_empty(executed); + if (empty < 0 || empty) { + isl_union_map_free(executed); + return empty < 0 ? -1 : 0; + } + + space = isl_set_get_space(class_domain); + domain = separate_schedule_domains(space, executed, domains->build); + + list = isl_basic_set_list_from_set(domain); + domains->list = isl_basic_set_list_concat(domains->list, list); + + return 0; +} + +/* Split up the domain at the current depth into disjoint + * basic sets for which code should be generated separately + * for the given separation class domain. + * + * If any separation classes have been defined, then "class_domain" + * is the domain of the current class and does not refer to inner dimensions. + * Otherwise, "class_domain" is the universe domain. + * + * We first make sure that the class domain is disjoint from + * previously considered class domains. + * + * The separate domains can be computed directly from the "class_domain". + * + * The unroll, atomic and remainder domains need the constraints + * from the schedule domain. + * + * For unrolling, the actual schedule domain is needed (with divs that + * may refer to the current dimension) so that stride detection can be + * performed. + * + * For atomic and remainder domains, inner dimensions and divs involving + * the current dimensions should be eliminated. + * In case we are working within a separation class, we need to intersect + * the result with the current "class_domain" to ensure that the domains + * are disjoint from those generated from other class domains. + * + * The domain that has been made atomic may be larger than specified + * by the user since it needs to be representable as a single basic set. + * This possibly larger domain is removed from class_domain by + * compute_atomic_domain. It is computed first so that the extended domain + * would not overlap with any domains computed before. + * Similary, the unrolled domains may have some constraints removed and + * may therefore also be larger than specified by the user. + * + * If anything is left after handling separate, unroll and atomic, + * we split it up into basic sets and append the basic sets to domains->list. + */ +static isl_stat compute_partial_domains(struct isl_codegen_domains *domains, + __isl_take isl_set *class_domain) +{ + isl_basic_set_list *list; + isl_set *domain; + + class_domain = isl_set_subtract(class_domain, + isl_set_copy(domains->done)); + domains->done = isl_set_union(domains->done, + isl_set_copy(class_domain)); + + class_domain = compute_atomic_domain(domains, class_domain); + class_domain = compute_unroll_domains(domains, class_domain); + + domain = isl_set_copy(class_domain); + + if (compute_separate_domain(domains, domain) < 0) + goto error; + domain = isl_set_subtract(domain, + isl_set_copy(domains->option[isl_ast_loop_separate])); + + domain = isl_set_intersect(domain, + isl_set_copy(domains->schedule_domain)); + + domain = isl_ast_build_eliminate(domains->build, domain); + domain = isl_set_intersect(domain, isl_set_copy(class_domain)); + + domain = isl_set_coalesce(domain); + domain = isl_set_make_disjoint(domain); + + list = isl_basic_set_list_from_set(domain); + domains->list = isl_basic_set_list_concat(domains->list, list); + + isl_set_free(class_domain); + + return isl_stat_ok; +error: + isl_set_free(domain); + isl_set_free(class_domain); + return isl_stat_error; +} + +/* Split up the domain at the current depth into disjoint + * basic sets for which code should be generated separately + * for the separation class identified by "pnt". + * + * We extract the corresponding class domain from domains->sep_class, + * eliminate inner dimensions and pass control to compute_partial_domains. + */ +static isl_stat compute_class_domains(__isl_take isl_point *pnt, void *user) +{ + struct isl_codegen_domains *domains = user; + isl_set *class_set; + isl_set *domain; + int disjoint; + + class_set = isl_set_from_point(pnt); + domain = isl_map_domain(isl_map_intersect_range( + isl_map_copy(domains->sep_class), class_set)); + domain = isl_ast_build_compute_gist(domains->build, domain); + domain = isl_ast_build_eliminate(domains->build, domain); + + disjoint = isl_set_plain_is_disjoint(domain, domains->schedule_domain); + if (disjoint < 0) + return isl_stat_error; + if (disjoint) { + isl_set_free(domain); + return isl_stat_ok; + } + + return compute_partial_domains(domains, domain); +} + +/* Extract the domains at the current depth that should be atomic, + * separated or unrolled and store them in option. + * + * The domains specified by the user might overlap, so we make + * them disjoint by subtracting earlier domains from later domains. + */ +static void compute_domains_init_options(isl_set *option[4], + __isl_keep isl_ast_build *build) +{ + enum isl_ast_loop_type type, type2; + isl_set *unroll; + + for (type = isl_ast_loop_atomic; + type <= isl_ast_loop_separate; ++type) { + option[type] = isl_ast_build_get_option_domain(build, type); + for (type2 = isl_ast_loop_atomic; type2 < type; ++type2) + option[type] = isl_set_subtract(option[type], + isl_set_copy(option[type2])); + } + + unroll = option[isl_ast_loop_unroll]; + unroll = isl_set_coalesce(unroll); + unroll = isl_set_make_disjoint(unroll); + option[isl_ast_loop_unroll] = unroll; +} + +/* Split up the domain at the current depth into disjoint + * basic sets for which code should be generated separately, + * based on the user-specified options. + * Return the list of disjoint basic sets. + * + * There are three kinds of domains that we need to keep track of. + * - the "schedule domain" is the domain of "executed" + * - the "class domain" is the domain corresponding to the currrent + * separation class + * - the "option domain" is the domain corresponding to one of the options + * atomic, unroll or separate + * + * We first consider the individial values of the separation classes + * and split up the domain for each of them separately. + * Finally, we consider the remainder. If no separation classes were + * specified, then we call compute_partial_domains with the universe + * "class_domain". Otherwise, we take the "schedule_domain" as "class_domain", + * with inner dimensions removed. We do this because we want to + * avoid computing the complement of the class domains (i.e., the difference + * between the universe and domains->done). + */ +static __isl_give isl_basic_set_list *compute_domains( + __isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build) +{ + struct isl_codegen_domains domains; + isl_ctx *ctx; + isl_set *domain; + isl_union_set *schedule_domain; + isl_set *classes; + isl_space *space; + int n_param; + enum isl_ast_loop_type type; + int empty; + + if (!executed) + return NULL; + + ctx = isl_union_map_get_ctx(executed); + domains.list = isl_basic_set_list_alloc(ctx, 0); + + schedule_domain = isl_union_map_domain(isl_union_map_copy(executed)); + domain = isl_set_from_union_set(schedule_domain); + + compute_domains_init_options(domains.option, build); + + domains.sep_class = isl_ast_build_get_separation_class(build); + classes = isl_map_range(isl_map_copy(domains.sep_class)); + n_param = isl_set_dim(classes, isl_dim_param); + classes = isl_set_project_out(classes, isl_dim_param, 0, n_param); + + space = isl_set_get_space(domain); + domains.build = build; + domains.schedule_domain = isl_set_copy(domain); + domains.executed = executed; + domains.done = isl_set_empty(space); + + if (isl_set_foreach_point(classes, &compute_class_domains, &domains) < 0) + domains.list = isl_basic_set_list_free(domains.list); + isl_set_free(classes); + + empty = isl_set_is_empty(domains.done); + if (empty < 0) { + domains.list = isl_basic_set_list_free(domains.list); + domain = isl_set_free(domain); + } else if (empty) { + isl_set_free(domain); + domain = isl_set_universe(isl_set_get_space(domains.done)); + } else { + domain = isl_ast_build_eliminate(build, domain); + } + if (compute_partial_domains(&domains, domain) < 0) + domains.list = isl_basic_set_list_free(domains.list); + + isl_set_free(domains.schedule_domain); + isl_set_free(domains.done); + isl_map_free(domains.sep_class); + for (type = isl_ast_loop_atomic; type <= isl_ast_loop_separate; ++type) + isl_set_free(domains.option[type]); + + return domains.list; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a union map. + * + * We first split up the domain at the current depth into disjoint + * basic sets based on the user-specified options. + * Then we generated code for each of them and concatenate the results. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_flat( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + isl_basic_set_list *domain_list; + isl_ast_graft_list *list = NULL; + + domain_list = compute_domains(executed, build); + list = generate_parallel_domains(domain_list, executed, build); + + isl_basic_set_list_free(domain_list); + isl_union_map_free(executed); + isl_ast_build_free(build); + + return list; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree + * and the separate option was specified. + * + * We perform separation on the domain of "executed" and then generate + * an AST for each of the resulting disjoint basic sets. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_tree_separate( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + isl_space *space; + isl_set *domain; + isl_basic_set_list *domain_list; + isl_ast_graft_list *list; + + space = isl_ast_build_get_space(build, 1); + domain = separate_schedule_domains(space, + isl_union_map_copy(executed), build); + domain_list = isl_basic_set_list_from_set(domain); + + list = generate_parallel_domains(domain_list, executed, build); + + isl_basic_set_list_free(domain_list); + isl_union_map_free(executed); + isl_ast_build_free(build); + + return list; +} + +/* Internal data structure for generate_shifted_component_tree_unroll. + * + * "executed" and "build" are inputs to generate_shifted_component_tree_unroll. + * "list" collects the constructs grafts. + */ +struct isl_ast_unroll_tree_data { + isl_union_map *executed; + isl_ast_build *build; + isl_ast_graft_list *list; +}; + +/* Initialize data->list to a list of "n" elements. + */ +static int init_unroll_tree(int n, void *user) +{ + struct isl_ast_unroll_tree_data *data = user; + isl_ctx *ctx; + + ctx = isl_ast_build_get_ctx(data->build); + data->list = isl_ast_graft_list_alloc(ctx, n); + + return 0; +} + +/* Given an iteration of an unrolled domain represented by "bset", + * generate the corresponding AST and add the result to data->list. + */ +static int do_unroll_tree_iteration(__isl_take isl_basic_set *bset, void *user) +{ + struct isl_ast_unroll_tree_data *data = user; + + data->list = add_node(data->list, isl_union_map_copy(data->executed), + bset, isl_ast_build_copy(data->build)); + + return 0; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree + * and the unroll option was specified. + * + * We call foreach_iteration to iterate over the individual values and + * construct and collect the corresponding grafts in do_unroll_tree_iteration. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_tree_unroll( + __isl_take isl_union_map *executed, __isl_take isl_set *domain, + __isl_take isl_ast_build *build) +{ + struct isl_ast_unroll_tree_data data = { executed, build, NULL }; + + if (foreach_iteration(domain, build, &init_unroll_tree, + &do_unroll_tree_iteration, &data) < 0) + data.list = isl_ast_graft_list_free(data.list); + + isl_union_map_free(executed); + isl_ast_build_free(build); + + return data.list; +} + +/* Does "domain" involve a disjunction that is purely based on + * constraints involving only outer dimension? + * + * In particular, is there a disjunction such that the constraints + * involving the current and later dimensions are the same over + * all the disjuncts? + */ +static isl_bool has_pure_outer_disjunction(__isl_keep isl_set *domain, + __isl_keep isl_ast_build *build) +{ + isl_basic_set *hull; + isl_set *shared, *inner; + isl_bool equal; + int depth, dim; + + if (isl_set_n_basic_set(domain) <= 1) + return isl_bool_false; + + inner = isl_set_copy(domain); + depth = isl_ast_build_get_depth(build); + dim = isl_set_dim(inner, isl_dim_set); + inner = isl_set_drop_constraints_not_involving_dims(inner, + isl_dim_set, depth, dim - depth); + hull = isl_set_plain_unshifted_simple_hull(isl_set_copy(inner)); + shared = isl_set_from_basic_set(hull); + equal = isl_set_plain_is_equal(inner, shared); + isl_set_free(inner); + isl_set_free(shared); + + return equal; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree. + * In particular, handle the base case where there is either no isolated + * set or we are within the isolated set (in which case "isolated" is set) + * or the iterations that precede or follow the isolated set. + * + * The schedule domain is broken up or combined into basic sets + * according to the AST generation option specified in the current + * schedule node, which may be either atomic, separate, unroll or + * unspecified. If the option is unspecified, then we currently simply + * split the schedule domain into disjoint basic sets. + * + * In case the separate option is specified, the AST generation is + * handled by generate_shifted_component_tree_separate. + * In the other cases, we need the global schedule domain. + * In the unroll case, the AST generation is then handled by + * generate_shifted_component_tree_unroll which needs the actual + * schedule domain (with divs that may refer to the current dimension) + * so that stride detection can be performed. + * In the atomic or unspecified case, inner dimensions and divs involving + * the current dimensions should be eliminated. + * The result is then either combined into a single basic set or + * split up into disjoint basic sets. + * Finally an AST is generated for each basic set and the results are + * concatenated. + * + * If the schedule domain involves a disjunction that is purely based on + * constraints involving only outer dimension, then it is treated as + * if atomic was specified. This ensures that only a single loop + * is generated instead of a sequence of identical loops with + * different guards. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_tree_base( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build, + int isolated) +{ + isl_bool outer_disjunction; + isl_union_set *schedule_domain; + isl_set *domain; + isl_basic_set_list *domain_list; + isl_ast_graft_list *list; + enum isl_ast_loop_type type; + + type = isl_ast_build_get_loop_type(build, isolated); + if (type < 0) + goto error; + + if (type == isl_ast_loop_separate) + return generate_shifted_component_tree_separate(executed, + build); + + schedule_domain = isl_union_map_domain(isl_union_map_copy(executed)); + domain = isl_set_from_union_set(schedule_domain); + + if (type == isl_ast_loop_unroll) + return generate_shifted_component_tree_unroll(executed, domain, + build); + + domain = isl_ast_build_eliminate(build, domain); + domain = isl_set_coalesce(domain); + + outer_disjunction = has_pure_outer_disjunction(domain, build); + if (outer_disjunction < 0) + domain = isl_set_free(domain); + + if (outer_disjunction || type == isl_ast_loop_atomic) { + isl_basic_set *hull; + hull = isl_set_unshifted_simple_hull(domain); + domain_list = isl_basic_set_list_from_basic_set(hull); + } else { + domain = isl_set_make_disjoint(domain); + domain_list = isl_basic_set_list_from_set(domain); + } + + list = generate_parallel_domains(domain_list, executed, build); + + isl_basic_set_list_free(domain_list); + isl_union_map_free(executed); + isl_ast_build_free(build); + + return list; +error: + isl_union_map_free(executed); + isl_ast_build_free(build); + return NULL; +} + +/* Extract out the disjunction imposed by "domain" on the outer + * schedule dimensions. + * + * In particular, remove all inner dimensions from "domain" (including + * the current dimension) and then remove the constraints that are shared + * by all disjuncts in the result. + */ +static __isl_give isl_set *extract_disjunction(__isl_take isl_set *domain, + __isl_keep isl_ast_build *build) +{ + isl_set *hull; + int depth, dim; + + domain = isl_ast_build_specialize(build, domain); + depth = isl_ast_build_get_depth(build); + dim = isl_set_dim(domain, isl_dim_set); + domain = isl_set_eliminate(domain, isl_dim_set, depth, dim - depth); + domain = isl_set_remove_unknown_divs(domain); + hull = isl_set_copy(domain); + hull = isl_set_from_basic_set(isl_set_unshifted_simple_hull(hull)); + domain = isl_set_gist(domain, hull); + + return domain; +} + +/* Add "guard" to the grafts in "list". + * "build" is the outer AST build, while "sub_build" includes "guard" + * in its generated domain. + * + * First combine the grafts into a single graft and then add the guard. + * If the list is empty, or if some error occurred, then simply return + * the list. + */ +static __isl_give isl_ast_graft_list *list_add_guard( + __isl_take isl_ast_graft_list *list, __isl_keep isl_set *guard, + __isl_keep isl_ast_build *build, __isl_keep isl_ast_build *sub_build) +{ + isl_ast_graft *graft; + + list = isl_ast_graft_list_fuse(list, sub_build); + + if (isl_ast_graft_list_n_ast_graft(list) != 1) + return list; + + graft = isl_ast_graft_list_get_ast_graft(list, 0); + graft = isl_ast_graft_add_guard(graft, isl_set_copy(guard), build); + list = isl_ast_graft_list_set_ast_graft(list, 0, graft); + + return list; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree. + * In particular, do so for the specified subset of the schedule domain. + * + * If we are outside of the isolated part, then "domain" may include + * a disjunction. Explicitly generate this disjunction at this point + * instead of relying on the disjunction getting hoisted back up + * to this level. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_tree_part( + __isl_keep isl_union_map *executed, __isl_take isl_set *domain, + __isl_keep isl_ast_build *build, int isolated) +{ + isl_union_set *uset; + isl_ast_graft_list *list; + isl_ast_build *sub_build; + int empty; + + uset = isl_union_set_from_set(isl_set_copy(domain)); + executed = isl_union_map_copy(executed); + executed = isl_union_map_intersect_domain(executed, uset); + empty = isl_union_map_is_empty(executed); + if (empty < 0) + goto error; + if (empty) { + isl_ctx *ctx; + isl_union_map_free(executed); + isl_set_free(domain); + ctx = isl_ast_build_get_ctx(build); + return isl_ast_graft_list_alloc(ctx, 0); + } + + sub_build = isl_ast_build_copy(build); + if (!isolated) { + domain = extract_disjunction(domain, build); + sub_build = isl_ast_build_restrict_generated(sub_build, + isl_set_copy(domain)); + } + list = generate_shifted_component_tree_base(executed, + isl_ast_build_copy(sub_build), isolated); + if (!isolated) + list = list_add_guard(list, domain, build, sub_build); + isl_ast_build_free(sub_build); + isl_set_free(domain); + return list; +error: + isl_union_map_free(executed); + isl_set_free(domain); + return NULL; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree. + * In particular, do so for the specified sequence of subsets + * of the schedule domain, "before", "isolated", "after" and "other", + * where only the "isolated" part is considered to be isolated. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_parts( + __isl_take isl_union_map *executed, __isl_take isl_set *before, + __isl_take isl_set *isolated, __isl_take isl_set *after, + __isl_take isl_set *other, __isl_take isl_ast_build *build) +{ + isl_ast_graft_list *list, *res; + + res = generate_shifted_component_tree_part(executed, before, build, 0); + list = generate_shifted_component_tree_part(executed, isolated, + build, 1); + res = isl_ast_graft_list_concat(res, list); + list = generate_shifted_component_tree_part(executed, after, build, 0); + res = isl_ast_graft_list_concat(res, list); + list = generate_shifted_component_tree_part(executed, other, build, 0); + res = isl_ast_graft_list_concat(res, list); + + isl_union_map_free(executed); + isl_ast_build_free(build); + + return res; +} + +/* Does "set" intersect "first", but not "second"? + */ +static isl_bool only_intersects_first(__isl_keep isl_set *set, + __isl_keep isl_set *first, __isl_keep isl_set *second) +{ + isl_bool disjoint; + + disjoint = isl_set_is_disjoint(set, first); + if (disjoint < 0) + return isl_bool_error; + if (disjoint) + return isl_bool_false; + + return isl_set_is_disjoint(set, second); +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree. + * In particular, do so in case of isolation where there is + * only an "isolated" part and an "after" part. + * "dead1" and "dead2" are freed by this function in order to simplify + * the caller. + * + * The "before" and "other" parts are set to empty sets. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_only_after( + __isl_take isl_union_map *executed, __isl_take isl_set *isolated, + __isl_take isl_set *after, __isl_take isl_ast_build *build, + __isl_take isl_set *dead1, __isl_take isl_set *dead2) +{ + isl_set *empty; + + empty = isl_set_empty(isl_set_get_space(after)); + isl_set_free(dead1); + isl_set_free(dead2); + return generate_shifted_component_parts(executed, isl_set_copy(empty), + isolated, after, empty, build); +} + +/* Generate code for a single component, after shifting (if any) + * has been applied, in case the schedule was specified as a schedule tree. + * + * We first check if the user has specified an isolated schedule domain + * and that we are not already outside of this isolated schedule domain. + * If so, we break up the schedule domain into iterations that + * precede the isolated domain, the isolated domain itself, + * the iterations that follow the isolated domain and + * the remaining iterations (those that are incomparable + * to the isolated domain). + * We generate an AST for each piece and concatenate the results. + * + * In the special case where at least one element of the schedule + * domain that does not belong to the isolated domain needs + * to be scheduled after this isolated domain, but none of those + * elements need to be scheduled before, break up the schedule domain + * in only two parts, the isolated domain, and a part that will be + * scheduled after the isolated domain. + * + * If no isolated set has been specified, then we generate an + * AST for the entire inverse schedule. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_tree( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + int i, depth; + int empty, has_isolate; + isl_space *space; + isl_union_set *schedule_domain; + isl_set *domain; + isl_basic_set *hull; + isl_set *isolated, *before, *after, *test; + isl_map *gt, *lt; + isl_bool pure; + + build = isl_ast_build_extract_isolated(build); + has_isolate = isl_ast_build_has_isolated(build); + if (has_isolate < 0) + executed = isl_union_map_free(executed); + else if (!has_isolate) + return generate_shifted_component_tree_base(executed, build, 0); + + schedule_domain = isl_union_map_domain(isl_union_map_copy(executed)); + domain = isl_set_from_union_set(schedule_domain); + + isolated = isl_ast_build_get_isolated(build); + isolated = isl_set_intersect(isolated, isl_set_copy(domain)); + test = isl_ast_build_specialize(build, isl_set_copy(isolated)); + empty = isl_set_is_empty(test); + isl_set_free(test); + if (empty < 0) + goto error; + if (empty) { + isl_set_free(isolated); + isl_set_free(domain); + return generate_shifted_component_tree_base(executed, build, 0); + } + isolated = isl_ast_build_eliminate(build, isolated); + hull = isl_set_unshifted_simple_hull(isolated); + isolated = isl_set_from_basic_set(hull); + + depth = isl_ast_build_get_depth(build); + space = isl_space_map_from_set(isl_set_get_space(isolated)); + gt = isl_map_universe(space); + for (i = 0; i < depth; ++i) + gt = isl_map_equate(gt, isl_dim_in, i, isl_dim_out, i); + gt = isl_map_order_gt(gt, isl_dim_in, depth, isl_dim_out, depth); + lt = isl_map_reverse(isl_map_copy(gt)); + before = isl_set_apply(isl_set_copy(isolated), gt); + after = isl_set_apply(isl_set_copy(isolated), lt); + + domain = isl_set_subtract(domain, isl_set_copy(isolated)); + pure = only_intersects_first(domain, after, before); + if (pure < 0) + executed = isl_union_map_free(executed); + else if (pure) + return generate_shifted_component_only_after(executed, isolated, + domain, build, before, after); + domain = isl_set_subtract(domain, isl_set_copy(before)); + domain = isl_set_subtract(domain, isl_set_copy(after)); + after = isl_set_subtract(after, isl_set_copy(isolated)); + after = isl_set_subtract(after, isl_set_copy(before)); + before = isl_set_subtract(before, isl_set_copy(isolated)); + + return generate_shifted_component_parts(executed, before, isolated, + after, domain, build); +error: + isl_set_free(domain); + isl_set_free(isolated); + isl_union_map_free(executed); + isl_ast_build_free(build); + return NULL; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied. + * + * Call generate_shifted_component_tree or generate_shifted_component_flat + * depending on whether the schedule was specified as a schedule tree. + */ +static __isl_give isl_ast_graft_list *generate_shifted_component( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + if (isl_ast_build_has_schedule_node(build)) + return generate_shifted_component_tree(executed, build); + else + return generate_shifted_component_flat(executed, build); +} + +struct isl_set_map_pair { + isl_set *set; + isl_map *map; +}; + +/* Given an array "domain" of isl_set_map_pairs and an array "order" + * of indices into the "domain" array, + * return the union of the "map" fields of the elements + * indexed by the first "n" elements of "order". + */ +static __isl_give isl_union_map *construct_component_executed( + struct isl_set_map_pair *domain, int *order, int n) +{ + int i; + isl_map *map; + isl_union_map *executed; + + map = isl_map_copy(domain[order[0]].map); + executed = isl_union_map_from_map(map); + for (i = 1; i < n; ++i) { + map = isl_map_copy(domain[order[i]].map); + executed = isl_union_map_add_map(executed, map); + } + + return executed; +} + +/* Generate code for a single component, after shifting (if any) + * has been applied. + * + * The component inverse schedule is specified as the "map" fields + * of the elements of "domain" indexed by the first "n" elements of "order". + */ +static __isl_give isl_ast_graft_list *generate_shifted_component_from_list( + struct isl_set_map_pair *domain, int *order, int n, + __isl_take isl_ast_build *build) +{ + isl_union_map *executed; + + executed = construct_component_executed(domain, order, n); + return generate_shifted_component(executed, build); +} + +/* Does set dimension "pos" of "set" have an obviously fixed value? + */ +static int dim_is_fixed(__isl_keep isl_set *set, int pos) +{ + int fixed; + isl_val *v; + + v = isl_set_plain_get_val_if_fixed(set, isl_dim_set, pos); + if (!v) + return -1; + fixed = !isl_val_is_nan(v); + isl_val_free(v); + + return fixed; +} + +/* Given an array "domain" of isl_set_map_pairs and an array "order" + * of indices into the "domain" array, + * do all (except for at most one) of the "set" field of the elements + * indexed by the first "n" elements of "order" have a fixed value + * at position "depth"? + */ +static int at_most_one_non_fixed(struct isl_set_map_pair *domain, + int *order, int n, int depth) +{ + int i; + int non_fixed = -1; + + for (i = 0; i < n; ++i) { + int f; + + f = dim_is_fixed(domain[order[i]].set, depth); + if (f < 0) + return -1; + if (f) + continue; + if (non_fixed >= 0) + return 0; + non_fixed = i; + } + + return 1; +} + +/* Given an array "domain" of isl_set_map_pairs and an array "order" + * of indices into the "domain" array, + * eliminate the inner dimensions from the "set" field of the elements + * indexed by the first "n" elements of "order", provided the current + * dimension does not have a fixed value. + * + * Return the index of the first element in "order" with a corresponding + * "set" field that does not have an (obviously) fixed value. + */ +static int eliminate_non_fixed(struct isl_set_map_pair *domain, + int *order, int n, int depth, __isl_keep isl_ast_build *build) +{ + int i; + int base = -1; + + for (i = n - 1; i >= 0; --i) { + int f; + f = dim_is_fixed(domain[order[i]].set, depth); + if (f < 0) + return -1; + if (f) + continue; + domain[order[i]].set = isl_ast_build_eliminate_inner(build, + domain[order[i]].set); + base = i; + } + + return base; +} + +/* Given an array "domain" of isl_set_map_pairs and an array "order" + * of indices into the "domain" array, + * find the element of "domain" (amongst those indexed by the first "n" + * elements of "order") with the "set" field that has the smallest + * value for the current iterator. + * + * Note that the domain with the smallest value may depend on the parameters + * and/or outer loop dimension. Since the result of this function is only + * used as heuristic, we only make a reasonable attempt at finding the best + * domain, one that should work in case a single domain provides the smallest + * value for the current dimension over all values of the parameters + * and outer dimensions. + * + * In particular, we compute the smallest value of the first domain + * and replace it by that of any later domain if that later domain + * has a smallest value that is smaller for at least some value + * of the parameters and outer dimensions. + */ +static int first_offset(struct isl_set_map_pair *domain, int *order, int n, + __isl_keep isl_ast_build *build) +{ + int i; + isl_map *min_first; + int first = 0; + + min_first = isl_ast_build_map_to_iterator(build, + isl_set_copy(domain[order[0]].set)); + min_first = isl_map_lexmin(min_first); + + for (i = 1; i < n; ++i) { + isl_map *min, *test; + int empty; + + min = isl_ast_build_map_to_iterator(build, + isl_set_copy(domain[order[i]].set)); + min = isl_map_lexmin(min); + test = isl_map_copy(min); + test = isl_map_apply_domain(isl_map_copy(min_first), test); + test = isl_map_order_lt(test, isl_dim_in, 0, isl_dim_out, 0); + empty = isl_map_is_empty(test); + isl_map_free(test); + if (empty >= 0 && !empty) { + isl_map_free(min_first); + first = i; + min_first = min; + } else + isl_map_free(min); + + if (empty < 0) + break; + } + + isl_map_free(min_first); + + return i < n ? -1 : first; +} + +/* Construct a shifted inverse schedule based on the original inverse schedule, + * the stride and the offset. + * + * The original inverse schedule is specified as the "map" fields + * of the elements of "domain" indexed by the first "n" elements of "order". + * + * "stride" and "offset" are such that the difference + * between the values of the current dimension of domain "i" + * and the values of the current dimension for some reference domain are + * equal to + * + * stride * integer + offset[i] + * + * Moreover, 0 <= offset[i] < stride. + * + * For each domain, we create a map + * + * { [..., j, ...] -> [..., j - offset[i], offset[i], ....] } + * + * where j refers to the current dimension and the other dimensions are + * unchanged, and apply this map to the original schedule domain. + * + * For example, for the original schedule + * + * { A[i] -> [2i]: 0 <= i < 10; B[i] -> [2i+1] : 0 <= i < 10 } + * + * and assuming the offset is 0 for the A domain and 1 for the B domain, + * we apply the mapping + * + * { [j] -> [j, 0] } + * + * to the schedule of the "A" domain and the mapping + * + * { [j - 1] -> [j, 1] } + * + * to the schedule of the "B" domain. + * + * + * Note that after the transformation, the differences between pairs + * of values of the current dimension over all domains are multiples + * of stride and that we have therefore exposed the stride. + * + * + * To see that the mapping preserves the lexicographic order, + * first note that each of the individual maps above preserves the order. + * If the value of the current iterator is j1 in one domain and j2 in another, + * then if j1 = j2, we know that the same map is applied to both domains + * and the order is preserved. + * Otherwise, let us assume, without loss of generality, that j1 < j2. + * If c1 >= c2 (with c1 and c2 the corresponding offsets), then + * + * j1 - c1 < j2 - c2 + * + * and the order is preserved. + * If c1 < c2, then we know + * + * 0 <= c2 - c1 < s + * + * We also have + * + * j2 - j1 = n * s + r + * + * with n >= 0 and 0 <= r < s. + * In other words, r = c2 - c1. + * If n > 0, then + * + * j1 - c1 < j2 - c2 + * + * If n = 0, then + * + * j1 - c1 = j2 - c2 + * + * and so + * + * (j1 - c1, c1) << (j2 - c2, c2) + * + * with "<<" the lexicographic order, proving that the order is preserved + * in all cases. + */ +static __isl_give isl_union_map *contruct_shifted_executed( + struct isl_set_map_pair *domain, int *order, int n, + __isl_keep isl_val *stride, __isl_keep isl_multi_val *offset, + __isl_take isl_ast_build *build) +{ + int i; + isl_union_map *executed; + isl_space *space; + isl_map *map; + int depth; + isl_constraint *c; + + depth = isl_ast_build_get_depth(build); + space = isl_ast_build_get_space(build, 1); + executed = isl_union_map_empty(isl_space_copy(space)); + space = isl_space_map_from_set(space); + map = isl_map_identity(isl_space_copy(space)); + map = isl_map_eliminate(map, isl_dim_out, depth, 1); + map = isl_map_insert_dims(map, isl_dim_out, depth + 1, 1); + space = isl_space_insert_dims(space, isl_dim_out, depth + 1, 1); + + c = isl_constraint_alloc_equality(isl_local_space_from_space(space)); + c = isl_constraint_set_coefficient_si(c, isl_dim_in, depth, 1); + c = isl_constraint_set_coefficient_si(c, isl_dim_out, depth, -1); + + for (i = 0; i < n; ++i) { + isl_map *map_i; + isl_val *v; + + v = isl_multi_val_get_val(offset, i); + if (!v) + break; + map_i = isl_map_copy(map); + map_i = isl_map_fix_val(map_i, isl_dim_out, depth + 1, + isl_val_copy(v)); + v = isl_val_neg(v); + c = isl_constraint_set_constant_val(c, v); + map_i = isl_map_add_constraint(map_i, isl_constraint_copy(c)); + + map_i = isl_map_apply_domain(isl_map_copy(domain[order[i]].map), + map_i); + executed = isl_union_map_add_map(executed, map_i); + } + + isl_constraint_free(c); + isl_map_free(map); + + if (i < n) + executed = isl_union_map_free(executed); + + return executed; +} + +/* Generate code for a single component, after exposing the stride, + * given that the schedule domain is "shifted strided". + * + * The component inverse schedule is specified as the "map" fields + * of the elements of "domain" indexed by the first "n" elements of "order". + * + * The schedule domain being "shifted strided" means that the differences + * between the values of the current dimension of domain "i" + * and the values of the current dimension for some reference domain are + * equal to + * + * stride * integer + offset[i] + * + * We first look for the domain with the "smallest" value for the current + * dimension and adjust the offsets such that the offset of the "smallest" + * domain is equal to zero. The other offsets are reduced modulo stride. + * + * Based on this information, we construct a new inverse schedule in + * contruct_shifted_executed that exposes the stride. + * Since this involves the introduction of a new schedule dimension, + * the build needs to be changed accodingly. + * After computing the AST, the newly introduced dimension needs + * to be removed again from the list of grafts. We do this by plugging + * in a mapping that represents the new schedule domain in terms of the + * old schedule domain. + */ +static __isl_give isl_ast_graft_list *generate_shift_component( + struct isl_set_map_pair *domain, int *order, int n, + __isl_keep isl_val *stride, __isl_keep isl_multi_val *offset, + __isl_take isl_ast_build *build) +{ + isl_ast_graft_list *list; + int first; + int depth; + isl_val *val; + isl_multi_val *mv; + isl_space *space; + isl_multi_aff *ma, *zero; + isl_union_map *executed; + + depth = isl_ast_build_get_depth(build); + + first = first_offset(domain, order, n, build); + if (first < 0) + goto error; + + mv = isl_multi_val_copy(offset); + val = isl_multi_val_get_val(offset, first); + val = isl_val_neg(val); + mv = isl_multi_val_add_val(mv, val); + mv = isl_multi_val_mod_val(mv, isl_val_copy(stride)); + + executed = contruct_shifted_executed(domain, order, n, stride, mv, + build); + space = isl_ast_build_get_space(build, 1); + space = isl_space_map_from_set(space); + ma = isl_multi_aff_identity(isl_space_copy(space)); + space = isl_space_from_domain(isl_space_domain(space)); + space = isl_space_add_dims(space, isl_dim_out, 1); + zero = isl_multi_aff_zero(space); + ma = isl_multi_aff_range_splice(ma, depth + 1, zero); + build = isl_ast_build_insert_dim(build, depth + 1); + list = generate_shifted_component(executed, build); + + list = isl_ast_graft_list_preimage_multi_aff(list, ma); + + isl_multi_val_free(mv); + + return list; +error: + isl_ast_build_free(build); + return NULL; +} + +/* Does any node in the schedule tree rooted at the current schedule node + * of "build" depend on outer schedule nodes? + */ +static int has_anchored_subtree(__isl_keep isl_ast_build *build) +{ + isl_schedule_node *node; + int dependent = 0; + + node = isl_ast_build_get_schedule_node(build); + dependent = isl_schedule_node_is_subtree_anchored(node); + isl_schedule_node_free(node); + + return dependent; +} + +/* Generate code for a single component. + * + * The component inverse schedule is specified as the "map" fields + * of the elements of "domain" indexed by the first "n" elements of "order". + * + * This function may modify the "set" fields of "domain". + * + * Before proceeding with the actual code generation for the component, + * we first check if there are any "shifted" strides, meaning that + * the schedule domains of the individual domains are all strided, + * but that they have different offsets, resulting in the union + * of schedule domains not being strided anymore. + * + * The simplest example is the schedule + * + * { A[i] -> [2i]: 0 <= i < 10; B[i] -> [2i+1] : 0 <= i < 10 } + * + * Both schedule domains are strided, but their union is not. + * This function detects such cases and then rewrites the schedule to + * + * { A[i] -> [2i, 0]: 0 <= i < 10; B[i] -> [2i, 1] : 0 <= i < 10 } + * + * In the new schedule, the schedule domains have the same offset (modulo + * the stride), ensuring that the union of schedule domains is also strided. + * + * + * If there is only a single domain in the component, then there is + * nothing to do. Similarly, if the current schedule dimension has + * a fixed value for almost all domains then there is nothing to be done. + * In particular, we need at least two domains where the current schedule + * dimension does not have a fixed value. + * Finally, in case of a schedule map input, + * if any of the options refer to the current schedule dimension, + * then we bail out as well. It would be possible to reformulate the options + * in terms of the new schedule domain, but that would introduce constraints + * that separate the domains in the options and that is something we would + * like to avoid. + * In the case of a schedule tree input, we bail out if any of + * the descendants of the current schedule node refer to outer + * schedule nodes in any way. + * + * + * To see if there is any shifted stride, we look at the differences + * between the values of the current dimension in pairs of domains + * for equal values of outer dimensions. These differences should be + * of the form + * + * m x + r + * + * with "m" the stride and "r" a constant. Note that we cannot perform + * this analysis on individual domains as the lower bound in each domain + * may depend on parameters or outer dimensions and so the current dimension + * itself may not have a fixed remainder on division by the stride. + * + * In particular, we compare the first domain that does not have an + * obviously fixed value for the current dimension to itself and all + * other domains and collect the offsets and the gcd of the strides. + * If the gcd becomes one, then we failed to find shifted strides. + * If the gcd is zero, then the differences were all fixed, meaning + * that some domains had non-obviously fixed values for the current dimension. + * If all the offsets are the same (for those domains that do not have + * an obviously fixed value for the current dimension), then we do not + * apply the transformation. + * If none of the domains were skipped, then there is nothing to do. + * If some of them were skipped, then if we apply separation, the schedule + * domain should get split in pieces with a (non-shifted) stride. + * + * Otherwise, we apply a shift to expose the stride in + * generate_shift_component. + */ +static __isl_give isl_ast_graft_list *generate_component( + struct isl_set_map_pair *domain, int *order, int n, + __isl_take isl_ast_build *build) +{ + int i, d; + int depth; + isl_ctx *ctx; + isl_map *map; + isl_set *deltas; + isl_val *gcd = NULL; + isl_multi_val *mv; + int fixed, skip; + int base; + isl_ast_graft_list *list; + int res = 0; + + depth = isl_ast_build_get_depth(build); + + skip = n == 1; + if (skip >= 0 && !skip) + skip = at_most_one_non_fixed(domain, order, n, depth); + if (skip >= 0 && !skip) { + if (isl_ast_build_has_schedule_node(build)) + skip = has_anchored_subtree(build); + else + skip = isl_ast_build_options_involve_depth(build); + } + if (skip < 0) + goto error; + if (skip) + return generate_shifted_component_from_list(domain, + order, n, build); + + base = eliminate_non_fixed(domain, order, n, depth, build); + if (base < 0) + goto error; + + ctx = isl_ast_build_get_ctx(build); + + mv = isl_multi_val_zero(isl_space_set_alloc(ctx, 0, n)); + + fixed = 1; + for (i = 0; i < n; ++i) { + isl_val *r, *m; + + map = isl_map_from_domain_and_range( + isl_set_copy(domain[order[base]].set), + isl_set_copy(domain[order[i]].set)); + for (d = 0; d < depth; ++d) + map = isl_map_equate(map, isl_dim_in, d, + isl_dim_out, d); + deltas = isl_map_deltas(map); + res = isl_set_dim_residue_class_val(deltas, depth, &m, &r); + isl_set_free(deltas); + if (res < 0) + break; + + if (i == 0) + gcd = m; + else + gcd = isl_val_gcd(gcd, m); + if (isl_val_is_one(gcd)) { + isl_val_free(r); + break; + } + mv = isl_multi_val_set_val(mv, i, r); + + res = dim_is_fixed(domain[order[i]].set, depth); + if (res < 0) + break; + if (res) + continue; + + if (fixed && i > base) { + isl_val *a, *b; + a = isl_multi_val_get_val(mv, i); + b = isl_multi_val_get_val(mv, base); + if (isl_val_ne(a, b)) + fixed = 0; + isl_val_free(a); + isl_val_free(b); + } + } + + if (res < 0 || !gcd) { + isl_ast_build_free(build); + list = NULL; + } else if (i < n || fixed || isl_val_is_zero(gcd)) { + list = generate_shifted_component_from_list(domain, + order, n, build); + } else { + list = generate_shift_component(domain, order, n, gcd, mv, + build); + } + + isl_val_free(gcd); + isl_multi_val_free(mv); + + return list; +error: + isl_ast_build_free(build); + return NULL; +} + +/* Store both "map" itself and its domain in the + * structure pointed to by *next and advance to the next array element. + */ +static isl_stat extract_domain(__isl_take isl_map *map, void *user) +{ + struct isl_set_map_pair **next = user; + + (*next)->map = isl_map_copy(map); + (*next)->set = isl_map_domain(map); + (*next)++; + + return isl_stat_ok; +} + +static int after_in_tree(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node); + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the child of "node"? + */ +static int after_in_child(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + isl_schedule_node *child; + int after; + + child = isl_schedule_node_get_child(node, 0); + after = after_in_tree(umap, child); + isl_schedule_node_free(child); + + return after; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the band node "node"? + * + * We first check if any domain element is scheduled after any + * of the corresponding image elements by the band node itself. + * If not, we restrict "map" to those pairs of element that + * are scheduled together by the band node and continue with + * the child of the band node. + * If there are no such pairs then the map passed to after_in_child + * will be empty causing it to return 0. + */ +static int after_in_band(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + isl_multi_union_pw_aff *mupa; + isl_union_map *partial, *test, *gt, *universe, *umap1, *umap2; + isl_union_set *domain, *range; + isl_space *space; + int empty; + int after; + + if (isl_schedule_node_band_n_member(node) == 0) + return after_in_child(umap, node); + + mupa = isl_schedule_node_band_get_partial_schedule(node); + space = isl_multi_union_pw_aff_get_space(mupa); + partial = isl_union_map_from_multi_union_pw_aff(mupa); + test = isl_union_map_copy(umap); + test = isl_union_map_apply_domain(test, isl_union_map_copy(partial)); + test = isl_union_map_apply_range(test, isl_union_map_copy(partial)); + gt = isl_union_map_from_map(isl_map_lex_gt(space)); + test = isl_union_map_intersect(test, gt); + empty = isl_union_map_is_empty(test); + isl_union_map_free(test); + + if (empty < 0 || !empty) { + isl_union_map_free(partial); + return empty < 0 ? -1 : 1; + } + + universe = isl_union_map_universe(isl_union_map_copy(umap)); + domain = isl_union_map_domain(isl_union_map_copy(universe)); + range = isl_union_map_range(universe); + umap1 = isl_union_map_copy(partial); + umap1 = isl_union_map_intersect_domain(umap1, domain); + umap2 = isl_union_map_intersect_domain(partial, range); + test = isl_union_map_apply_range(umap1, isl_union_map_reverse(umap2)); + test = isl_union_map_intersect(test, isl_union_map_copy(umap)); + after = after_in_child(test, node); + isl_union_map_free(test); + return after; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the context node "node"? + * + * The context constraints apply to the schedule domain, + * so we cannot apply them directly to "umap", which contains + * pairs of statement instances. Instead, we add them + * to the range of the prefix schedule for both domain and + * range of "umap". + */ +static int after_in_context(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + isl_union_map *prefix, *universe, *umap1, *umap2; + isl_union_set *domain, *range; + isl_set *context; + int after; + + umap = isl_union_map_copy(umap); + context = isl_schedule_node_context_get_context(node); + prefix = isl_schedule_node_get_prefix_schedule_union_map(node); + universe = isl_union_map_universe(isl_union_map_copy(umap)); + domain = isl_union_map_domain(isl_union_map_copy(universe)); + range = isl_union_map_range(universe); + umap1 = isl_union_map_copy(prefix); + umap1 = isl_union_map_intersect_domain(umap1, domain); + umap2 = isl_union_map_intersect_domain(prefix, range); + umap1 = isl_union_map_intersect_range(umap1, + isl_union_set_from_set(context)); + umap1 = isl_union_map_apply_range(umap1, isl_union_map_reverse(umap2)); + umap = isl_union_map_intersect(umap, umap1); + + after = after_in_child(umap, node); + + isl_union_map_free(umap); + + return after; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the expansion node "node"? + * + * We apply the expansion to domain and range of "umap" and + * continue with its child. + */ +static int after_in_expansion(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + isl_union_map *expansion; + int after; + + expansion = isl_schedule_node_expansion_get_expansion(node); + umap = isl_union_map_copy(umap); + umap = isl_union_map_apply_domain(umap, isl_union_map_copy(expansion)); + umap = isl_union_map_apply_range(umap, expansion); + + after = after_in_child(umap, node); + + isl_union_map_free(umap); + + return after; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the extension node "node"? + * + * Since the extension node may add statement instances before or + * after the pairs of statement instances in "umap", we return 1 + * to ensure that these pairs are not broken up. + */ +static int after_in_extension(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + return 1; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the filter node "node"? + * + * We intersect domain and range of "umap" with the filter and + * continue with its child. + */ +static int after_in_filter(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + isl_union_set *filter; + int after; + + umap = isl_union_map_copy(umap); + filter = isl_schedule_node_filter_get_filter(node); + umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(filter)); + umap = isl_union_map_intersect_range(umap, filter); + + after = after_in_child(umap, node); + + isl_union_map_free(umap); + + return after; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the set node "node"? + * + * This is only the case if this condition holds in any + * of the (filter) children of the set node. + * In particular, if the domain and the range of "umap" + * are contained in different children, then the condition + * does not hold. + */ +static int after_in_set(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + int i, n; + + n = isl_schedule_node_n_children(node); + for (i = 0; i < n; ++i) { + isl_schedule_node *child; + int after; + + child = isl_schedule_node_get_child(node, i); + after = after_in_tree(umap, child); + isl_schedule_node_free(child); + + if (after < 0 || after) + return after; + } + + return 0; +} + +/* Return the filter of child "i" of "node". + */ +static __isl_give isl_union_set *child_filter( + __isl_keep isl_schedule_node *node, int i) +{ + isl_schedule_node *child; + isl_union_set *filter; + + child = isl_schedule_node_get_child(node, i); + filter = isl_schedule_node_filter_get_filter(child); + isl_schedule_node_free(child); + + return filter; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at + * the sequence node "node"? + * + * This happens in particular if any domain element is + * contained in a later child than one containing a range element or + * if the condition holds within a given child in the sequence. + * The later part of the condition is checked by after_in_set. + */ +static int after_in_sequence(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + int i, j, n; + isl_union_map *umap_i; + int empty, after = 0; + + n = isl_schedule_node_n_children(node); + for (i = 1; i < n; ++i) { + isl_union_set *filter_i; + + umap_i = isl_union_map_copy(umap); + filter_i = child_filter(node, i); + umap_i = isl_union_map_intersect_domain(umap_i, filter_i); + empty = isl_union_map_is_empty(umap_i); + if (empty < 0) + goto error; + if (empty) { + isl_union_map_free(umap_i); + continue; + } + + for (j = 0; j < i; ++j) { + isl_union_set *filter_j; + isl_union_map *umap_ij; + + umap_ij = isl_union_map_copy(umap_i); + filter_j = child_filter(node, j); + umap_ij = isl_union_map_intersect_range(umap_ij, + filter_j); + empty = isl_union_map_is_empty(umap_ij); + isl_union_map_free(umap_ij); + + if (empty < 0) + goto error; + if (!empty) + after = 1; + if (after) + break; + } + + isl_union_map_free(umap_i); + if (after) + break; + } + + if (after < 0 || after) + return after; + + return after_in_set(umap, node); +error: + isl_union_map_free(umap_i); + return -1; +} + +/* Is any domain element of "umap" scheduled after any of + * the corresponding image elements by the tree rooted at "node"? + * + * If "umap" is empty, then clearly there is no such element. + * Otherwise, consider the different types of nodes separately. + */ +static int after_in_tree(__isl_keep isl_union_map *umap, + __isl_keep isl_schedule_node *node) +{ + int empty; + enum isl_schedule_node_type type; + + empty = isl_union_map_is_empty(umap); + if (empty < 0) + return -1; + if (empty) + return 0; + if (!node) + return -1; + + type = isl_schedule_node_get_type(node); + switch (type) { + case isl_schedule_node_error: + return -1; + case isl_schedule_node_leaf: + return 0; + case isl_schedule_node_band: + return after_in_band(umap, node); + case isl_schedule_node_domain: + isl_die(isl_schedule_node_get_ctx(node), isl_error_internal, + "unexpected internal domain node", return -1); + case isl_schedule_node_context: + return after_in_context(umap, node); + case isl_schedule_node_expansion: + return after_in_expansion(umap, node); + case isl_schedule_node_extension: + return after_in_extension(umap, node); + case isl_schedule_node_filter: + return after_in_filter(umap, node); + case isl_schedule_node_guard: + case isl_schedule_node_mark: + return after_in_child(umap, node); + case isl_schedule_node_set: + return after_in_set(umap, node); + case isl_schedule_node_sequence: + return after_in_sequence(umap, node); + } + + return 1; +} + +/* Is any domain element of "map1" scheduled after any domain + * element of "map2" by the subtree underneath the current band node, + * while at the same time being scheduled together by the current + * band node, i.e., by "map1" and "map2? + * + * If the child of the current band node is a leaf, then + * no element can be scheduled after any other element. + * + * Otherwise, we construct a relation between domain elements + * of "map1" and domain elements of "map2" that are scheduled + * together and then check if the subtree underneath the current + * band node determines their relative order. + */ +static int after_in_subtree(__isl_keep isl_ast_build *build, + __isl_keep isl_map *map1, __isl_keep isl_map *map2) +{ + isl_schedule_node *node; + isl_map *map; + isl_union_map *umap; + int after; + + node = isl_ast_build_get_schedule_node(build); + if (!node) + return -1; + node = isl_schedule_node_child(node, 0); + if (isl_schedule_node_get_type(node) == isl_schedule_node_leaf) { + isl_schedule_node_free(node); + return 0; + } + map = isl_map_copy(map2); + map = isl_map_apply_domain(map, isl_map_copy(map1)); + umap = isl_union_map_from_map(map); + after = after_in_tree(umap, node); + isl_union_map_free(umap); + isl_schedule_node_free(node); + return after; +} + +/* Internal data for any_scheduled_after. + * + * "build" is the build in which the AST is constructed. + * "depth" is the number of loops that have already been generated + * "group_coscheduled" is a local copy of options->ast_build_group_coscheduled + * "domain" is an array of set-map pairs corresponding to the different + * iteration domains. The set is the schedule domain, i.e., the domain + * of the inverse schedule, while the map is the inverse schedule itself. + */ +struct isl_any_scheduled_after_data { + isl_ast_build *build; + int depth; + int group_coscheduled; + struct isl_set_map_pair *domain; +}; + +/* Is any element of domain "i" scheduled after any element of domain "j" + * (for a common iteration of the first data->depth loops)? + * + * data->domain[i].set contains the domain of the inverse schedule + * for domain "i", i.e., elements in the schedule domain. + * + * If we are inside a band of a schedule tree and there is a pair + * of elements in the two domains that is schedule together by + * the current band, then we check if any element of "i" may be schedule + * after element of "j" by the descendants of the band node. + * + * If data->group_coscheduled is set, then we also return 1 if there + * is any pair of elements in the two domains that are scheduled together. + */ +static isl_bool any_scheduled_after(int i, int j, void *user) +{ + struct isl_any_scheduled_after_data *data = user; + int dim = isl_set_dim(data->domain[i].set, isl_dim_set); + int pos; + + for (pos = data->depth; pos < dim; ++pos) { + int follows; + + follows = isl_set_follows_at(data->domain[i].set, + data->domain[j].set, pos); + + if (follows < -1) + return isl_bool_error; + if (follows > 0) + return isl_bool_true; + if (follows < 0) + return isl_bool_false; + } + + if (isl_ast_build_has_schedule_node(data->build)) { + int after; + + after = after_in_subtree(data->build, data->domain[i].map, + data->domain[j].map); + if (after < 0 || after) + return after; + } + + return data->group_coscheduled; +} + +/* Look for independent components at the current depth and generate code + * for each component separately. The resulting lists of grafts are + * merged in an attempt to combine grafts with identical guards. + * + * Code for two domains can be generated separately if all the elements + * of one domain are scheduled before (or together with) all the elements + * of the other domain. We therefore consider the graph with as nodes + * the domains and an edge between two nodes if any element of the first + * node is scheduled after any element of the second node. + * If the ast_build_group_coscheduled is set, then we also add an edge if + * there is any pair of elements in the two domains that are scheduled + * together. + * Code is then generated (by generate_component) + * for each of the strongly connected components in this graph + * in their topological order. + * + * Since the test is performed on the domain of the inverse schedules of + * the different domains, we precompute these domains and store + * them in data.domain. + */ +static __isl_give isl_ast_graft_list *generate_components( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + int i; + isl_ctx *ctx = isl_ast_build_get_ctx(build); + int n = isl_union_map_n_map(executed); + struct isl_any_scheduled_after_data data; + struct isl_set_map_pair *next; + struct isl_tarjan_graph *g = NULL; + isl_ast_graft_list *list = NULL; + int n_domain = 0; + + data.domain = isl_calloc_array(ctx, struct isl_set_map_pair, n); + if (!data.domain) + goto error; + n_domain = n; + + next = data.domain; + if (isl_union_map_foreach_map(executed, &extract_domain, &next) < 0) + goto error; + + if (!build) + goto error; + data.build = build; + data.depth = isl_ast_build_get_depth(build); + data.group_coscheduled = isl_options_get_ast_build_group_coscheduled(ctx); + g = isl_tarjan_graph_init(ctx, n, &any_scheduled_after, &data); + if (!g) + goto error; + + list = isl_ast_graft_list_alloc(ctx, 0); + + i = 0; + while (list && n) { + isl_ast_graft_list *list_c; + int first = i; + + if (g->order[i] == -1) + isl_die(ctx, isl_error_internal, "cannot happen", + goto error); + ++i; --n; + while (g->order[i] != -1) { + ++i; --n; + } + + list_c = generate_component(data.domain, + g->order + first, i - first, + isl_ast_build_copy(build)); + list = isl_ast_graft_list_merge(list, list_c, build); + + ++i; + } + + if (0) +error: list = isl_ast_graft_list_free(list); + isl_tarjan_graph_free(g); + for (i = 0; i < n_domain; ++i) { + isl_map_free(data.domain[i].map); + isl_set_free(data.domain[i].set); + } + free(data.domain); + isl_union_map_free(executed); + isl_ast_build_free(build); + + return list; +} + +/* Generate code for the next level (and all inner levels). + * + * If "executed" is empty, i.e., no code needs to be generated, + * then we return an empty list. + * + * If we have already generated code for all loop levels, then we pass + * control to generate_inner_level. + * + * If "executed" lives in a single space, i.e., if code needs to be + * generated for a single domain, then there can only be a single + * component and we go directly to generate_shifted_component. + * Otherwise, we call generate_components to detect the components + * and to call generate_component on each of them separately. + */ +static __isl_give isl_ast_graft_list *generate_next_level( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build) +{ + int depth; + + if (!build || !executed) + goto error; + + if (isl_union_map_is_empty(executed)) { + isl_ctx *ctx = isl_ast_build_get_ctx(build); + isl_union_map_free(executed); + isl_ast_build_free(build); + return isl_ast_graft_list_alloc(ctx, 0); + } + + depth = isl_ast_build_get_depth(build); + if (depth >= isl_ast_build_dim(build, isl_dim_set)) + return generate_inner_level(executed, build); + + if (isl_union_map_n_map(executed) == 1) + return generate_shifted_component(executed, build); + + return generate_components(executed, build); +error: + isl_union_map_free(executed); + isl_ast_build_free(build); + return NULL; +} + +/* Internal data structure used by isl_ast_build_node_from_schedule_map. + * internal, executed and build are the inputs to generate_code. + * list collects the output. + */ +struct isl_generate_code_data { + int internal; + isl_union_map *executed; + isl_ast_build *build; + + isl_ast_graft_list *list; +}; + +/* Given an inverse schedule in terms of the external build schedule, i.e., + * + * [E -> S] -> D + * + * with E the external build schedule and S the additional schedule "space", + * reformulate the inverse schedule in terms of the internal schedule domain, + * i.e., return + * + * [I -> S] -> D + * + * We first obtain a mapping + * + * I -> E + * + * take the inverse and the product with S -> S, resulting in + * + * [I -> S] -> [E -> S] + * + * Applying the map to the input produces the desired result. + */ +static __isl_give isl_union_map *internal_executed( + __isl_take isl_union_map *executed, __isl_keep isl_space *space, + __isl_keep isl_ast_build *build) +{ + isl_map *id, *proj; + + proj = isl_ast_build_get_schedule_map(build); + proj = isl_map_reverse(proj); + space = isl_space_map_from_set(isl_space_copy(space)); + id = isl_map_identity(space); + proj = isl_map_product(proj, id); + executed = isl_union_map_apply_domain(executed, + isl_union_map_from_map(proj)); + return executed; +} + +/* Generate an AST that visits the elements in the range of data->executed + * in the relative order specified by the corresponding domain element(s) + * for those domain elements that belong to "set". + * Add the result to data->list. + * + * The caller ensures that "set" is a universe domain. + * "space" is the space of the additional part of the schedule. + * It is equal to the space of "set" if build->domain is parametric. + * Otherwise, it is equal to the range of the wrapped space of "set". + * + * If the build space is not parametric and + * if isl_ast_build_node_from_schedule_map + * was called from an outside user (data->internal not set), then + * the (inverse) schedule refers to the external build domain and needs to + * be transformed to refer to the internal build domain. + * + * If the build space is parametric, then we add some of the parameter + * constraints to the executed relation. Adding these constraints + * allows for an earlier detection of conflicts in some cases. + * However, we do not want to divide the executed relation into + * more disjuncts than necessary. We therefore approximate + * the constraints on the parameters by a single disjunct set. + * + * The build is extended to include the additional part of the schedule. + * If the original build space was not parametric, then the options + * in data->build refer only to the additional part of the schedule + * and they need to be adjusted to refer to the complete AST build + * domain. + * + * After having adjusted inverse schedule and build, we start generating + * code with the outer loop of the current code generation + * in generate_next_level. + * + * If the original build space was not parametric, we undo the embedding + * on the resulting isl_ast_node_list so that it can be used within + * the outer AST build. + */ +static isl_stat generate_code_in_space(struct isl_generate_code_data *data, + __isl_take isl_set *set, __isl_take isl_space *space) +{ + isl_union_map *executed; + isl_ast_build *build; + isl_ast_graft_list *list; + int embed; + + executed = isl_union_map_copy(data->executed); + executed = isl_union_map_intersect_domain(executed, + isl_union_set_from_set(set)); + + embed = !isl_set_is_params(data->build->domain); + if (embed && !data->internal) + executed = internal_executed(executed, space, data->build); + if (!embed) { + isl_set *domain; + domain = isl_ast_build_get_domain(data->build); + domain = isl_set_from_basic_set(isl_set_simple_hull(domain)); + executed = isl_union_map_intersect_params(executed, domain); + } + + build = isl_ast_build_copy(data->build); + build = isl_ast_build_product(build, space); + + list = generate_next_level(executed, build); + + list = isl_ast_graft_list_unembed(list, embed); + + data->list = isl_ast_graft_list_concat(data->list, list); + + return isl_stat_ok; +} + +/* Generate an AST that visits the elements in the range of data->executed + * in the relative order specified by the corresponding domain element(s) + * for those domain elements that belong to "set". + * Add the result to data->list. + * + * The caller ensures that "set" is a universe domain. + * + * If the build space S is not parametric, then the space of "set" + * need to be a wrapped relation with S as domain. That is, it needs + * to be of the form + * + * [S -> T] + * + * Check this property and pass control to generate_code_in_space + * passing along T. + * If the build space is not parametric, then T is the space of "set". + */ +static isl_stat generate_code_set(__isl_take isl_set *set, void *user) +{ + struct isl_generate_code_data *data = user; + isl_space *space, *build_space; + int is_domain; + + space = isl_set_get_space(set); + + if (isl_set_is_params(data->build->domain)) + return generate_code_in_space(data, set, space); + + build_space = isl_ast_build_get_space(data->build, data->internal); + space = isl_space_unwrap(space); + is_domain = isl_space_is_domain(build_space, space); + isl_space_free(build_space); + space = isl_space_range(space); + + if (is_domain < 0) + goto error; + if (!is_domain) + isl_die(isl_set_get_ctx(set), isl_error_invalid, + "invalid nested schedule space", goto error); + + return generate_code_in_space(data, set, space); +error: + isl_set_free(set); + isl_space_free(space); + return isl_stat_error; +} + +/* Generate an AST that visits the elements in the range of "executed" + * in the relative order specified by the corresponding domain element(s). + * + * "build" is an isl_ast_build that has either been constructed by + * isl_ast_build_from_context or passed to a callback set by + * isl_ast_build_set_create_leaf. + * In the first case, the space of the isl_ast_build is typically + * a parametric space, although this is currently not enforced. + * In the second case, the space is never a parametric space. + * If the space S is not parametric, then the domain space(s) of "executed" + * need to be wrapped relations with S as domain. + * + * If the domain of "executed" consists of several spaces, then an AST + * is generated for each of them (in arbitrary order) and the results + * are concatenated. + * + * If "internal" is set, then the domain "S" above refers to the internal + * schedule domain representation. Otherwise, it refers to the external + * representation, as returned by isl_ast_build_get_schedule_space. + * + * We essentially run over all the spaces in the domain of "executed" + * and call generate_code_set on each of them. + */ +static __isl_give isl_ast_graft_list *generate_code( + __isl_take isl_union_map *executed, __isl_take isl_ast_build *build, + int internal) +{ + isl_ctx *ctx; + struct isl_generate_code_data data = { 0 }; + isl_space *space; + isl_union_set *schedule_domain; + isl_union_map *universe; + + if (!build) + goto error; + space = isl_ast_build_get_space(build, 1); + space = isl_space_align_params(space, + isl_union_map_get_space(executed)); + space = isl_space_align_params(space, + isl_union_map_get_space(build->options)); + build = isl_ast_build_align_params(build, isl_space_copy(space)); + executed = isl_union_map_align_params(executed, space); + if (!executed || !build) + goto error; + + ctx = isl_ast_build_get_ctx(build); + + data.internal = internal; + data.executed = executed; + data.build = build; + data.list = isl_ast_graft_list_alloc(ctx, 0); + + universe = isl_union_map_universe(isl_union_map_copy(executed)); + schedule_domain = isl_union_map_domain(universe); + if (isl_union_set_foreach_set(schedule_domain, &generate_code_set, + &data) < 0) + data.list = isl_ast_graft_list_free(data.list); + + isl_union_set_free(schedule_domain); + isl_union_map_free(executed); + + isl_ast_build_free(build); + return data.list; +error: + isl_union_map_free(executed); + isl_ast_build_free(build); + return NULL; +} + +/* Generate an AST that visits the elements in the domain of "schedule" + * in the relative order specified by the corresponding image element(s). + * + * "build" is an isl_ast_build that has either been constructed by + * isl_ast_build_from_context or passed to a callback set by + * isl_ast_build_set_create_leaf. + * In the first case, the space of the isl_ast_build is typically + * a parametric space, although this is currently not enforced. + * In the second case, the space is never a parametric space. + * If the space S is not parametric, then the range space(s) of "schedule" + * need to be wrapped relations with S as domain. + * + * If the range of "schedule" consists of several spaces, then an AST + * is generated for each of them (in arbitrary order) and the results + * are concatenated. + * + * We first initialize the local copies of the relevant options. + * We do this here rather than when the isl_ast_build is created + * because the options may have changed between the construction + * of the isl_ast_build and the call to isl_generate_code. + * + * The main computation is performed on an inverse schedule (with + * the schedule domain in the domain and the elements to be executed + * in the range) called "executed". + */ +__isl_give isl_ast_node *isl_ast_build_node_from_schedule_map( + __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule) +{ + isl_ast_graft_list *list; + isl_ast_node *node; + isl_union_map *executed; + + build = isl_ast_build_copy(build); + build = isl_ast_build_set_single_valued(build, 0); + schedule = isl_union_map_coalesce(schedule); + schedule = isl_union_map_remove_redundancies(schedule); + executed = isl_union_map_reverse(schedule); + list = generate_code(executed, isl_ast_build_copy(build), 0); + node = isl_ast_node_from_graft_list(list, build); + isl_ast_build_free(build); + + return node; +} + +/* The old name for isl_ast_build_node_from_schedule_map. + * It is being kept for backward compatibility, but + * it will be removed in the future. + */ +__isl_give isl_ast_node *isl_ast_build_ast_from_schedule( + __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule) +{ + return isl_ast_build_node_from_schedule_map(build, schedule); +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the band node "node" and its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * If the band is empty, we continue with its descendants. + * Otherwise, we extend the build and the inverse schedule with + * the additional space/partial schedule and continue generating + * an AST in generate_next_level. + * As soon as we have extended the inverse schedule with the additional + * partial schedule, we look for equalities that may exists between + * the old and the new part. + */ +static __isl_give isl_ast_graft_list *build_ast_from_band( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_space *space; + isl_multi_union_pw_aff *extra; + isl_union_map *extra_umap; + isl_ast_graft_list *list; + unsigned n1, n2; + + if (!build || !node || !executed) + goto error; + + if (isl_schedule_node_band_n_member(node) == 0) + return build_ast_from_child(build, node, executed); + + extra = isl_schedule_node_band_get_partial_schedule(node); + extra = isl_multi_union_pw_aff_align_params(extra, + isl_ast_build_get_space(build, 1)); + space = isl_multi_union_pw_aff_get_space(extra); + + extra_umap = isl_union_map_from_multi_union_pw_aff(extra); + extra_umap = isl_union_map_reverse(extra_umap); + + executed = isl_union_map_domain_product(executed, extra_umap); + executed = isl_union_map_detect_equalities(executed); + + n1 = isl_ast_build_dim(build, isl_dim_param); + build = isl_ast_build_product(build, space); + n2 = isl_ast_build_dim(build, isl_dim_param); + if (n2 > n1) + isl_die(isl_ast_build_get_ctx(build), isl_error_invalid, + "band node is not allowed to introduce new parameters", + build = isl_ast_build_free(build)); + build = isl_ast_build_set_schedule_node(build, node); + + list = generate_next_level(executed, build); + + list = isl_ast_graft_list_unembed(list, 1); + + return list; +error: + isl_schedule_node_free(node); + isl_union_map_free(executed); + isl_ast_build_free(build); + return NULL; +} + +/* Hoist a list of grafts (in practice containing a single graft) + * from "sub_build" (which includes extra context information) + * to "build". + * + * In particular, project out all additional parameters introduced + * by the context node from the enforced constraints and the guard + * of the single graft. + */ +static __isl_give isl_ast_graft_list *hoist_out_of_context( + __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build, + __isl_keep isl_ast_build *sub_build) +{ + isl_ast_graft *graft; + isl_basic_set *enforced; + isl_set *guard; + unsigned n_param, extra_param; + + if (!build || !sub_build) + return isl_ast_graft_list_free(list); + + n_param = isl_ast_build_dim(build, isl_dim_param); + extra_param = isl_ast_build_dim(sub_build, isl_dim_param); + + if (extra_param == n_param) + return list; + + extra_param -= n_param; + enforced = isl_ast_graft_list_extract_shared_enforced(list, sub_build); + enforced = isl_basic_set_project_out(enforced, isl_dim_param, + n_param, extra_param); + enforced = isl_basic_set_remove_unknown_divs(enforced); + guard = isl_ast_graft_list_extract_hoistable_guard(list, sub_build); + guard = isl_set_remove_divs_involving_dims(guard, isl_dim_param, + n_param, extra_param); + guard = isl_set_project_out(guard, isl_dim_param, n_param, extra_param); + guard = isl_set_compute_divs(guard); + graft = isl_ast_graft_alloc_from_children(list, guard, enforced, + build, sub_build); + list = isl_ast_graft_list_from_ast_graft(graft); + + return list; +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the context node "node" + * and its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * The context node may introduce additional parameters as well as + * constraints on the outer schedule dimenions or original parameters. + * + * We add the extra parameters to a new build and the context + * constraints to both the build and (as a single disjunct) + * to the domain of "executed". Since the context constraints + * are specified in terms of the input schedule, we first need + * to map them to the internal schedule domain. + * + * After constructing the AST from the descendants of "node", + * we combine the list of grafts into a single graft within + * the new build, in order to be able to exploit the additional + * context constraints during this combination. + * + * Additionally, if the current node is the outermost node in + * the schedule tree (apart from the root domain node), we generate + * all pending guards, again to be able to exploit the additional + * context constraints. We currently do not do this for internal + * context nodes since we may still want to hoist conditions + * to outer AST nodes. + * + * If the context node introduced any new parameters, then they + * are removed from the set of enforced constraints and guard + * in hoist_out_of_context. + */ +static __isl_give isl_ast_graft_list *build_ast_from_context( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_set *context; + isl_space *space; + isl_multi_aff *internal2input; + isl_ast_build *sub_build; + isl_ast_graft_list *list; + int n, depth; + + depth = isl_schedule_node_get_tree_depth(node); + space = isl_ast_build_get_space(build, 1); + context = isl_schedule_node_context_get_context(node); + context = isl_set_align_params(context, space); + sub_build = isl_ast_build_copy(build); + space = isl_set_get_space(context); + sub_build = isl_ast_build_align_params(sub_build, space); + internal2input = isl_ast_build_get_internal2input(sub_build); + context = isl_set_preimage_multi_aff(context, internal2input); + sub_build = isl_ast_build_restrict_generated(sub_build, + isl_set_copy(context)); + context = isl_set_from_basic_set(isl_set_simple_hull(context)); + executed = isl_union_map_intersect_domain(executed, + isl_union_set_from_set(context)); + + list = build_ast_from_child(isl_ast_build_copy(sub_build), + node, executed); + n = isl_ast_graft_list_n_ast_graft(list); + if (n < 0) + list = isl_ast_graft_list_free(list); + + list = isl_ast_graft_list_fuse(list, sub_build); + if (depth == 1) + list = isl_ast_graft_list_insert_pending_guard_nodes(list, + sub_build); + if (n >= 1) + list = hoist_out_of_context(list, build, sub_build); + + isl_ast_build_free(build); + isl_ast_build_free(sub_build); + + return list; +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the expansion node "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * We expand the domain elements by the expansion and + * continue with the descendants of the node. + */ +static __isl_give isl_ast_graft_list *build_ast_from_expansion( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_union_map *expansion; + unsigned n1, n2; + + expansion = isl_schedule_node_expansion_get_expansion(node); + expansion = isl_union_map_align_params(expansion, + isl_union_map_get_space(executed)); + + n1 = isl_union_map_dim(executed, isl_dim_param); + executed = isl_union_map_apply_range(executed, expansion); + n2 = isl_union_map_dim(executed, isl_dim_param); + if (n2 > n1) + isl_die(isl_ast_build_get_ctx(build), isl_error_invalid, + "expansion node is not allowed to introduce " + "new parameters", goto error); + + return build_ast_from_child(build, node, executed); +error: + isl_ast_build_free(build); + isl_schedule_node_free(node); + isl_union_map_free(executed); + return NULL; +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the extension node "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * Extend the inverse schedule with the extension applied to current + * set of generated constraints. Since the extension if formulated + * in terms of the input schedule, it first needs to be transformed + * to refer to the internal schedule. + */ +static __isl_give isl_ast_graft_list *build_ast_from_extension( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_union_set *schedule_domain; + isl_union_map *extension; + isl_set *set; + + set = isl_ast_build_get_generated(build); + set = isl_set_from_basic_set(isl_set_simple_hull(set)); + schedule_domain = isl_union_set_from_set(set); + + extension = isl_schedule_node_extension_get_extension(node); + + extension = isl_union_map_preimage_domain_multi_aff(extension, + isl_multi_aff_copy(build->internal2input)); + extension = isl_union_map_intersect_domain(extension, schedule_domain); + extension = isl_ast_build_substitute_values_union_map_domain(build, + extension); + executed = isl_union_map_union(executed, extension); + + return build_ast_from_child(build, node, executed); +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the filter node "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * We simply intersect the iteration domain (i.e., the range of "executed") + * with the filter and continue with the descendants of the node, + * unless the resulting inverse schedule is empty, in which + * case we return an empty list. + * + * If the result of the intersection is equal to the original "executed" + * relation, then keep the original representation since the intersection + * may have unnecessarily broken up the relation into a greater number + * of disjuncts. + */ +static __isl_give isl_ast_graft_list *build_ast_from_filter( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_ctx *ctx; + isl_union_set *filter; + isl_union_map *orig; + isl_ast_graft_list *list; + int empty; + isl_bool unchanged; + unsigned n1, n2; + + orig = isl_union_map_copy(executed); + if (!build || !node || !executed) + goto error; + + filter = isl_schedule_node_filter_get_filter(node); + filter = isl_union_set_align_params(filter, + isl_union_map_get_space(executed)); + n1 = isl_union_map_dim(executed, isl_dim_param); + executed = isl_union_map_intersect_range(executed, filter); + n2 = isl_union_map_dim(executed, isl_dim_param); + if (n2 > n1) + isl_die(isl_ast_build_get_ctx(build), isl_error_invalid, + "filter node is not allowed to introduce " + "new parameters", goto error); + + unchanged = isl_union_map_is_subset(orig, executed); + empty = isl_union_map_is_empty(executed); + if (unchanged < 0 || empty < 0) + goto error; + if (unchanged) { + isl_union_map_free(executed); + return build_ast_from_child(build, node, orig); + } + isl_union_map_free(orig); + if (!empty) + return build_ast_from_child(build, node, executed); + + ctx = isl_ast_build_get_ctx(build); + list = isl_ast_graft_list_alloc(ctx, 0); + isl_ast_build_free(build); + isl_schedule_node_free(node); + isl_union_map_free(executed); + return list; +error: + isl_ast_build_free(build); + isl_schedule_node_free(node); + isl_union_map_free(executed); + isl_union_map_free(orig); + return NULL; +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the guard node "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * Ensure that the associated guard is enforced by the outer AST + * constructs by adding it to the guard of the graft. + * Since we know that we will enforce the guard, we can also include it + * in the generated constraints used to construct an AST for + * the descendant nodes. + */ +static __isl_give isl_ast_graft_list *build_ast_from_guard( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_space *space; + isl_set *guard, *hoisted; + isl_basic_set *enforced; + isl_ast_build *sub_build; + isl_ast_graft *graft; + isl_ast_graft_list *list; + unsigned n1, n2; + + space = isl_ast_build_get_space(build, 1); + guard = isl_schedule_node_guard_get_guard(node); + n1 = isl_space_dim(space, isl_dim_param); + guard = isl_set_align_params(guard, space); + n2 = isl_set_dim(guard, isl_dim_param); + if (n2 > n1) + isl_die(isl_ast_build_get_ctx(build), isl_error_invalid, + "guard node is not allowed to introduce " + "new parameters", guard = isl_set_free(guard)); + guard = isl_set_preimage_multi_aff(guard, + isl_multi_aff_copy(build->internal2input)); + guard = isl_ast_build_specialize(build, guard); + guard = isl_set_gist(guard, isl_set_copy(build->generated)); + + sub_build = isl_ast_build_copy(build); + sub_build = isl_ast_build_restrict_generated(sub_build, + isl_set_copy(guard)); + + list = build_ast_from_child(isl_ast_build_copy(sub_build), + node, executed); + + hoisted = isl_ast_graft_list_extract_hoistable_guard(list, sub_build); + if (isl_set_n_basic_set(hoisted) > 1) + list = isl_ast_graft_list_gist_guards(list, + isl_set_copy(hoisted)); + guard = isl_set_intersect(guard, hoisted); + enforced = extract_shared_enforced(list, build); + graft = isl_ast_graft_alloc_from_children(list, guard, enforced, + build, sub_build); + + isl_ast_build_free(sub_build); + isl_ast_build_free(build); + return isl_ast_graft_list_from_ast_graft(graft); +} + +/* Call the before_each_mark callback, if requested by the user. + * + * Return 0 on success and -1 on error. + * + * The caller is responsible for recording the current inverse schedule + * in "build". + */ +static isl_stat before_each_mark(__isl_keep isl_id *mark, + __isl_keep isl_ast_build *build) +{ + if (!build) + return isl_stat_error; + if (!build->before_each_mark) + return isl_stat_ok; + return build->before_each_mark(mark, build, + build->before_each_mark_user); +} + +/* Call the after_each_mark callback, if requested by the user. + * + * The caller is responsible for recording the current inverse schedule + * in "build". + */ +static __isl_give isl_ast_graft *after_each_mark( + __isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build) +{ + if (!graft || !build) + return isl_ast_graft_free(graft); + if (!build->after_each_mark) + return graft; + graft->node = build->after_each_mark(graft->node, build, + build->after_each_mark_user); + if (!graft->node) + return isl_ast_graft_free(graft); + return graft; +} + + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the mark node "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + + * Since we may be calling before_each_mark and after_each_mark + * callbacks, we record the current inverse schedule in the build. + * + * We generate an AST for the child of the mark node, combine + * the graft list into a single graft and then insert the mark + * in the AST of that single graft. + */ +static __isl_give isl_ast_graft_list *build_ast_from_mark( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + isl_id *mark; + isl_ast_graft *graft; + isl_ast_graft_list *list; + int n; + + build = isl_ast_build_set_executed(build, isl_union_map_copy(executed)); + + mark = isl_schedule_node_mark_get_id(node); + if (before_each_mark(mark, build) < 0) + node = isl_schedule_node_free(node); + + list = build_ast_from_child(isl_ast_build_copy(build), node, executed); + list = isl_ast_graft_list_fuse(list, build); + n = isl_ast_graft_list_n_ast_graft(list); + if (n < 0) + list = isl_ast_graft_list_free(list); + if (n == 0) { + isl_id_free(mark); + } else { + graft = isl_ast_graft_list_get_ast_graft(list, 0); + graft = isl_ast_graft_insert_mark(graft, mark); + graft = after_each_mark(graft, build); + list = isl_ast_graft_list_set_ast_graft(list, 0, graft); + } + isl_ast_build_free(build); + + return list; +} + +static __isl_give isl_ast_graft_list *build_ast_from_schedule_node( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed); + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the sequence (or set) node "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * We simply generate an AST for each of the children and concatenate + * the results. + */ +static __isl_give isl_ast_graft_list *build_ast_from_sequence( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + int i, n; + isl_ctx *ctx; + isl_ast_graft_list *list; + + ctx = isl_ast_build_get_ctx(build); + list = isl_ast_graft_list_alloc(ctx, 0); + + n = isl_schedule_node_n_children(node); + for (i = 0; i < n; ++i) { + isl_schedule_node *child; + isl_ast_graft_list *list_i; + + child = isl_schedule_node_get_child(node, i); + list_i = build_ast_from_schedule_node(isl_ast_build_copy(build), + child, isl_union_map_copy(executed)); + list = isl_ast_graft_list_concat(list, list_i); + } + isl_ast_build_free(build); + isl_schedule_node_free(node); + isl_union_map_free(executed); + + return list; +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the node "node" and its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * If the node is a leaf, then we pass control to generate_inner_level. + * Note that the current build does not refer to any band node, so + * that generate_inner_level will not try to visit the child of + * the leaf node. + * + * The other node types are handled in separate functions. + * Set nodes are currently treated in the same way as sequence nodes. + * The children of a set node may be executed in any order, + * including the order of the children. + */ +static __isl_give isl_ast_graft_list *build_ast_from_schedule_node( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + enum isl_schedule_node_type type; + + type = isl_schedule_node_get_type(node); + + switch (type) { + case isl_schedule_node_error: + goto error; + case isl_schedule_node_leaf: + isl_schedule_node_free(node); + return generate_inner_level(executed, build); + case isl_schedule_node_band: + return build_ast_from_band(build, node, executed); + case isl_schedule_node_context: + return build_ast_from_context(build, node, executed); + case isl_schedule_node_domain: + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "unexpected internal domain node", goto error); + case isl_schedule_node_expansion: + return build_ast_from_expansion(build, node, executed); + case isl_schedule_node_extension: + return build_ast_from_extension(build, node, executed); + case isl_schedule_node_filter: + return build_ast_from_filter(build, node, executed); + case isl_schedule_node_guard: + return build_ast_from_guard(build, node, executed); + case isl_schedule_node_mark: + return build_ast_from_mark(build, node, executed); + case isl_schedule_node_sequence: + case isl_schedule_node_set: + return build_ast_from_sequence(build, node, executed); + } + + isl_die(isl_ast_build_get_ctx(build), isl_error_internal, + "unhandled type", goto error); +error: + isl_union_map_free(executed); + isl_schedule_node_free(node); + isl_ast_build_free(build); + + return NULL; +} + +/* Generate an AST that visits the elements in the domain of "executed" + * in the relative order specified by the (single) child of "node" and + * its descendants. + * + * The relation "executed" maps the outer generated loop iterators + * to the domain elements executed by those iterations. + * + * This function is never called on a leaf, set or sequence node, + * so the node always has exactly one child. + */ +static __isl_give isl_ast_graft_list *build_ast_from_child( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node, + __isl_take isl_union_map *executed) +{ + node = isl_schedule_node_child(node, 0); + return build_ast_from_schedule_node(build, node, executed); +} + +/* Generate an AST that visits the elements in the domain of the domain + * node "node" in the relative order specified by its descendants. + * + * An initial inverse schedule is created that maps a zero-dimensional + * schedule space to the node domain. + * The input "build" is assumed to have a parametric domain and + * is replaced by the same zero-dimensional schedule space. + * + * We also add some of the parameter constraints in the build domain + * to the executed relation. Adding these constraints + * allows for an earlier detection of conflicts in some cases. + * However, we do not want to divide the executed relation into + * more disjuncts than necessary. We therefore approximate + * the constraints on the parameters by a single disjunct set. + */ +static __isl_give isl_ast_node *build_ast_from_domain( + __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node) +{ + isl_ctx *ctx; + isl_union_set *domain, *schedule_domain; + isl_union_map *executed; + isl_space *space; + isl_set *set; + isl_ast_graft_list *list; + isl_ast_node *ast; + int is_params; + + if (!build) + goto error; + + ctx = isl_ast_build_get_ctx(build); + space = isl_ast_build_get_space(build, 1); + is_params = isl_space_is_params(space); + isl_space_free(space); + if (is_params < 0) + goto error; + if (!is_params) + isl_die(ctx, isl_error_unsupported, + "expecting parametric initial context", goto error); + + domain = isl_schedule_node_domain_get_domain(node); + domain = isl_union_set_coalesce(domain); + + space = isl_union_set_get_space(domain); + space = isl_space_set_from_params(space); + build = isl_ast_build_product(build, space); + + set = isl_ast_build_get_domain(build); + set = isl_set_from_basic_set(isl_set_simple_hull(set)); + schedule_domain = isl_union_set_from_set(set); + + executed = isl_union_map_from_domain_and_range(schedule_domain, domain); + list = build_ast_from_child(isl_ast_build_copy(build), node, executed); + ast = isl_ast_node_from_graft_list(list, build); + isl_ast_build_free(build); + + return ast; +error: + isl_schedule_node_free(node); + isl_ast_build_free(build); + return NULL; +} + +/* Generate an AST that visits the elements in the domain of "schedule" + * in the relative order specified by the schedule tree. + * + * "build" is an isl_ast_build that has been created using + * isl_ast_build_alloc or isl_ast_build_from_context based + * on a parametric set. + * + * The construction starts at the root node of the schedule, + * which is assumed to be a domain node. + */ +__isl_give isl_ast_node *isl_ast_build_node_from_schedule( + __isl_keep isl_ast_build *build, __isl_take isl_schedule *schedule) +{ + isl_ctx *ctx; + isl_schedule_node *node; + + if (!build || !schedule) + goto error; + + ctx = isl_ast_build_get_ctx(build); + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + + build = isl_ast_build_copy(build); + build = isl_ast_build_set_single_valued(build, 0); + if (isl_schedule_node_get_type(node) != isl_schedule_node_domain) + isl_die(ctx, isl_error_unsupported, + "expecting root domain node", + build = isl_ast_build_free(build)); + return build_ast_from_domain(build, node); +error: + isl_schedule_free(schedule); + return NULL; +} Index: lib/Analysis/isl/isl_ast_graft.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_graft.c @@ -0,0 +1,1296 @@ +/* + * Copyright 2012 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include + +static __isl_give isl_ast_graft *isl_ast_graft_copy( + __isl_keep isl_ast_graft *graft); + +#undef BASE +#define BASE ast_graft + +#include + +#undef BASE +#define BASE ast_graft +#include + +isl_ctx *isl_ast_graft_get_ctx(__isl_keep isl_ast_graft *graft) +{ + if (!graft) + return NULL; + return isl_basic_set_get_ctx(graft->enforced); +} + +__isl_give isl_ast_node *isl_ast_graft_get_node( + __isl_keep isl_ast_graft *graft) +{ + return graft ? isl_ast_node_copy(graft->node) : NULL; +} + +/* Create a graft for "node" with no guards and no enforced conditions. + */ +__isl_give isl_ast_graft *isl_ast_graft_alloc( + __isl_take isl_ast_node *node, __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_space *space; + isl_ast_graft *graft; + + if (!node) + return NULL; + + ctx = isl_ast_node_get_ctx(node); + graft = isl_calloc_type(ctx, isl_ast_graft); + if (!graft) + goto error; + + space = isl_ast_build_get_space(build, 1); + + graft->ref = 1; + graft->node = node; + graft->guard = isl_set_universe(isl_space_copy(space)); + graft->enforced = isl_basic_set_universe(space); + + if (!graft->guard || !graft->enforced) + return isl_ast_graft_free(graft); + + return graft; +error: + isl_ast_node_free(node); + return NULL; +} + +/* Create a graft with no guards and no enforced conditions + * encapsulating a call to the domain element specified by "executed". + * "executed" is assumed to be single-valued. + */ +__isl_give isl_ast_graft *isl_ast_graft_alloc_domain( + __isl_take isl_map *executed, __isl_keep isl_ast_build *build) +{ + isl_ast_node *node; + + node = isl_ast_build_call_from_executed(build, executed); + + return isl_ast_graft_alloc(node, build); +} + +static __isl_give isl_ast_graft *isl_ast_graft_copy( + __isl_keep isl_ast_graft *graft) +{ + if (!graft) + return NULL; + + graft->ref++; + return graft; +} + +/* Do all the grafts in "list" have the same guard and is this guard + * independent of the current depth? + */ +static int equal_independent_guards(__isl_keep isl_ast_graft_list *list, + __isl_keep isl_ast_build *build) +{ + int i, n; + int depth; + isl_ast_graft *graft_0; + int equal = 1; + int skip; + + graft_0 = isl_ast_graft_list_get_ast_graft(list, 0); + if (!graft_0) + return -1; + + depth = isl_ast_build_get_depth(build); + if (isl_set_dim(graft_0->guard, isl_dim_set) <= depth) + skip = 0; + else + skip = isl_set_involves_dims(graft_0->guard, + isl_dim_set, depth, 1); + if (skip < 0 || skip) { + isl_ast_graft_free(graft_0); + return skip < 0 ? -1 : 0; + } + + n = isl_ast_graft_list_n_ast_graft(list); + for (i = 1; i < n; ++i) { + isl_ast_graft *graft; + graft = isl_ast_graft_list_get_ast_graft(list, i); + if (!graft) + equal = -1; + else + equal = isl_set_is_equal(graft_0->guard, graft->guard); + isl_ast_graft_free(graft); + if (equal < 0 || !equal) + break; + } + + isl_ast_graft_free(graft_0); + + return equal; +} + +/* Hoist "guard" out of the current level (given by "build"). + * + * In particular, eliminate the dimension corresponding to the current depth. + */ +static __isl_give isl_set *hoist_guard(__isl_take isl_set *guard, + __isl_keep isl_ast_build *build) +{ + int depth; + + depth = isl_ast_build_get_depth(build); + if (depth < isl_set_dim(guard, isl_dim_set)) { + guard = isl_set_remove_divs_involving_dims(guard, + isl_dim_set, depth, 1); + guard = isl_set_eliminate(guard, isl_dim_set, depth, 1); + guard = isl_set_compute_divs(guard); + } + + return guard; +} + +/* Extract a common guard from the grafts in "list" that can be hoisted + * out of the current level. If no such guard can be found, then return + * a universal set. + * + * If all the grafts in the list have the same guard and if this guard + * is independent of the current level, then it can be hoisted out. + * If there is only one graft in the list and if its guard + * depends on the current level, then we eliminate this level and + * return the result. + * + * Otherwise, we return the unshifted simple hull of the guards. + * In order to be able to hoist as many constraints as possible, + * but at the same time avoid hoisting constraints that did not + * appear in the guards in the first place, we intersect the guards + * with all the information that is available (i.e., the domain + * from the build and the enforced constraints of the graft) and + * compute the unshifted hull of the result using only constraints + * from the original guards. + * In particular, intersecting the guards with other known information + * allows us to hoist guards that are only explicit is some of + * the grafts and implicit in the others. + * + * The special case for equal guards is needed in case those guards + * are non-convex. Taking the simple hull would remove information + * and would not allow for these guards to be hoisted completely. + */ +__isl_give isl_set *isl_ast_graft_list_extract_hoistable_guard( + __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build) +{ + int i, n; + int equal; + isl_ctx *ctx; + isl_set *guard; + isl_set_list *set_list; + isl_basic_set *hull; + + if (!list || !build) + return NULL; + + n = isl_ast_graft_list_n_ast_graft(list); + if (n == 0) + return isl_set_universe(isl_ast_build_get_space(build, 1)); + + equal = equal_independent_guards(list, build); + if (equal < 0) + return NULL; + + if (equal || n == 1) { + isl_ast_graft *graft_0; + + graft_0 = isl_ast_graft_list_get_ast_graft(list, 0); + if (!graft_0) + return NULL; + guard = isl_set_copy(graft_0->guard); + if (!equal) + guard = hoist_guard(guard, build); + isl_ast_graft_free(graft_0); + return guard; + } + + ctx = isl_ast_build_get_ctx(build); + set_list = isl_set_list_alloc(ctx, n); + guard = isl_set_empty(isl_ast_build_get_space(build, 1)); + for (i = 0; i < n; ++i) { + isl_ast_graft *graft; + isl_basic_set *enforced; + isl_set *guard_i; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + enforced = isl_ast_graft_get_enforced(graft); + guard_i = isl_set_copy(graft->guard); + isl_ast_graft_free(graft); + set_list = isl_set_list_add(set_list, isl_set_copy(guard_i)); + guard_i = isl_set_intersect(guard_i, + isl_set_from_basic_set(enforced)); + guard_i = isl_set_intersect(guard_i, + isl_ast_build_get_domain(build)); + guard = isl_set_union(guard, guard_i); + } + hull = isl_set_unshifted_simple_hull_from_set_list(guard, set_list); + guard = isl_set_from_basic_set(hull); + return hoist_guard(guard, build); +} + +/* Internal data structure used inside insert_if. + * + * list is the list of guarded nodes created by each call to insert_if. + * node is the original node that is guarded by insert_if. + * build is the build in which the AST is constructed. + */ +struct isl_insert_if_data { + isl_ast_node_list *list; + isl_ast_node *node; + isl_ast_build *build; +}; + +static isl_stat insert_if(__isl_take isl_basic_set *bset, void *user); + +/* Insert an if node around "node" testing the condition encoded + * in guard "guard". + * + * If the user does not want any disjunctions in the if conditions + * and if "guard" does involve a disjunction, then we make the different + * disjuncts disjoint and insert an if node corresponding to each disjunct + * around a copy of "node". The result is then a block node containing + * this sequence of guarded copies of "node". + */ +static __isl_give isl_ast_node *ast_node_insert_if( + __isl_take isl_ast_node *node, __isl_take isl_set *guard, + __isl_keep isl_ast_build *build) +{ + struct isl_insert_if_data data; + isl_ctx *ctx; + + ctx = isl_ast_build_get_ctx(build); + if (isl_options_get_ast_build_allow_or(ctx) || + isl_set_n_basic_set(guard) <= 1) { + isl_ast_node *if_node; + isl_ast_expr *expr; + + expr = isl_ast_build_expr_from_set_internal(build, guard); + + if_node = isl_ast_node_alloc_if(expr); + return isl_ast_node_if_set_then(if_node, node); + } + + guard = isl_set_make_disjoint(guard); + + data.list = isl_ast_node_list_alloc(ctx, 0); + data.node = node; + data.build = build; + if (isl_set_foreach_basic_set(guard, &insert_if, &data) < 0) + data.list = isl_ast_node_list_free(data.list); + + isl_set_free(guard); + isl_ast_node_free(data.node); + return isl_ast_node_alloc_block(data.list); +} + +/* Insert an if node around a copy of "data->node" testing the condition + * encoded in guard "bset" and add the result to data->list. + */ +static isl_stat insert_if(__isl_take isl_basic_set *bset, void *user) +{ + struct isl_insert_if_data *data = user; + isl_ast_node *node; + isl_set *set; + + set = isl_set_from_basic_set(bset); + node = isl_ast_node_copy(data->node); + node = ast_node_insert_if(node, set, data->build); + data->list = isl_ast_node_list_add(data->list, node); + + return isl_stat_ok; +} + +/* Insert an if node around graft->node testing the condition encoded + * in guard "guard", assuming guard involves any conditions. + */ +static __isl_give isl_ast_graft *insert_if_node( + __isl_take isl_ast_graft *graft, __isl_take isl_set *guard, + __isl_keep isl_ast_build *build) +{ + int univ; + + if (!graft) + goto error; + + univ = isl_set_plain_is_universe(guard); + if (univ < 0) + goto error; + if (univ) { + isl_set_free(guard); + return graft; + } + + build = isl_ast_build_copy(build); + graft->node = ast_node_insert_if(graft->node, guard, build); + isl_ast_build_free(build); + + if (!graft->node) + return isl_ast_graft_free(graft); + + return graft; +error: + isl_set_free(guard); + return isl_ast_graft_free(graft); +} + +/* Insert an if node around graft->node testing the condition encoded + * in graft->guard, assuming graft->guard involves any conditions. + */ +static __isl_give isl_ast_graft *insert_pending_guard_node( + __isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build) +{ + if (!graft) + return NULL; + + return insert_if_node(graft, isl_set_copy(graft->guard), build); +} + +/* Replace graft->enforced by "enforced". + */ +__isl_give isl_ast_graft *isl_ast_graft_set_enforced( + __isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced) +{ + if (!graft || !enforced) + goto error; + + isl_basic_set_free(graft->enforced); + graft->enforced = enforced; + + return graft; +error: + isl_basic_set_free(enforced); + return isl_ast_graft_free(graft); +} + +/* Update "enforced" such that it only involves constraints that are + * also enforced by "graft". + */ +static __isl_give isl_basic_set *update_enforced( + __isl_take isl_basic_set *enforced, __isl_keep isl_ast_graft *graft, + int depth) +{ + isl_basic_set *enforced_g; + + enforced_g = isl_ast_graft_get_enforced(graft); + if (depth < isl_basic_set_dim(enforced_g, isl_dim_set)) + enforced_g = isl_basic_set_eliminate(enforced_g, + isl_dim_set, depth, 1); + enforced_g = isl_basic_set_remove_unknown_divs(enforced_g); + enforced_g = isl_basic_set_align_params(enforced_g, + isl_basic_set_get_space(enforced)); + enforced = isl_basic_set_align_params(enforced, + isl_basic_set_get_space(enforced_g)); + enforced = isl_set_simple_hull(isl_basic_set_union(enforced, + enforced_g)); + + return enforced; +} + +/* Extend the node at *body with node. + * + * If body points to the else branch, then *body may still be NULL. + * If so, we simply attach node to this else branch. + * Otherwise, we attach a list containing the statements already + * attached at *body followed by node. + */ +static void extend_body(__isl_keep isl_ast_node **body, + __isl_take isl_ast_node *node) +{ + isl_ast_node_list *list; + + if (!*body) { + *body = node; + return; + } + + if ((*body)->type == isl_ast_node_block) { + list = isl_ast_node_block_get_children(*body); + isl_ast_node_free(*body); + } else + list = isl_ast_node_list_from_ast_node(*body); + list = isl_ast_node_list_add(list, node); + *body = isl_ast_node_alloc_block(list); +} + +/* Merge "graft" into the last graft of "list". + * body points to the then or else branch of an if node in that last graft. + * + * We attach graft->node to this branch and update the enforced + * set of the last graft of "list" to take into account the enforced + * set of "graft". + */ +static __isl_give isl_ast_graft_list *graft_extend_body( + __isl_take isl_ast_graft_list *list, + __isl_keep isl_ast_node **body, __isl_take isl_ast_graft *graft, + __isl_keep isl_ast_build *build) +{ + int n; + int depth; + isl_ast_graft *last; + isl_space *space; + isl_basic_set *enforced; + + if (!list || !graft) + goto error; + extend_body(body, isl_ast_node_copy(graft->node)); + if (!*body) + goto error; + + n = isl_ast_graft_list_n_ast_graft(list); + last = isl_ast_graft_list_get_ast_graft(list, n - 1); + + depth = isl_ast_build_get_depth(build); + space = isl_ast_build_get_space(build, 1); + enforced = isl_basic_set_empty(space); + enforced = update_enforced(enforced, last, depth); + enforced = update_enforced(enforced, graft, depth); + last = isl_ast_graft_set_enforced(last, enforced); + + list = isl_ast_graft_list_set_ast_graft(list, n - 1, last); + isl_ast_graft_free(graft); + return list; +error: + isl_ast_graft_free(graft); + return isl_ast_graft_list_free(list); +} + +/* Merge "graft" into the last graft of "list", attaching graft->node + * to the then branch of "last_if". + */ +static __isl_give isl_ast_graft_list *extend_then( + __isl_take isl_ast_graft_list *list, + __isl_keep isl_ast_node *last_if, __isl_take isl_ast_graft *graft, + __isl_keep isl_ast_build *build) +{ + return graft_extend_body(list, &last_if->u.i.then, graft, build); +} + +/* Merge "graft" into the last graft of "list", attaching graft->node + * to the else branch of "last_if". + */ +static __isl_give isl_ast_graft_list *extend_else( + __isl_take isl_ast_graft_list *list, + __isl_keep isl_ast_node *last_if, __isl_take isl_ast_graft *graft, + __isl_keep isl_ast_build *build) +{ + return graft_extend_body(list, &last_if->u.i.else_node, graft, build); +} + +/* This data structure keeps track of an if node. + * + * "node" is the actual if-node + * "guard" is the original, non-simplified guard of the node + * "complement" is the complement of "guard" in the context of outer if nodes + */ +struct isl_if_node { + isl_ast_node *node; + isl_set *guard; + isl_set *complement; +}; + +/* Given a list of "n" if nodes, clear those starting at "first" + * and return "first" (i.e., the updated size of the array). + */ +static int clear_if_nodes(struct isl_if_node *if_node, int first, int n) +{ + int i; + + for (i = first; i < n; ++i) { + isl_set_free(if_node[i].guard); + isl_set_free(if_node[i].complement); + } + + return first; +} + +/* For each graft in "list", + * insert an if node around graft->node testing the condition encoded + * in graft->guard, assuming graft->guard involves any conditions. + * + * We keep track of a list of generated if nodes that can be extended + * without changing the order of the elements in "list". + * If the guard of a graft is a subset of either the guard or its complement + * of one of those if nodes, then the node + * of the new graft is inserted into the then or else branch of the last graft + * and the current graft is discarded. + * The guard of the node is then simplified based on the conditions + * enforced at that then or else branch. + * Otherwise, the current graft is appended to the list. + * + * We only construct else branches if allowed by the user. + */ +static __isl_give isl_ast_graft_list *insert_pending_guard_nodes( + __isl_take isl_ast_graft_list *list, + __isl_keep isl_ast_build *build) +{ + int i, j, n, n_if; + int allow_else; + isl_ctx *ctx; + isl_ast_graft_list *res; + struct isl_if_node *if_node = NULL; + + if (!build || !list) + return isl_ast_graft_list_free(list); + + ctx = isl_ast_build_get_ctx(build); + n = isl_ast_graft_list_n_ast_graft(list); + + allow_else = isl_options_get_ast_build_allow_else(ctx); + + n_if = 0; + if (n > 1) { + if_node = isl_alloc_array(ctx, struct isl_if_node, n - 1); + if (!if_node) + return isl_ast_graft_list_free(list); + } + + res = isl_ast_graft_list_alloc(ctx, n); + + for (i = 0; i < n; ++i) { + isl_set *guard; + isl_ast_graft *graft; + int subset, found_then, found_else; + isl_ast_node *node; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + if (!graft) + break; + subset = 0; + found_then = found_else = -1; + if (n_if > 0) { + isl_set *test; + test = isl_set_copy(graft->guard); + test = isl_set_intersect(test, + isl_set_copy(build->domain)); + for (j = n_if - 1; j >= 0; --j) { + subset = isl_set_is_subset(test, + if_node[j].guard); + if (subset < 0 || subset) { + found_then = j; + break; + } + if (!allow_else) + continue; + subset = isl_set_is_subset(test, + if_node[j].complement); + if (subset < 0 || subset) { + found_else = j; + break; + } + } + n_if = clear_if_nodes(if_node, j + 1, n_if); + isl_set_free(test); + } + if (subset < 0) { + graft = isl_ast_graft_free(graft); + break; + } + + guard = isl_set_copy(graft->guard); + if (found_then >= 0) + graft->guard = isl_set_gist(graft->guard, + isl_set_copy(if_node[found_then].guard)); + else if (found_else >= 0) + graft->guard = isl_set_gist(graft->guard, + isl_set_copy(if_node[found_else].complement)); + + node = graft->node; + if (!graft->guard) + graft = isl_ast_graft_free(graft); + graft = insert_pending_guard_node(graft, build); + if (graft && graft->node != node && i != n - 1) { + isl_set *set; + if_node[n_if].node = graft->node; + if_node[n_if].guard = guard; + if (found_then >= 0) + set = if_node[found_then].guard; + else if (found_else >= 0) + set = if_node[found_else].complement; + else + set = build->domain; + set = isl_set_copy(set); + set = isl_set_subtract(set, isl_set_copy(guard)); + if_node[n_if].complement = set; + n_if++; + } else + isl_set_free(guard); + if (!graft) + break; + + if (found_then >= 0) + res = extend_then(res, if_node[found_then].node, + graft, build); + else if (found_else >= 0) + res = extend_else(res, if_node[found_else].node, + graft, build); + else + res = isl_ast_graft_list_add(res, graft); + } + if (i < n) + res = isl_ast_graft_list_free(res); + + isl_ast_graft_list_free(list); + clear_if_nodes(if_node, 0, n_if); + free(if_node); + return res; +} + +/* For each graft in "list", + * insert an if node around graft->node testing the condition encoded + * in graft->guard, assuming graft->guard involves any conditions. + * Subsequently remove the guards from the grafts. + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_insert_pending_guard_nodes( + __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build) +{ + int i, n; + isl_set *universe; + + list = insert_pending_guard_nodes(list, build); + if (!list) + return NULL; + + universe = isl_set_universe(isl_ast_build_get_space(build, 1)); + n = isl_ast_graft_list_n_ast_graft(list); + for (i = 0; i < n; ++i) { + isl_ast_graft *graft; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + if (!graft) + break; + isl_set_free(graft->guard); + graft->guard = isl_set_copy(universe); + if (!graft->guard) + graft = isl_ast_graft_free(graft); + list = isl_ast_graft_list_set_ast_graft(list, i, graft); + } + isl_set_free(universe); + if (i < n) + return isl_ast_graft_list_free(list); + + return list; +} + +/* Collect the nodes contained in the grafts in "list" in a node list. + */ +static __isl_give isl_ast_node_list *extract_node_list( + __isl_keep isl_ast_graft_list *list) +{ + int i, n; + isl_ctx *ctx; + isl_ast_node_list *node_list; + + if (!list) + return NULL; + ctx = isl_ast_graft_list_get_ctx(list); + n = isl_ast_graft_list_n_ast_graft(list); + node_list = isl_ast_node_list_alloc(ctx, n); + for (i = 0; i < n; ++i) { + isl_ast_node *node; + isl_ast_graft *graft; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + node = isl_ast_graft_get_node(graft); + node_list = isl_ast_node_list_add(node_list, node); + isl_ast_graft_free(graft); + } + + return node_list; +} + +/* Look for shared enforced constraints by all the elements in "list" + * on outer loops (with respect to the current depth) and return the result. + * + * If there are no elements in "list", then return the empty set. + */ +__isl_give isl_basic_set *isl_ast_graft_list_extract_shared_enforced( + __isl_keep isl_ast_graft_list *list, + __isl_keep isl_ast_build *build) +{ + int i, n; + int depth; + isl_space *space; + isl_basic_set *enforced; + + if (!list) + return NULL; + + space = isl_ast_build_get_space(build, 1); + enforced = isl_basic_set_empty(space); + + depth = isl_ast_build_get_depth(build); + n = isl_ast_graft_list_n_ast_graft(list); + for (i = 0; i < n; ++i) { + isl_ast_graft *graft; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + enforced = update_enforced(enforced, graft, depth); + isl_ast_graft_free(graft); + } + + return enforced; +} + +/* Record "guard" in "graft" so that it will be enforced somewhere + * up the tree. If the graft already has a guard, then it may be partially + * redundant in combination with the new guard and in the context + * the generated constraints of "build". In fact, the new guard + * may in itself have some redundant constraints. + * We therefore (re)compute the gist of the intersection + * and coalesce the result. + */ +static __isl_give isl_ast_graft *store_guard(__isl_take isl_ast_graft *graft, + __isl_take isl_set *guard, __isl_keep isl_ast_build *build) +{ + int is_universe; + + if (!graft) + goto error; + + is_universe = isl_set_plain_is_universe(guard); + if (is_universe < 0) + goto error; + if (is_universe) { + isl_set_free(guard); + return graft; + } + + graft->guard = isl_set_intersect(graft->guard, guard); + graft->guard = isl_set_gist(graft->guard, + isl_ast_build_get_generated(build)); + graft->guard = isl_set_coalesce(graft->guard); + if (!graft->guard) + return isl_ast_graft_free(graft); + + return graft; +error: + isl_set_free(guard); + return isl_ast_graft_free(graft); +} + +/* For each graft in "list", replace its guard with the gist with + * respect to "context". + */ +static __isl_give isl_ast_graft_list *gist_guards( + __isl_take isl_ast_graft_list *list, __isl_keep isl_set *context) +{ + int i, n; + + if (!list) + return NULL; + + n = isl_ast_graft_list_n_ast_graft(list); + for (i = 0; i < n; ++i) { + isl_ast_graft *graft; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + if (!graft) + break; + graft->guard = isl_set_gist(graft->guard, + isl_set_copy(context)); + if (!graft->guard) + graft = isl_ast_graft_free(graft); + list = isl_ast_graft_list_set_ast_graft(list, i, graft); + } + if (i < n) + return isl_ast_graft_list_free(list); + + return list; +} + +/* For each graft in "list", replace its guard with the gist with + * respect to "context". + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_gist_guards( + __isl_take isl_ast_graft_list *list, __isl_take isl_set *context) +{ + list = gist_guards(list, context); + isl_set_free(context); + + return list; +} + +/* Allocate a graft in "build" based on the list of grafts in "sub_build". + * "guard" and "enforced" are the guard and enforced constraints + * of the allocated graft. The guard is used to simplify the guards + * of the elements in "list". + * + * The node is initialized to either a block containing the nodes of "children" + * or, if there is only a single child, the node of that child. + * If the current level requires a for node, it should be inserted by + * a subsequent call to isl_ast_graft_insert_for. + */ +__isl_give isl_ast_graft *isl_ast_graft_alloc_from_children( + __isl_take isl_ast_graft_list *list, __isl_take isl_set *guard, + __isl_take isl_basic_set *enforced, __isl_keep isl_ast_build *build, + __isl_keep isl_ast_build *sub_build) +{ + isl_ast_build *guard_build; + isl_ast_node *node; + isl_ast_node_list *node_list; + isl_ast_graft *graft; + + guard_build = isl_ast_build_copy(sub_build); + guard_build = isl_ast_build_replace_pending_by_guard(guard_build, + isl_set_copy(guard)); + list = gist_guards(list, guard); + list = insert_pending_guard_nodes(list, guard_build); + isl_ast_build_free(guard_build); + + node_list = extract_node_list(list); + node = isl_ast_node_from_ast_node_list(node_list); + isl_ast_graft_list_free(list); + + graft = isl_ast_graft_alloc(node, build); + graft = store_guard(graft, guard, build); + graft = isl_ast_graft_enforce(graft, enforced); + + return graft; +} + +/* Combine the grafts in the list into a single graft. + * + * The guard is initialized to the shared guard of the list elements (if any), + * provided it does not depend on the current dimension. + * The guards in the elements are then simplified with respect to the + * hoisted guard and materialized as if nodes around the contained AST nodes + * in the context of "sub_build". + * + * The enforced set is initialized to the simple hull of the enforced sets + * of the elements, provided the ast_build_exploit_nested_bounds option is set + * or the new graft will be used at the same level. + * + * The node is initialized to either a block containing the nodes of "list" + * or, if there is only a single element, the node of that element. + */ +static __isl_give isl_ast_graft *ast_graft_list_fuse( + __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build) +{ + isl_ast_graft *graft; + isl_basic_set *enforced; + isl_set *guard; + + if (!list) + return NULL; + + enforced = isl_ast_graft_list_extract_shared_enforced(list, build); + guard = isl_ast_graft_list_extract_hoistable_guard(list, build); + graft = isl_ast_graft_alloc_from_children(list, guard, enforced, + build, build); + + return graft; +} + +/* Combine the grafts in the list into a single graft. + * Return a list containing this single graft. + * If the original list is empty, then return an empty list. + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_fuse( + __isl_take isl_ast_graft_list *list, + __isl_keep isl_ast_build *build) +{ + isl_ast_graft *graft; + + if (!list) + return NULL; + if (isl_ast_graft_list_n_ast_graft(list) <= 1) + return list; + graft = ast_graft_list_fuse(list, build); + return isl_ast_graft_list_from_ast_graft(graft); +} + +/* Combine the two grafts into a single graft. + * Return a list containing this single graft. + */ +static __isl_give isl_ast_graft *isl_ast_graft_fuse( + __isl_take isl_ast_graft *graft1, __isl_take isl_ast_graft *graft2, + __isl_keep isl_ast_build *build) +{ + isl_ctx *ctx; + isl_ast_graft_list *list; + + ctx = isl_ast_build_get_ctx(build); + + list = isl_ast_graft_list_alloc(ctx, 2); + list = isl_ast_graft_list_add(list, graft1); + list = isl_ast_graft_list_add(list, graft2); + + return ast_graft_list_fuse(list, build); +} + +/* Insert a for node enclosing the current graft->node. + */ +__isl_give isl_ast_graft *isl_ast_graft_insert_for( + __isl_take isl_ast_graft *graft, __isl_take isl_ast_node *node) +{ + if (!graft) + goto error; + + graft->node = isl_ast_node_for_set_body(node, graft->node); + if (!graft->node) + return isl_ast_graft_free(graft); + + return graft; +error: + isl_ast_node_free(node); + isl_ast_graft_free(graft); + return NULL; +} + +/* Insert a mark governing the current graft->node. + */ +__isl_give isl_ast_graft *isl_ast_graft_insert_mark( + __isl_take isl_ast_graft *graft, __isl_take isl_id *mark) +{ + if (!graft) + goto error; + + graft->node = isl_ast_node_alloc_mark(mark, graft->node); + if (!graft->node) + return isl_ast_graft_free(graft); + + return graft; +error: + isl_id_free(mark); + isl_ast_graft_free(graft); + return NULL; +} + +/* Represent the graft list as an AST node. + * This operation drops the information about guards in the grafts, so + * if there are any pending guards, then they are materialized as if nodes. + */ +__isl_give isl_ast_node *isl_ast_node_from_graft_list( + __isl_take isl_ast_graft_list *list, + __isl_keep isl_ast_build *build) +{ + isl_ast_node_list *node_list; + + list = insert_pending_guard_nodes(list, build); + node_list = extract_node_list(list); + isl_ast_graft_list_free(list); + + return isl_ast_node_from_ast_node_list(node_list); +} + +void *isl_ast_graft_free(__isl_take isl_ast_graft *graft) +{ + if (!graft) + return NULL; + + if (--graft->ref > 0) + return NULL; + + isl_ast_node_free(graft->node); + isl_set_free(graft->guard); + isl_basic_set_free(graft->enforced); + free(graft); + + return NULL; +} + +/* Record that the grafted tree enforces + * "enforced" by intersecting graft->enforced with "enforced". + */ +__isl_give isl_ast_graft *isl_ast_graft_enforce( + __isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced) +{ + if (!graft || !enforced) + goto error; + + enforced = isl_basic_set_align_params(enforced, + isl_basic_set_get_space(graft->enforced)); + graft->enforced = isl_basic_set_align_params(graft->enforced, + isl_basic_set_get_space(enforced)); + graft->enforced = isl_basic_set_intersect(graft->enforced, enforced); + if (!graft->enforced) + return isl_ast_graft_free(graft); + + return graft; +error: + isl_basic_set_free(enforced); + return isl_ast_graft_free(graft); +} + +__isl_give isl_basic_set *isl_ast_graft_get_enforced( + __isl_keep isl_ast_graft *graft) +{ + return graft ? isl_basic_set_copy(graft->enforced) : NULL; +} + +__isl_give isl_set *isl_ast_graft_get_guard(__isl_keep isl_ast_graft *graft) +{ + return graft ? isl_set_copy(graft->guard) : NULL; +} + +/* Record that "guard" needs to be inserted in "graft". + */ +__isl_give isl_ast_graft *isl_ast_graft_add_guard( + __isl_take isl_ast_graft *graft, + __isl_take isl_set *guard, __isl_keep isl_ast_build *build) +{ + return store_guard(graft, guard, build); +} + +/* Reformulate the "graft", which was generated in the context + * of an inner code generation, in terms of the outer code generation + * AST build. + * + * If "product" is set, then the domain of the inner code generation build is + * + * [O -> S] + * + * with O the domain of the outer code generation build. + * We essentially need to project out S. + * + * If "product" is not set, then we need to project the domains onto + * their parameter spaces. + */ +__isl_give isl_ast_graft *isl_ast_graft_unembed(__isl_take isl_ast_graft *graft, + int product) +{ + isl_basic_set *enforced; + + if (!graft) + return NULL; + + if (product) { + enforced = graft->enforced; + enforced = isl_basic_map_domain(isl_basic_set_unwrap(enforced)); + graft->enforced = enforced; + graft->guard = isl_map_domain(isl_set_unwrap(graft->guard)); + } else { + graft->enforced = isl_basic_set_params(graft->enforced); + graft->guard = isl_set_params(graft->guard); + } + graft->guard = isl_set_compute_divs(graft->guard); + + if (!graft->enforced || !graft->guard) + return isl_ast_graft_free(graft); + + return graft; +} + +/* Reformulate the grafts in "list", which were generated in the context + * of an inner code generation, in terms of the outer code generation + * AST build. + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_unembed( + __isl_take isl_ast_graft_list *list, int product) +{ + int i, n; + + n = isl_ast_graft_list_n_ast_graft(list); + for (i = 0; i < n; ++i) { + isl_ast_graft *graft; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + graft = isl_ast_graft_unembed(graft, product); + list = isl_ast_graft_list_set_ast_graft(list, i, graft); + } + + return list; +} + +/* Compute the preimage of "graft" under the function represented by "ma". + * In other words, plug in "ma" in "enforced" and "guard" fields of "graft". + */ +__isl_give isl_ast_graft *isl_ast_graft_preimage_multi_aff( + __isl_take isl_ast_graft *graft, __isl_take isl_multi_aff *ma) +{ + isl_basic_set *enforced; + + if (!graft) + return NULL; + + enforced = graft->enforced; + graft->enforced = isl_basic_set_preimage_multi_aff(enforced, + isl_multi_aff_copy(ma)); + graft->guard = isl_set_preimage_multi_aff(graft->guard, ma); + + if (!graft->enforced || !graft->guard) + return isl_ast_graft_free(graft); + + return graft; +} + +/* Compute the preimage of all the grafts in "list" under + * the function represented by "ma". + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_preimage_multi_aff( + __isl_take isl_ast_graft_list *list, __isl_take isl_multi_aff *ma) +{ + int i, n; + + n = isl_ast_graft_list_n_ast_graft(list); + for (i = 0; i < n; ++i) { + isl_ast_graft *graft; + + graft = isl_ast_graft_list_get_ast_graft(list, i); + graft = isl_ast_graft_preimage_multi_aff(graft, + isl_multi_aff_copy(ma)); + list = isl_ast_graft_list_set_ast_graft(list, i, graft); + } + + isl_multi_aff_free(ma); + return list; +} + +/* Compare two grafts based on their guards. + */ +static int cmp_graft(__isl_keep isl_ast_graft *a, __isl_keep isl_ast_graft *b, + void *user) +{ + return isl_set_plain_cmp(a->guard, b->guard); +} + +/* Order the elements in "list" based on their guards. + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_sort_guard( + __isl_take isl_ast_graft_list *list) +{ + return isl_ast_graft_list_sort(list, &cmp_graft, NULL); +} + +/* Merge the given two lists into a single list of grafts, + * merging grafts with the same guard into a single graft. + * + * "list2" has been sorted using isl_ast_graft_list_sort. + * "list1" may be the result of a previous call to isl_ast_graft_list_merge + * and may therefore not be completely sorted. + * + * The elements in "list2" need to be executed after those in "list1", + * but if the guard of a graft in "list2" is disjoint from the guards + * of some final elements in "list1", then it can be moved up to before + * those final elements. + * + * In particular, we look at each element g of "list2" in turn + * and move it up beyond elements of "list1" that would be sorted + * after g as long as each of these elements has a guard that is disjoint + * from that of g. + * + * We do not allow the second or any later element of "list2" to be moved + * before a previous elements of "list2" even if the reason that + * that element didn't move up further was that its guard was not disjoint + * from that of the previous element in "list1". + */ +__isl_give isl_ast_graft_list *isl_ast_graft_list_merge( + __isl_take isl_ast_graft_list *list1, + __isl_take isl_ast_graft_list *list2, + __isl_keep isl_ast_build *build) +{ + int i, j, first; + + if (!list1 || !list2 || !build) + goto error; + if (list2->n == 0) { + isl_ast_graft_list_free(list2); + return list1; + } + if (list1->n == 0) { + isl_ast_graft_list_free(list1); + return list2; + } + + first = 0; + for (i = 0; i < list2->n; ++i) { + isl_ast_graft *graft; + graft = isl_ast_graft_list_get_ast_graft(list2, i); + if (!graft) + break; + + for (j = list1->n; j >= 0; --j) { + int cmp, disjoint; + isl_ast_graft *graft_j; + + if (j == first) + cmp = -1; + else + cmp = isl_set_plain_cmp(list1->p[j - 1]->guard, + graft->guard); + if (cmp > 0) { + disjoint = isl_set_is_disjoint(graft->guard, + list1->p[j - 1]->guard); + if (disjoint < 0) { + list1 = isl_ast_graft_list_free(list1); + break; + } + if (!disjoint) + cmp = -1; + } + if (cmp > 0) + continue; + if (cmp < 0) { + list1 = isl_ast_graft_list_insert(list1, j, + graft); + break; + } + + --j; + + graft_j = isl_ast_graft_list_get_ast_graft(list1, j); + graft_j = isl_ast_graft_fuse(graft_j, graft, build); + list1 = isl_ast_graft_list_set_ast_graft(list1, j, + graft_j); + break; + } + + if (j < 0) + isl_die(isl_ast_build_get_ctx(build), + isl_error_internal, + "element failed to get inserted", break); + + first = j + 1; + if (!list1) + break; + } + if (i < list2->n) + list1 = isl_ast_graft_list_free(list1); + isl_ast_graft_list_free(list2); + + return list1; +error: + isl_ast_graft_list_free(list1); + isl_ast_graft_list_free(list2); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_ast_graft(__isl_take isl_printer *p, + __isl_keep isl_ast_graft *graft) +{ + if (!p) + return NULL; + if (!graft) + return isl_printer_free(p); + + p = isl_printer_print_str(p, "("); + p = isl_printer_print_str(p, "guard: "); + p = isl_printer_print_set(p, graft->guard); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "enforced: "); + p = isl_printer_print_basic_set(p, graft->enforced); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_str(p, "node: "); + p = isl_printer_print_ast_node(p, graft->node); + p = isl_printer_print_str(p, ")"); + + return p; +} Index: lib/Analysis/isl/isl_ast_graft_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_graft_private.h @@ -0,0 +1,102 @@ +#ifndef ISL_AST_GRAFT_PRIVATE_H +#define ISL_AST_GRAFT_PRIVATE_H + +#include +#include +#include +#include + +struct isl_ast_graft; +typedef struct isl_ast_graft isl_ast_graft; + +/* Representation of part of an AST ("node") with some additional polyhedral + * information about the tree. + * + * "guard" contains conditions that should still be enforced by + * some ancestor of the current tree. In particular, the already + * generated tree assumes that these conditions hold, but may not + * have enforced them itself. + * The guard should not contain any unknown divs as it will be used + * to generate an if condition. + * + * "enforced" expresses constraints that are already enforced by the for + * nodes in the current tree and that therefore do not need to be enforced + * by any ancestor. + * The constraints only involve outer loop iterators. + */ +struct isl_ast_graft { + int ref; + + isl_ast_node *node; + + isl_set *guard; + isl_basic_set *enforced; +}; + +ISL_DECLARE_LIST(ast_graft) + +#undef EL +#define EL isl_ast_graft + +#include + +isl_ctx *isl_ast_graft_get_ctx(__isl_keep isl_ast_graft *graft); + +__isl_give isl_ast_graft *isl_ast_graft_alloc( + __isl_take isl_ast_node *node, __isl_keep isl_ast_build *build); +__isl_give isl_ast_graft *isl_ast_graft_alloc_from_children( + __isl_take isl_ast_graft_list *list, __isl_take isl_set *guard, + __isl_take isl_basic_set *enforced, __isl_keep isl_ast_build *build, + __isl_keep isl_ast_build *sub_build); +__isl_give isl_ast_graft_list *isl_ast_graft_list_fuse( + __isl_take isl_ast_graft_list *children, + __isl_keep isl_ast_build *build); +__isl_give isl_ast_graft *isl_ast_graft_alloc_domain( + __isl_take isl_map *schedule, __isl_keep isl_ast_build *build); +void *isl_ast_graft_free(__isl_take isl_ast_graft *graft); +__isl_give isl_ast_graft_list *isl_ast_graft_list_sort_guard( + __isl_take isl_ast_graft_list *list); + +__isl_give isl_ast_graft_list *isl_ast_graft_list_merge( + __isl_take isl_ast_graft_list *list1, + __isl_take isl_ast_graft_list *list2, + __isl_keep isl_ast_build *build); + +__isl_give isl_ast_node *isl_ast_graft_get_node( + __isl_keep isl_ast_graft *graft); +__isl_give isl_basic_set *isl_ast_graft_get_enforced( + __isl_keep isl_ast_graft *graft); +__isl_give isl_set *isl_ast_graft_get_guard(__isl_keep isl_ast_graft *graft); + +__isl_give isl_ast_graft *isl_ast_graft_insert_for( + __isl_take isl_ast_graft *graft, __isl_take isl_ast_node *node); +__isl_give isl_ast_graft *isl_ast_graft_add_guard( + __isl_take isl_ast_graft *graft, + __isl_take isl_set *guard, __isl_keep isl_ast_build *build); +__isl_give isl_ast_graft *isl_ast_graft_enforce( + __isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced); + +__isl_give isl_ast_graft *isl_ast_graft_insert_mark( + __isl_take isl_ast_graft *graft, __isl_take isl_id *mark); + +__isl_give isl_ast_graft_list *isl_ast_graft_list_unembed( + __isl_take isl_ast_graft_list *list, int product); +__isl_give isl_ast_graft_list *isl_ast_graft_list_preimage_multi_aff( + __isl_take isl_ast_graft_list *list, __isl_take isl_multi_aff *ma); +__isl_give isl_ast_graft_list *isl_ast_graft_list_insert_pending_guard_nodes( + __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build); + +__isl_give isl_ast_node *isl_ast_node_from_graft_list( + __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build); + +__isl_give isl_basic_set *isl_ast_graft_list_extract_shared_enforced( + __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build); +__isl_give isl_set *isl_ast_graft_list_extract_hoistable_guard( + __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build); +__isl_give isl_ast_graft_list *isl_ast_graft_list_gist_guards( + __isl_take isl_ast_graft_list *list, __isl_take isl_set *context); + +__isl_give isl_printer *isl_printer_print_ast_graft(__isl_take isl_printer *p, + __isl_keep isl_ast_graft *graft); + +#endif Index: lib/Analysis/isl/isl_ast_int.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_int.c @@ -0,0 +1,13 @@ +#include +#include +#include + +int isl_ast_expr_get_int(__isl_keep isl_ast_expr *expr, isl_int *v) +{ + if (!expr) + return -1; + if (expr->type != isl_ast_expr_int) + isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid, + "expression not an int", return -1); + return isl_val_get_num_isl_int(expr->u.v, v); +} Index: lib/Analysis/isl/isl_ast_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ast_private.h @@ -0,0 +1,121 @@ +#ifndef ISL_AST_PRIVATE_H +#define ISL_AST_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +/* An expression is either an integer, an identifier or an operation + * with zero or more arguments. + */ +struct isl_ast_expr { + int ref; + + isl_ctx *ctx; + + enum isl_ast_expr_type type; + + union { + isl_val *v; + isl_id *id; + struct { + enum isl_ast_op_type op; + unsigned n_arg; + isl_ast_expr **args; + } op; + } u; +}; + +#undef EL +#define EL isl_ast_expr + +#include + +__isl_give isl_ast_expr *isl_ast_expr_alloc_int_si(isl_ctx *ctx, int i); +__isl_give isl_ast_expr *isl_ast_expr_alloc_op(isl_ctx *ctx, + enum isl_ast_op_type op, int n_arg); +__isl_give isl_ast_expr *isl_ast_expr_alloc_binary(enum isl_ast_op_type type, + __isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2); + +#undef EL +#define EL isl_ast_node + +#include + +/* A node is either a block, an if, a for, a user node or a mark node. + * "else_node" is NULL if the if node does not have an else branch. + * "cond" and "inc" are NULL for degenerate for nodes. + * In case of a mark node, "mark" is the mark and "node" is the marked node. + */ +struct isl_ast_node { + int ref; + + isl_ctx *ctx; + enum isl_ast_node_type type; + + union { + struct { + isl_ast_node_list *children; + } b; + struct { + isl_ast_expr *guard; + isl_ast_node *then; + isl_ast_node *else_node; + } i; + struct { + unsigned degenerate : 1; + isl_ast_expr *iterator; + isl_ast_expr *init; + isl_ast_expr *cond; + isl_ast_expr *inc; + isl_ast_node *body; + } f; + struct { + isl_ast_expr *expr; + } e; + struct { + isl_id *mark; + isl_ast_node *node; + } m; + } u; + + isl_id *annotation; +}; + +__isl_give isl_ast_node *isl_ast_node_alloc_for(__isl_take isl_id *id); +__isl_give isl_ast_node *isl_ast_node_for_mark_degenerate( + __isl_take isl_ast_node *node); +__isl_give isl_ast_node *isl_ast_node_alloc_if(__isl_take isl_ast_expr *guard); +__isl_give isl_ast_node *isl_ast_node_alloc_block( + __isl_take isl_ast_node_list *list); +__isl_give isl_ast_node *isl_ast_node_alloc_mark(__isl_take isl_id *id, + __isl_take isl_ast_node *node); +__isl_give isl_ast_node *isl_ast_node_from_ast_node_list( + __isl_take isl_ast_node_list *list); +__isl_give isl_ast_node *isl_ast_node_for_set_body( + __isl_take isl_ast_node *node, __isl_take isl_ast_node *body); +__isl_give isl_ast_node *isl_ast_node_if_set_then( + __isl_take isl_ast_node *node, __isl_take isl_ast_node *child); + +struct isl_ast_print_options { + int ref; + isl_ctx *ctx; + + __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user); + void *print_for_user; + __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p, + __isl_take isl_ast_print_options *options, + __isl_keep isl_ast_node *node, void *user); + void *print_user_user; +}; + +__isl_give isl_printer *isl_ast_node_list_print( + __isl_keep isl_ast_node_list *list, __isl_take isl_printer *p, + __isl_keep isl_ast_print_options *options); + +#endif Index: lib/Analysis/isl/isl_band.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_band.c @@ -0,0 +1,727 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include + +#undef BASE +#define BASE band + +#include + +isl_ctx *isl_band_get_ctx(__isl_keep isl_band *band) +{ + return band ? isl_union_pw_multi_aff_get_ctx(band->pma) : NULL; +} + +__isl_give isl_band *isl_band_alloc(isl_ctx *ctx) +{ + isl_band *band; + + band = isl_calloc_type(ctx, isl_band); + if (!band) + return NULL; + + band->ref = 1; + + return band; +} + +/* Create a duplicate of the given band. The duplicate refers + * to the same schedule and parent as the input, but does not + * increment their reference counts. + */ +__isl_give isl_band *isl_band_dup(__isl_keep isl_band *band) +{ + int i; + isl_ctx *ctx; + isl_band *dup; + + if (!band) + return NULL; + + ctx = isl_band_get_ctx(band); + dup = isl_band_alloc(ctx); + if (!dup) + return NULL; + + dup->n = band->n; + dup->coincident = isl_alloc_array(ctx, int, band->n); + if (band->n && !dup->coincident) + goto error; + + for (i = 0; i < band->n; ++i) + dup->coincident[i] = band->coincident[i]; + + dup->pma = isl_union_pw_multi_aff_copy(band->pma); + dup->schedule = band->schedule; + dup->parent = band->parent; + + if (!dup->pma) + goto error; + + return dup; +error: + isl_band_free(dup); + return NULL; +} + +/* We not only increment the reference count of the band, + * but also that of the schedule that contains this band. + * This ensures that the schedule won't disappear while there + * is still a reference to the band outside of the schedule. + * There is no need to increment the reference count of the parent + * band as the parent band is part of the same schedule. + */ +__isl_give isl_band *isl_band_copy(__isl_keep isl_band *band) +{ + if (!band) + return NULL; + + band->ref++; + band->schedule->ref++; + return band; +} + +/* If this is not the last reference to the band (the one from within the + * schedule), then we also need to decrement the reference count of the + * containing schedule as it was incremented in isl_band_copy. + */ +__isl_null isl_band *isl_band_free(__isl_take isl_band *band) +{ + if (!band) + return NULL; + + if (--band->ref > 0) { + isl_schedule_free(band->schedule); + return NULL; + } + + isl_union_pw_multi_aff_free(band->pma); + isl_band_list_free(band->children); + free(band->coincident); + free(band); + + return NULL; +} + +int isl_band_has_children(__isl_keep isl_band *band) +{ + if (!band) + return -1; + + return band->children != NULL; +} + +__isl_give isl_band_list *isl_band_get_children( + __isl_keep isl_band *band) +{ + if (!band) + return NULL; + if (!band->children) + isl_die(isl_band_get_ctx(band), isl_error_invalid, + "band has no children", return NULL); + return isl_band_list_dup(band->children); +} + +int isl_band_n_member(__isl_keep isl_band *band) +{ + return band ? band->n : 0; +} + +/* Is the given scheduling dimension coincident within the band and + * with respect to the coincidence constraints. + */ +int isl_band_member_is_coincident(__isl_keep isl_band *band, int pos) +{ + if (!band) + return -1; + + if (pos < 0 || pos >= band->n) + isl_die(isl_band_get_ctx(band), isl_error_invalid, + "invalid member position", return -1); + + return band->coincident[pos]; +} + +/* Return the schedule that leads up to this band. + */ +__isl_give isl_union_map *isl_band_get_prefix_schedule( + __isl_keep isl_band *band) +{ + isl_union_set *domain; + isl_union_pw_multi_aff *prefix; + isl_band *a; + + if (!band) + return NULL; + + prefix = isl_union_pw_multi_aff_copy(band->pma); + domain = isl_union_pw_multi_aff_domain(prefix); + prefix = isl_union_pw_multi_aff_from_domain(domain); + + for (a = band->parent; a; a = a->parent) { + isl_union_pw_multi_aff *partial; + + partial = isl_union_pw_multi_aff_copy(a->pma); + prefix = isl_union_pw_multi_aff_flat_range_product(partial, + prefix); + } + + return isl_union_map_from_union_pw_multi_aff(prefix); +} + +/* Return the schedule of the band in isolation. + */ +__isl_give isl_union_pw_multi_aff * +isl_band_get_partial_schedule_union_pw_multi_aff(__isl_keep isl_band *band) +{ + return band ? isl_union_pw_multi_aff_copy(band->pma) : NULL; +} + +/* Return the schedule of the band in isolation. + */ +__isl_give isl_union_map *isl_band_get_partial_schedule( + __isl_keep isl_band *band) +{ + isl_union_pw_multi_aff *sched; + + sched = isl_band_get_partial_schedule_union_pw_multi_aff(band); + return isl_union_map_from_union_pw_multi_aff(sched); +} + +__isl_give isl_union_pw_multi_aff * +isl_band_get_suffix_schedule_union_pw_multi_aff(__isl_keep isl_band *band); + +/* Return the schedule for the given band list. + * For each band in the list, the schedule is composed of the partial + * and suffix schedules of that band. + */ +__isl_give isl_union_pw_multi_aff * +isl_band_list_get_suffix_schedule_union_pw_multi_aff( + __isl_keep isl_band_list *list) +{ + isl_ctx *ctx; + int i, n; + isl_space *space; + isl_union_pw_multi_aff *suffix; + + if (!list) + return NULL; + + ctx = isl_band_list_get_ctx(list); + space = isl_space_alloc(ctx, 0, 0, 0); + suffix = isl_union_pw_multi_aff_empty(space); + n = isl_band_list_n_band(list); + for (i = 0; i < n; ++i) { + isl_band *el; + isl_union_pw_multi_aff *partial; + isl_union_pw_multi_aff *suffix_i; + + el = isl_band_list_get_band(list, i); + partial = isl_band_get_partial_schedule_union_pw_multi_aff(el); + suffix_i = isl_band_get_suffix_schedule_union_pw_multi_aff(el); + suffix_i = isl_union_pw_multi_aff_flat_range_product( + partial, suffix_i); + suffix = isl_union_pw_multi_aff_union_add(suffix, suffix_i); + + isl_band_free(el); + } + + return suffix; +} + +/* Return the schedule for the given band list. + * For each band in the list, the schedule is composed of the partial + * and suffix schedules of that band. + */ +__isl_give isl_union_map *isl_band_list_get_suffix_schedule( + __isl_keep isl_band_list *list) +{ + isl_union_pw_multi_aff *suffix; + + suffix = isl_band_list_get_suffix_schedule_union_pw_multi_aff(list); + return isl_union_map_from_union_pw_multi_aff(suffix); +} + +/* Return the schedule for the forest underneath the given band. + */ +__isl_give isl_union_pw_multi_aff * +isl_band_get_suffix_schedule_union_pw_multi_aff(__isl_keep isl_band *band) +{ + isl_union_pw_multi_aff *suffix; + + if (!band) + return NULL; + + if (!isl_band_has_children(band)) { + isl_union_set *domain; + + suffix = isl_union_pw_multi_aff_copy(band->pma); + domain = isl_union_pw_multi_aff_domain(suffix); + suffix = isl_union_pw_multi_aff_from_domain(domain); + } else { + isl_band_list *list; + + list = isl_band_get_children(band); + suffix = + isl_band_list_get_suffix_schedule_union_pw_multi_aff(list); + isl_band_list_free(list); + } + + return suffix; +} + +/* Return the schedule for the forest underneath the given band. + */ +__isl_give isl_union_map *isl_band_get_suffix_schedule( + __isl_keep isl_band *band) +{ + isl_union_pw_multi_aff *suffix; + + suffix = isl_band_get_suffix_schedule_union_pw_multi_aff(band); + return isl_union_map_from_union_pw_multi_aff(suffix); +} + +/* Call "fn" on each band (recursively) in the list + * in depth-first post-order. + */ +int isl_band_list_foreach_band(__isl_keep isl_band_list *list, + int (*fn)(__isl_keep isl_band *band, void *user), void *user) +{ + int i, n; + + if (!list) + return -1; + + n = isl_band_list_n_band(list); + for (i = 0; i < n; ++i) { + isl_band *band; + int r = 0; + + band = isl_band_list_get_band(list, i); + if (isl_band_has_children(band)) { + isl_band_list *children; + + children = isl_band_get_children(band); + r = isl_band_list_foreach_band(children, fn, user); + isl_band_list_free(children); + } + + if (!band) + r = -1; + if (r == 0) + r = fn(band, user); + + isl_band_free(band); + if (r) + return r; + } + + return 0; +} + +/* Internal data used during the construction of the schedule + * for the tile loops. + * + * sizes contains the tile sizes + * scale is set if the tile loops should be scaled + * tiled collects the result for a single statement + * res collects the result for all statements + */ +struct isl_band_tile_data { + isl_multi_val *sizes; + isl_union_pw_multi_aff *res; + isl_pw_multi_aff *tiled; + int scale; +}; + +/* Given part of the schedule of a band, construct the corresponding + * schedule for the tile loops based on the tile sizes in data->sizes + * and add the result to data->tiled. + * + * If data->scale is set, then dimension i of the schedule will be + * of the form + * + * m_i * floor(s_i(x) / m_i) + * + * where s_i(x) refers to the original schedule and m_i is the tile size. + * If data->scale is not set, then dimension i of the schedule will be + * of the form + * + * floor(s_i(x) / m_i) + * + */ +static isl_stat multi_aff_tile(__isl_take isl_set *set, + __isl_take isl_multi_aff *ma, void *user) +{ + struct isl_band_tile_data *data = user; + isl_pw_multi_aff *pma; + int i, n; + isl_val *v; + + n = isl_multi_aff_dim(ma, isl_dim_out); + + for (i = 0; i < n; ++i) { + isl_aff *aff; + + aff = isl_multi_aff_get_aff(ma, i); + v = isl_multi_val_get_val(data->sizes, i); + + aff = isl_aff_scale_down_val(aff, isl_val_copy(v)); + aff = isl_aff_floor(aff); + if (data->scale) + aff = isl_aff_scale_val(aff, isl_val_copy(v)); + isl_val_free(v); + + ma = isl_multi_aff_set_aff(ma, i, aff); + } + + pma = isl_pw_multi_aff_alloc(set, ma); + data->tiled = isl_pw_multi_aff_union_add(data->tiled, pma); + + return isl_stat_ok; +} + +/* Given part of the schedule of a band, construct the corresponding + * schedule for the tile loops based on the tile sizes in data->sizes + * and add the result to data->res. + */ +static isl_stat pw_multi_aff_tile(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_band_tile_data *data = user; + + data->tiled = isl_pw_multi_aff_empty(isl_pw_multi_aff_get_space(pma)); + + if (isl_pw_multi_aff_foreach_piece(pma, &multi_aff_tile, data) < 0) + goto error; + + isl_pw_multi_aff_free(pma); + data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, + data->tiled); + + return isl_stat_ok; +error: + isl_pw_multi_aff_free(pma); + isl_pw_multi_aff_free(data->tiled); + return isl_stat_error; +} + +/* Given the schedule of a band, construct the corresponding + * schedule for the tile loops based on the given tile sizes + * and return the result. + */ +static isl_union_pw_multi_aff *isl_union_pw_multi_aff_tile( + __isl_take isl_union_pw_multi_aff *sched, + __isl_keep isl_multi_val *sizes) +{ + isl_ctx *ctx; + isl_space *space; + struct isl_band_tile_data data = { sizes }; + + ctx = isl_multi_val_get_ctx(sizes); + + space = isl_union_pw_multi_aff_get_space(sched); + data.res = isl_union_pw_multi_aff_empty(space); + data.scale = isl_options_get_tile_scale_tile_loops(ctx); + + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(sched, + &pw_multi_aff_tile, &data) < 0) + goto error; + + isl_union_pw_multi_aff_free(sched); + return data.res; +error: + isl_union_pw_multi_aff_free(sched); + isl_union_pw_multi_aff_free(data.res); + return NULL; +} + +/* Extract the range space from "pma" and store it in *user. + * All entries are expected to have the same range space, so we can + * stop after extracting the range space from the first entry. + */ +static isl_stat extract_range_space(__isl_take isl_pw_multi_aff *pma, + void *user) +{ + isl_space **space = user; + + *space = isl_space_range(isl_pw_multi_aff_get_space(pma)); + isl_pw_multi_aff_free(pma); + + return isl_stat_error; +} + +/* Extract the range space of "band". All entries in band->pma should + * have the same range space. Furthermore, band->pma should have at least + * one entry. + */ +static __isl_give isl_space *band_get_range_space(__isl_keep isl_band *band) +{ + isl_space *space; + + if (!band) + return NULL; + + space = NULL; + isl_union_pw_multi_aff_foreach_pw_multi_aff(band->pma, + &extract_range_space, &space); + + return space; +} + +/* Construct and return an isl_multi_val in the given space, with as entries + * the first elements of "v", padded with ones if the size of "v" is smaller + * than the dimension of "space". + */ +static __isl_give isl_multi_val *multi_val_from_vec(__isl_take isl_space *space, + __isl_take isl_vec *v) +{ + isl_ctx *ctx; + isl_multi_val *mv; + int i, n, size; + + if (!space || !v) + goto error; + + ctx = isl_space_get_ctx(space); + mv = isl_multi_val_zero(space); + n = isl_multi_val_dim(mv, isl_dim_set); + size = isl_vec_size(v); + if (n < size) + size = n; + + for (i = 0; i < size; ++i) { + isl_val *val = isl_vec_get_element_val(v, i); + mv = isl_multi_val_set_val(mv, i, val); + } + for (i = size; i < n; ++i) + mv = isl_multi_val_set_val(mv, i, isl_val_one(ctx)); + + isl_vec_free(v); + return mv; +error: + isl_space_free(space); + isl_vec_free(v); + return NULL; +} + +/* Tile the given band using the specified tile sizes. + * The given band is modified to refer to the tile loops and + * a child band is created to refer to the point loops. + * The children of this point loop band are the children + * of the original band. + * + * If the scale tile loops option is set, then the tile loops + * are scaled by the tile sizes. If the shift point loops option is set, + * then the point loops are shifted to start at zero. + * In particular, these options affect the tile and point loop schedules + * as follows + * + * scale shift original tile point + * + * 0 0 i floor(i/s) i + * 1 0 i s * floor(i/s) i + * 0 1 i floor(i/s) i - s * floor(i/s) + * 1 1 i s * floor(i/s) i - s * floor(i/s) + */ +int isl_band_tile(__isl_keep isl_band *band, __isl_take isl_vec *sizes) +{ + isl_ctx *ctx; + isl_band *child; + isl_band_list *list = NULL; + isl_union_pw_multi_aff *sched = NULL, *child_sched = NULL; + isl_space *space; + isl_multi_val *mv_sizes; + + if (!band || !sizes) + goto error; + + ctx = isl_vec_get_ctx(sizes); + child = isl_band_dup(band); + list = isl_band_list_alloc(ctx, 1); + list = isl_band_list_add(list, child); + if (!list) + goto error; + + space = band_get_range_space(band); + mv_sizes = multi_val_from_vec(space, isl_vec_copy(sizes)); + sched = isl_union_pw_multi_aff_copy(band->pma); + sched = isl_union_pw_multi_aff_tile(sched, mv_sizes); + + child_sched = isl_union_pw_multi_aff_copy(child->pma); + if (isl_options_get_tile_shift_point_loops(ctx)) { + isl_union_pw_multi_aff *scaled; + scaled = isl_union_pw_multi_aff_copy(sched); + if (!isl_options_get_tile_scale_tile_loops(ctx)) + scaled = isl_union_pw_multi_aff_scale_multi_val(scaled, + isl_multi_val_copy(mv_sizes)); + child_sched = isl_union_pw_multi_aff_sub(child_sched, scaled); + } + isl_multi_val_free(mv_sizes); + if (!sched || !child_sched) + goto error; + + child->children = band->children; + band->children = list; + child->parent = band; + isl_union_pw_multi_aff_free(band->pma); + band->pma = sched; + isl_union_pw_multi_aff_free(child->pma); + child->pma = child_sched; + + isl_vec_free(sizes); + return 0; +error: + isl_union_pw_multi_aff_free(sched); + isl_union_pw_multi_aff_free(child_sched); + isl_band_list_free(list); + isl_vec_free(sizes); + return -1; +} + +/* Internal data structure used inside isl_union_pw_multi_aff_drop. + * + * "pos" is the position of the first dimension to drop. + * "n" is the number of dimensions to drop. + * "res" accumulates the result. + */ +struct isl_union_pw_multi_aff_drop_data { + int pos; + int n; + isl_union_pw_multi_aff *res; +}; + +/* Drop the data->n output dimensions starting at data->pos from "pma" + * and add the result to data->res. + */ +static isl_stat pw_multi_aff_drop(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_union_pw_multi_aff_drop_data *data = user; + + pma = isl_pw_multi_aff_drop_dims(pma, isl_dim_out, data->pos, data->n); + + data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Drop the "n" output dimensions starting at "pos" from "sched". + */ +static isl_union_pw_multi_aff *isl_union_pw_multi_aff_drop( + __isl_take isl_union_pw_multi_aff *sched, int pos, int n) +{ + isl_space *space; + struct isl_union_pw_multi_aff_drop_data data = { pos, n }; + + space = isl_union_pw_multi_aff_get_space(sched); + data.res = isl_union_pw_multi_aff_empty(space); + + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(sched, + &pw_multi_aff_drop, &data) < 0) + data.res = isl_union_pw_multi_aff_free(data.res); + + isl_union_pw_multi_aff_free(sched); + return data.res; +} + +/* Drop the "n" dimensions starting at "pos" from "band". + */ +static int isl_band_drop(__isl_keep isl_band *band, int pos, int n) +{ + int i; + isl_union_pw_multi_aff *sched; + + if (!band) + return -1; + if (n == 0) + return 0; + + sched = isl_union_pw_multi_aff_copy(band->pma); + sched = isl_union_pw_multi_aff_drop(sched, pos, n); + if (!sched) + return -1; + + isl_union_pw_multi_aff_free(band->pma); + band->pma = sched; + + for (i = pos + n; i < band->n; ++i) + band->coincident[i - n] = band->coincident[i]; + + band->n -= n; + + return 0; +} + +/* Split the given band into two nested bands, one with the first "pos" + * dimensions of "band" and one with the remaining band->n - pos dimensions. + */ +int isl_band_split(__isl_keep isl_band *band, int pos) +{ + isl_ctx *ctx; + isl_band *child; + isl_band_list *list; + + if (!band) + return -1; + + ctx = isl_band_get_ctx(band); + + if (pos < 0 || pos > band->n) + isl_die(ctx, isl_error_invalid, "position out of bounds", + return -1); + + child = isl_band_dup(band); + if (isl_band_drop(child, 0, pos) < 0) + child = isl_band_free(child); + list = isl_band_list_alloc(ctx, 1); + list = isl_band_list_add(list, child); + if (!list) + return -1; + + if (isl_band_drop(band, pos, band->n - pos) < 0) { + isl_band_list_free(list); + return -1; + } + + child->children = band->children; + band->children = list; + child->parent = band; + + return 0; +} + +__isl_give isl_printer *isl_printer_print_band(__isl_take isl_printer *p, + __isl_keep isl_band *band) +{ + isl_union_map *prefix, *partial, *suffix; + + prefix = isl_band_get_prefix_schedule(band); + partial = isl_band_get_partial_schedule(band); + suffix = isl_band_get_suffix_schedule(band); + + p = isl_printer_print_str(p, "("); + p = isl_printer_print_union_map(p, prefix); + p = isl_printer_print_str(p, ","); + p = isl_printer_print_union_map(p, partial); + p = isl_printer_print_str(p, ","); + p = isl_printer_print_union_map(p, suffix); + p = isl_printer_print_str(p, ")"); + + isl_union_map_free(prefix); + isl_union_map_free(partial); + isl_union_map_free(suffix); + + return p; +} Index: lib/Analysis/isl/isl_band_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_band_private.h @@ -0,0 +1,47 @@ +#ifndef ISL_BAND_PRIVATE_H +#define ISL_BAND_PRIVATE_H + +#include +#include +#include +#include + +/* Information about a band within a schedule. + * + * n is the number of scheduling dimensions within the band. + * coincident is an array of length n, indicating whether a scheduling dimension + * satisfies the coincidence constraints in the sense that + * the corresponding dependence distances are zero. + * pma is the partial schedule corresponding to this band. + * schedule is the schedule that contains this band. + * parent is the parent of this band (or NULL if the band is a root). + * children are the children of this band (or NULL if the band is a leaf). + * + * To avoid circular dependences in the reference counting, + * the schedule and parent pointers are not reference counted. + * isl_band_copy increments the reference count of schedule to ensure + * that outside references to the band keep the schedule alive. + */ +struct isl_band { + int ref; + + int n; + int *coincident; + + isl_union_pw_multi_aff *pma; + isl_schedule *schedule; + isl_band *parent; + isl_band_list *children; +}; + +#undef EL +#define EL isl_band + +#include + +__isl_give isl_band *isl_band_alloc(isl_ctx *ctx); + +__isl_give isl_union_map *isl_band_list_get_suffix_schedule( + __isl_keep isl_band_list *list); + +#endif Index: lib/Analysis/isl/isl_basis_reduction.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_basis_reduction.h @@ -0,0 +1,27 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_BASIS_REDUCTION_H +#define ISL_BASIS_REDUCTION_H + +#include +#include +#include "isl_tab.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_tab *isl_tab_compute_reduced_basis(struct isl_tab *tab); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/isl_bernstein.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_bernstein.h @@ -0,0 +1,4 @@ +#include + +int isl_qpolynomial_bound_on_domain_bernstein(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct isl_bound *bound); Index: lib/Analysis/isl/isl_bernstein.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_bernstein.c @@ -0,0 +1,555 @@ +/* + * Copyright 2006-2007 Universiteit Leiden + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science, + * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands + * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A, + * B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bernstein_data { + enum isl_fold type; + isl_qpolynomial *poly; + int check_tight; + + isl_cell *cell; + + isl_qpolynomial_fold *fold; + isl_qpolynomial_fold *fold_tight; + isl_pw_qpolynomial_fold *pwf; + isl_pw_qpolynomial_fold *pwf_tight; +}; + +static int vertex_is_integral(__isl_keep isl_basic_set *vertex) +{ + unsigned nvar; + unsigned nparam; + int i; + + nvar = isl_basic_set_dim(vertex, isl_dim_set); + nparam = isl_basic_set_dim(vertex, isl_dim_param); + for (i = 0; i < nvar; ++i) { + int r = nvar - 1 - i; + if (!isl_int_is_one(vertex->eq[r][1 + nparam + i]) && + !isl_int_is_negone(vertex->eq[r][1 + nparam + i])) + return 0; + } + + return 1; +} + +static __isl_give isl_qpolynomial *vertex_coordinate( + __isl_keep isl_basic_set *vertex, int i, __isl_take isl_space *dim) +{ + unsigned nvar; + unsigned nparam; + int r; + isl_int denom; + isl_qpolynomial *v; + + nvar = isl_basic_set_dim(vertex, isl_dim_set); + nparam = isl_basic_set_dim(vertex, isl_dim_param); + r = nvar - 1 - i; + + isl_int_init(denom); + isl_int_set(denom, vertex->eq[r][1 + nparam + i]); + isl_assert(vertex->ctx, !isl_int_is_zero(denom), goto error); + + if (isl_int_is_pos(denom)) + isl_seq_neg(vertex->eq[r], vertex->eq[r], + 1 + isl_basic_set_total_dim(vertex)); + else + isl_int_neg(denom, denom); + + v = isl_qpolynomial_from_affine(dim, vertex->eq[r], denom); + isl_int_clear(denom); + + return v; +error: + isl_space_free(dim); + isl_int_clear(denom); + return NULL; +} + +/* Check whether the bound associated to the selection "k" is tight, + * which is the case if we select exactly one vertex and if that vertex + * is integral for all values of the parameters. + */ +static int is_tight(int *k, int n, int d, isl_cell *cell) +{ + int i; + + for (i = 0; i < n; ++i) { + int v; + if (k[i] != d) { + if (k[i]) + return 0; + continue; + } + v = cell->ids[n - 1 - i]; + return vertex_is_integral(cell->vertices->v[v].vertex); + } + + return 0; +} + +static void add_fold(__isl_take isl_qpolynomial *b, __isl_keep isl_set *dom, + int *k, int n, int d, struct bernstein_data *data) +{ + isl_qpolynomial_fold *fold; + + fold = isl_qpolynomial_fold_alloc(data->type, b); + + if (data->check_tight && is_tight(k, n, d, data->cell)) + data->fold_tight = isl_qpolynomial_fold_fold_on_domain(dom, + data->fold_tight, fold); + else + data->fold = isl_qpolynomial_fold_fold_on_domain(dom, + data->fold, fold); +} + +/* Extract the coefficients of the Bernstein base polynomials and store + * them in data->fold and data->fold_tight. + * + * In particular, the coefficient of each monomial + * of multi-degree (k[0], k[1], ..., k[n-1]) is divided by the corresponding + * multinomial coefficient d!/k[0]! k[1]! ... k[n-1]! + * + * c[i] contains the coefficient of the selected powers of the first i+1 vars. + * multinom[i] contains the partial multinomial coefficient. + */ +static void extract_coefficients(isl_qpolynomial *poly, + __isl_keep isl_set *dom, struct bernstein_data *data) +{ + int i; + int d; + int n; + isl_ctx *ctx; + isl_qpolynomial **c = NULL; + int *k = NULL; + int *left = NULL; + isl_vec *multinom = NULL; + + if (!poly) + return; + + ctx = isl_qpolynomial_get_ctx(poly); + n = isl_qpolynomial_dim(poly, isl_dim_in); + d = isl_qpolynomial_degree(poly); + isl_assert(ctx, n >= 2, return); + + c = isl_calloc_array(ctx, isl_qpolynomial *, n); + k = isl_alloc_array(ctx, int, n); + left = isl_alloc_array(ctx, int, n); + multinom = isl_vec_alloc(ctx, n); + if (!c || !k || !left || !multinom) + goto error; + + isl_int_set_si(multinom->el[0], 1); + for (k[0] = d; k[0] >= 0; --k[0]) { + int i = 1; + isl_qpolynomial_free(c[0]); + c[0] = isl_qpolynomial_coeff(poly, isl_dim_in, n - 1, k[0]); + left[0] = d - k[0]; + k[1] = -1; + isl_int_set(multinom->el[1], multinom->el[0]); + while (i > 0) { + if (i == n - 1) { + int j; + isl_space *dim; + isl_qpolynomial *b; + isl_qpolynomial *f; + for (j = 2; j <= left[i - 1]; ++j) + isl_int_divexact_ui(multinom->el[i], + multinom->el[i], j); + b = isl_qpolynomial_coeff(c[i - 1], isl_dim_in, + n - 1 - i, left[i - 1]); + b = isl_qpolynomial_project_domain_on_params(b); + dim = isl_qpolynomial_get_domain_space(b); + f = isl_qpolynomial_rat_cst_on_domain(dim, ctx->one, + multinom->el[i]); + b = isl_qpolynomial_mul(b, f); + k[n - 1] = left[n - 2]; + add_fold(b, dom, k, n, d, data); + --i; + continue; + } + if (k[i] >= left[i - 1]) { + --i; + continue; + } + ++k[i]; + if (k[i]) + isl_int_divexact_ui(multinom->el[i], + multinom->el[i], k[i]); + isl_qpolynomial_free(c[i]); + c[i] = isl_qpolynomial_coeff(c[i - 1], isl_dim_in, + n - 1 - i, k[i]); + left[i] = left[i - 1] - k[i]; + k[i + 1] = -1; + isl_int_set(multinom->el[i + 1], multinom->el[i]); + ++i; + } + isl_int_mul_ui(multinom->el[0], multinom->el[0], k[0]); + } + + for (i = 0; i < n; ++i) + isl_qpolynomial_free(c[i]); + + isl_vec_free(multinom); + free(left); + free(k); + free(c); + return; +error: + isl_vec_free(multinom); + free(left); + free(k); + if (c) + for (i = 0; i < n; ++i) + isl_qpolynomial_free(c[i]); + free(c); + return; +} + +/* Perform bernstein expansion on the parametric vertices that are active + * on "cell". + * + * data->poly has been homogenized in the calling function. + * + * We plug in the barycentric coordinates for the set variables + * + * \vec x = \sum_i \alpha_i v_i(\vec p) + * + * and the constant "1 = \sum_i \alpha_i" for the homogeneous dimension. + * Next, we extract the coefficients of the Bernstein base polynomials. + */ +static int bernstein_coefficients_cell(__isl_take isl_cell *cell, void *user) +{ + int i, j; + struct bernstein_data *data = (struct bernstein_data *)user; + isl_space *dim_param; + isl_space *dim_dst; + isl_qpolynomial *poly = data->poly; + unsigned nvar; + int n_vertices; + isl_qpolynomial **subs; + isl_pw_qpolynomial_fold *pwf; + isl_set *dom; + isl_ctx *ctx; + + if (!poly) + goto error; + + nvar = isl_qpolynomial_dim(poly, isl_dim_in) - 1; + n_vertices = cell->n_vertices; + + ctx = isl_qpolynomial_get_ctx(poly); + if (n_vertices > nvar + 1 && ctx->opt->bernstein_triangulate) + return isl_cell_foreach_simplex(cell, + &bernstein_coefficients_cell, user); + + subs = isl_alloc_array(ctx, isl_qpolynomial *, 1 + nvar); + if (!subs) + goto error; + + dim_param = isl_basic_set_get_space(cell->dom); + dim_dst = isl_qpolynomial_get_domain_space(poly); + dim_dst = isl_space_add_dims(dim_dst, isl_dim_set, n_vertices); + + for (i = 0; i < 1 + nvar; ++i) + subs[i] = isl_qpolynomial_zero_on_domain(isl_space_copy(dim_dst)); + + for (i = 0; i < n_vertices; ++i) { + isl_qpolynomial *c; + c = isl_qpolynomial_var_on_domain(isl_space_copy(dim_dst), isl_dim_set, + 1 + nvar + i); + for (j = 0; j < nvar; ++j) { + int k = cell->ids[i]; + isl_qpolynomial *v; + v = vertex_coordinate(cell->vertices->v[k].vertex, j, + isl_space_copy(dim_param)); + v = isl_qpolynomial_add_dims(v, isl_dim_in, + 1 + nvar + n_vertices); + v = isl_qpolynomial_mul(v, isl_qpolynomial_copy(c)); + subs[1 + j] = isl_qpolynomial_add(subs[1 + j], v); + } + subs[0] = isl_qpolynomial_add(subs[0], c); + } + isl_space_free(dim_dst); + + poly = isl_qpolynomial_copy(poly); + + poly = isl_qpolynomial_add_dims(poly, isl_dim_in, n_vertices); + poly = isl_qpolynomial_substitute(poly, isl_dim_in, 0, 1 + nvar, subs); + poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, 0, 1 + nvar); + + data->cell = cell; + dom = isl_set_from_basic_set(isl_basic_set_copy(cell->dom)); + data->fold = isl_qpolynomial_fold_empty(data->type, isl_space_copy(dim_param)); + data->fold_tight = isl_qpolynomial_fold_empty(data->type, dim_param); + extract_coefficients(poly, dom, data); + + pwf = isl_pw_qpolynomial_fold_alloc(data->type, isl_set_copy(dom), + data->fold); + data->pwf = isl_pw_qpolynomial_fold_fold(data->pwf, pwf); + pwf = isl_pw_qpolynomial_fold_alloc(data->type, dom, data->fold_tight); + data->pwf_tight = isl_pw_qpolynomial_fold_fold(data->pwf_tight, pwf); + + isl_qpolynomial_free(poly); + isl_cell_free(cell); + for (i = 0; i < 1 + nvar; ++i) + isl_qpolynomial_free(subs[i]); + free(subs); + return 0; +error: + isl_cell_free(cell); + return -1; +} + +/* Base case of applying bernstein expansion. + * + * We compute the chamber decomposition of the parametric polytope "bset" + * and then perform bernstein expansion on the parametric vertices + * that are active on each chamber. + */ +static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_base( + __isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct bernstein_data *data, int *tight) +{ + unsigned nvar; + isl_space *dim; + isl_pw_qpolynomial_fold *pwf; + isl_vertices *vertices; + int covers; + + nvar = isl_basic_set_dim(bset, isl_dim_set); + if (nvar == 0) { + isl_set *dom; + isl_qpolynomial_fold *fold; + + fold = isl_qpolynomial_fold_alloc(data->type, poly); + dom = isl_set_from_basic_set(bset); + if (tight) + *tight = 1; + pwf = isl_pw_qpolynomial_fold_alloc(data->type, dom, fold); + return isl_pw_qpolynomial_fold_project_domain_on_params(pwf); + } + + if (isl_qpolynomial_is_zero(poly)) { + isl_set *dom; + isl_qpolynomial_fold *fold; + fold = isl_qpolynomial_fold_alloc(data->type, poly); + dom = isl_set_from_basic_set(bset); + pwf = isl_pw_qpolynomial_fold_alloc(data->type, dom, fold); + if (tight) + *tight = 1; + return isl_pw_qpolynomial_fold_project_domain_on_params(pwf); + } + + dim = isl_basic_set_get_space(bset); + dim = isl_space_params(dim); + dim = isl_space_from_domain(dim); + dim = isl_space_add_dims(dim, isl_dim_set, 1); + data->pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(dim), data->type); + data->pwf_tight = isl_pw_qpolynomial_fold_zero(dim, data->type); + data->poly = isl_qpolynomial_homogenize(isl_qpolynomial_copy(poly)); + vertices = isl_basic_set_compute_vertices(bset); + isl_vertices_foreach_disjoint_cell(vertices, + &bernstein_coefficients_cell, data); + isl_vertices_free(vertices); + isl_qpolynomial_free(data->poly); + + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + + covers = isl_pw_qpolynomial_fold_covers(data->pwf_tight, data->pwf); + if (covers < 0) + goto error; + + if (tight) + *tight = covers; + + if (covers) { + isl_pw_qpolynomial_fold_free(data->pwf); + return data->pwf_tight; + } + + data->pwf = isl_pw_qpolynomial_fold_fold(data->pwf, data->pwf_tight); + + return data->pwf; +error: + isl_pw_qpolynomial_fold_free(data->pwf_tight); + isl_pw_qpolynomial_fold_free(data->pwf); + return NULL; +} + +/* Apply bernstein expansion recursively by working in on len[i] + * set variables at a time, with i ranging from n_group - 1 to 0. + */ +static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_recursive( + __isl_take isl_pw_qpolynomial *pwqp, + int n_group, int *len, struct bernstein_data *data, int *tight) +{ + int i; + unsigned nparam; + unsigned nvar; + isl_pw_qpolynomial_fold *pwf; + + if (!pwqp) + return NULL; + + nparam = isl_pw_qpolynomial_dim(pwqp, isl_dim_param); + nvar = isl_pw_qpolynomial_dim(pwqp, isl_dim_in); + + pwqp = isl_pw_qpolynomial_move_dims(pwqp, isl_dim_param, nparam, + isl_dim_in, 0, nvar - len[n_group - 1]); + pwf = isl_pw_qpolynomial_bound(pwqp, data->type, tight); + + for (i = n_group - 2; i >= 0; --i) { + nparam = isl_pw_qpolynomial_fold_dim(pwf, isl_dim_param); + pwf = isl_pw_qpolynomial_fold_move_dims(pwf, isl_dim_in, 0, + isl_dim_param, nparam - len[i], len[i]); + if (tight && !*tight) + tight = NULL; + pwf = isl_pw_qpolynomial_fold_bound(pwf, tight); + } + + return pwf; +} + +static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_factors( + __isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct bernstein_data *data, int *tight) +{ + isl_factorizer *f; + isl_set *set; + isl_pw_qpolynomial *pwqp; + isl_pw_qpolynomial_fold *pwf; + + f = isl_basic_set_factorizer(bset); + if (!f) + goto error; + if (f->n_group == 0) { + isl_factorizer_free(f); + return bernstein_coefficients_base(bset, poly, data, tight); + } + + set = isl_set_from_basic_set(bset); + pwqp = isl_pw_qpolynomial_alloc(set, poly); + pwqp = isl_pw_qpolynomial_morph_domain(pwqp, isl_morph_copy(f->morph)); + + pwf = bernstein_coefficients_recursive(pwqp, f->n_group, f->len, data, + tight); + + isl_factorizer_free(f); + + return pwf; +error: + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + return NULL; +} + +static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_full_recursive( + __isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct bernstein_data *data, int *tight) +{ + int i; + int *len; + unsigned nvar; + isl_pw_qpolynomial_fold *pwf; + isl_set *set; + isl_pw_qpolynomial *pwqp; + + if (!bset || !poly) + goto error; + + nvar = isl_basic_set_dim(bset, isl_dim_set); + + len = isl_alloc_array(bset->ctx, int, nvar); + if (nvar && !len) + goto error; + + for (i = 0; i < nvar; ++i) + len[i] = 1; + + set = isl_set_from_basic_set(bset); + pwqp = isl_pw_qpolynomial_alloc(set, poly); + + pwf = bernstein_coefficients_recursive(pwqp, nvar, len, data, tight); + + free(len); + + return pwf; +error: + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + return NULL; +} + +/* Compute a bound on the polynomial defined over the parametric polytope + * using bernstein expansion and store the result + * in bound->pwf and bound->pwf_tight. + * + * If bernstein_recurse is set to ISL_BERNSTEIN_FACTORS, we check if + * the polytope can be factorized and apply bernstein expansion recursively + * on the factors. + * If bernstein_recurse is set to ISL_BERNSTEIN_INTERVALS, we apply + * bernstein expansion recursively on each dimension. + * Otherwise, we apply bernstein expansion on the entire polytope. + */ +int isl_qpolynomial_bound_on_domain_bernstein(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct isl_bound *bound) +{ + struct bernstein_data data; + isl_pw_qpolynomial_fold *pwf; + unsigned nvar; + int tight = 0; + int *tp = bound->check_tight ? &tight : NULL; + + if (!bset || !poly) + goto error; + + data.type = bound->type; + data.check_tight = bound->check_tight; + + nvar = isl_basic_set_dim(bset, isl_dim_set); + + if (bset->ctx->opt->bernstein_recurse & ISL_BERNSTEIN_FACTORS) + pwf = bernstein_coefficients_factors(bset, poly, &data, tp); + else if (nvar > 1 && + (bset->ctx->opt->bernstein_recurse & ISL_BERNSTEIN_INTERVALS)) + pwf = bernstein_coefficients_full_recursive(bset, poly, &data, tp); + else + pwf = bernstein_coefficients_base(bset, poly, &data, tp); + + if (tight) + bound->pwf_tight = isl_pw_qpolynomial_fold_fold(bound->pwf_tight, pwf); + else + bound->pwf = isl_pw_qpolynomial_fold_fold(bound->pwf, pwf); + + return 0; +error: + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + return -1; +} Index: lib/Analysis/isl/isl_blk.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_blk.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_BLK_H +#define ISL_BLK_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_blk { + size_t size; + isl_int *data; +}; + +#define ISL_BLK_CACHE_SIZE 20 + +struct isl_ctx; + +struct isl_blk isl_blk_alloc(struct isl_ctx *ctx, size_t n); +struct isl_blk isl_blk_empty(void); +int isl_blk_is_error(struct isl_blk block); +struct isl_blk isl_blk_extend(struct isl_ctx *ctx, struct isl_blk block, + size_t new_n); +void isl_blk_free(struct isl_ctx *ctx, struct isl_blk block); +void isl_blk_clear_cache(struct isl_ctx *ctx); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/isl_blk.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_blk.c @@ -0,0 +1,134 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include + +/* The maximal number of cache misses before first element is evicted */ +#define ISL_BLK_MAX_MISS 100 + +struct isl_blk isl_blk_empty() +{ + struct isl_blk block; + block.size = 0; + block.data = NULL; + return block; +} + +static int isl_blk_is_empty(struct isl_blk block) +{ + return block.size == 0 && block.data == NULL; +} + +static struct isl_blk isl_blk_error() +{ + struct isl_blk block; + block.size = -1; + block.data = NULL; + return block; +} + +int isl_blk_is_error(struct isl_blk block) +{ + return block.size == -1 && block.data == NULL; +} + +static void isl_blk_free_force(struct isl_ctx *ctx, struct isl_blk block) +{ + int i; + + for (i = 0; i < block.size; ++i) + isl_int_clear(block.data[i]); + free(block.data); +} + +static struct isl_blk extend(struct isl_ctx *ctx, struct isl_blk block, + size_t new_n) +{ + int i; + isl_int *p; + + if (block.size >= new_n) + return block; + + p = isl_realloc_array(ctx, block.data, isl_int, new_n); + if (!p) { + isl_blk_free_force(ctx, block); + return isl_blk_error(); + } + block.data = p; + + for (i = block.size; i < new_n; ++i) + isl_int_init(block.data[i]); + block.size = new_n; + + return block; +} + +struct isl_blk isl_blk_alloc(struct isl_ctx *ctx, size_t n) +{ + int i; + struct isl_blk block; + + block = isl_blk_empty(); + if (n && ctx->n_cached) { + int best = 0; + for (i = 1; ctx->cache[best].size != n && i < ctx->n_cached; ++i) { + if (ctx->cache[best].size < n) { + if (ctx->cache[i].size > ctx->cache[best].size) + best = i; + } else if (ctx->cache[i].size >= n && + ctx->cache[i].size < ctx->cache[best].size) + best = i; + } + if (ctx->cache[best].size < 2 * n + 100) { + block = ctx->cache[best]; + if (--ctx->n_cached != best) + ctx->cache[best] = ctx->cache[ctx->n_cached]; + if (best == 0) + ctx->n_miss = 0; + } else if (ctx->n_miss++ >= ISL_BLK_MAX_MISS) { + isl_blk_free_force(ctx, ctx->cache[0]); + if (--ctx->n_cached != 0) + ctx->cache[0] = ctx->cache[ctx->n_cached]; + ctx->n_miss = 0; + } + } + + return extend(ctx, block, n); +} + +struct isl_blk isl_blk_extend(struct isl_ctx *ctx, struct isl_blk block, + size_t new_n) +{ + if (isl_blk_is_empty(block)) + return isl_blk_alloc(ctx, new_n); + + return extend(ctx, block, new_n); +} + +void isl_blk_free(struct isl_ctx *ctx, struct isl_blk block) +{ + if (isl_blk_is_empty(block) || isl_blk_is_error(block)) + return; + + if (ctx->n_cached < ISL_BLK_CACHE_SIZE) + ctx->cache[ctx->n_cached++] = block; + else + isl_blk_free_force(ctx, block); +} + +void isl_blk_clear_cache(struct isl_ctx *ctx) +{ + int i; + + for (i = 0; i < ctx->n_cached; ++i) + isl_blk_free_force(ctx, ctx->cache[i]); + ctx->n_cached = 0; +} Index: lib/Analysis/isl/isl_bound.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_bound.h @@ -0,0 +1,20 @@ +#ifndef ISL_BOUND_H +#define ISL_BOUND_H + +#include + +struct isl_bound { + /* input */ + int check_tight; + int wrapping; + enum isl_fold type; + isl_space *dim; + isl_basic_set *bset; + isl_qpolynomial_fold *fold; + + /* output */ + isl_pw_qpolynomial_fold *pwf; + isl_pw_qpolynomial_fold *pwf_tight; +}; + +#endif Index: lib/Analysis/isl/isl_bound.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_bound.c @@ -0,0 +1,331 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Compute a bound on the polynomial defined over the parametric polytope + * using either range propagation or bernstein expansion and + * store the result in bound->pwf and bound->pwf_tight. + * Since bernstein expansion requires bounded domains, we apply + * range propagation on unbounded domains. Otherwise, we respect the choice + * of the user. + */ +static int compressed_guarded_poly_bound(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, void *user) +{ + struct isl_bound *bound = (struct isl_bound *)user; + int bounded; + + if (!bset || !poly) + goto error; + + if (bset->ctx->opt->bound == ISL_BOUND_RANGE) + return isl_qpolynomial_bound_on_domain_range(bset, poly, bound); + + bounded = isl_basic_set_is_bounded(bset); + if (bounded < 0) + goto error; + if (bounded) + return isl_qpolynomial_bound_on_domain_bernstein(bset, poly, bound); + else + return isl_qpolynomial_bound_on_domain_range(bset, poly, bound); +error: + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + return -1; +} + +static int unwrapped_guarded_poly_bound(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, void *user) +{ + struct isl_bound *bound = (struct isl_bound *)user; + isl_pw_qpolynomial_fold *top_pwf; + isl_pw_qpolynomial_fold *top_pwf_tight; + isl_space *dim; + isl_morph *morph; + int r; + + bset = isl_basic_set_detect_equalities(bset); + + if (!bset) + goto error; + + if (bset->n_eq == 0) + return compressed_guarded_poly_bound(bset, poly, user); + + morph = isl_basic_set_full_compression(bset); + + bset = isl_morph_basic_set(isl_morph_copy(morph), bset); + poly = isl_qpolynomial_morph_domain(poly, isl_morph_copy(morph)); + + dim = isl_morph_get_ran_space(morph); + dim = isl_space_params(dim); + + top_pwf = bound->pwf; + top_pwf_tight = bound->pwf_tight; + + dim = isl_space_from_domain(dim); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + bound->pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(dim), + bound->type); + bound->pwf_tight = isl_pw_qpolynomial_fold_zero(dim, bound->type); + + r = compressed_guarded_poly_bound(bset, poly, user); + + morph = isl_morph_dom_params(morph); + morph = isl_morph_ran_params(morph); + morph = isl_morph_inverse(morph); + + bound->pwf = isl_pw_qpolynomial_fold_morph_domain(bound->pwf, + isl_morph_copy(morph)); + bound->pwf_tight = isl_pw_qpolynomial_fold_morph_domain( + bound->pwf_tight, morph); + + bound->pwf = isl_pw_qpolynomial_fold_fold(top_pwf, bound->pwf); + bound->pwf_tight = isl_pw_qpolynomial_fold_fold(top_pwf_tight, + bound->pwf_tight); + + return r; +error: + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + return -1; +} + +static int guarded_poly_bound(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, void *user) +{ + struct isl_bound *bound = (struct isl_bound *)user; + isl_space *dim; + isl_pw_qpolynomial_fold *top_pwf; + isl_pw_qpolynomial_fold *top_pwf_tight; + int nparam; + int n_in; + int r; + + if (!bound->wrapping) + return unwrapped_guarded_poly_bound(bset, poly, user); + + nparam = isl_space_dim(bound->dim, isl_dim_param); + n_in = isl_space_dim(bound->dim, isl_dim_in); + + bset = isl_basic_set_move_dims(bset, isl_dim_param, nparam, + isl_dim_set, 0, n_in); + poly = isl_qpolynomial_move_dims(poly, isl_dim_param, nparam, + isl_dim_in, 0, n_in); + + dim = isl_basic_set_get_space(bset); + dim = isl_space_params(dim); + + top_pwf = bound->pwf; + top_pwf_tight = bound->pwf_tight; + + dim = isl_space_from_domain(dim); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + bound->pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(dim), + bound->type); + bound->pwf_tight = isl_pw_qpolynomial_fold_zero(dim, bound->type); + + r = unwrapped_guarded_poly_bound(bset, poly, user); + + bound->pwf = isl_pw_qpolynomial_fold_reset_space(bound->pwf, + isl_space_copy(bound->dim)); + bound->pwf_tight = isl_pw_qpolynomial_fold_reset_space(bound->pwf_tight, + isl_space_copy(bound->dim)); + + bound->pwf = isl_pw_qpolynomial_fold_fold(top_pwf, bound->pwf); + bound->pwf_tight = isl_pw_qpolynomial_fold_fold(top_pwf_tight, + bound->pwf_tight); + + return r; +} + +static isl_stat guarded_qp(__isl_take isl_qpolynomial *qp, void *user) +{ + struct isl_bound *bound = (struct isl_bound *)user; + isl_stat r; + + r = isl_qpolynomial_as_polynomial_on_domain(qp, bound->bset, + &guarded_poly_bound, user); + isl_qpolynomial_free(qp); + return r; +} + +static isl_stat basic_guarded_fold(__isl_take isl_basic_set *bset, void *user) +{ + struct isl_bound *bound = (struct isl_bound *)user; + isl_stat r; + + bound->bset = bset; + r = isl_qpolynomial_fold_foreach_qpolynomial(bound->fold, + &guarded_qp, user); + isl_basic_set_free(bset); + return r; +} + +static isl_stat guarded_fold(__isl_take isl_set *set, + __isl_take isl_qpolynomial_fold *fold, void *user) +{ + struct isl_bound *bound = (struct isl_bound *)user; + + if (!set || !fold) + goto error; + + set = isl_set_make_disjoint(set); + + bound->fold = fold; + bound->type = isl_qpolynomial_fold_get_type(fold); + + if (isl_set_foreach_basic_set(set, &basic_guarded_fold, bound) < 0) + goto error; + + isl_set_free(set); + isl_qpolynomial_fold_free(fold); + + return isl_stat_ok; +error: + isl_set_free(set); + isl_qpolynomial_fold_free(fold); + return isl_stat_error; +} + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_bound( + __isl_take isl_pw_qpolynomial_fold *pwf, int *tight) +{ + unsigned nvar; + struct isl_bound bound; + int covers; + + if (!pwf) + return NULL; + + bound.dim = isl_pw_qpolynomial_fold_get_domain_space(pwf); + + bound.wrapping = isl_space_is_wrapping(bound.dim); + if (bound.wrapping) + bound.dim = isl_space_unwrap(bound.dim); + nvar = isl_space_dim(bound.dim, isl_dim_out); + bound.dim = isl_space_domain(bound.dim); + bound.dim = isl_space_from_domain(bound.dim); + bound.dim = isl_space_add_dims(bound.dim, isl_dim_out, 1); + + if (nvar == 0) { + if (tight) + *tight = 1; + return isl_pw_qpolynomial_fold_reset_space(pwf, bound.dim); + } + + if (isl_pw_qpolynomial_fold_is_zero(pwf)) { + enum isl_fold type = pwf->type; + isl_pw_qpolynomial_fold_free(pwf); + if (tight) + *tight = 1; + return isl_pw_qpolynomial_fold_zero(bound.dim, type); + } + + bound.pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(bound.dim), + pwf->type); + bound.pwf_tight = isl_pw_qpolynomial_fold_zero(isl_space_copy(bound.dim), + pwf->type); + bound.check_tight = !!tight; + + if (isl_pw_qpolynomial_fold_foreach_lifted_piece(pwf, + guarded_fold, &bound) < 0) + goto error; + + covers = isl_pw_qpolynomial_fold_covers(bound.pwf_tight, bound.pwf); + if (covers < 0) + goto error; + + if (tight) + *tight = covers; + + isl_space_free(bound.dim); + isl_pw_qpolynomial_fold_free(pwf); + + if (covers) { + isl_pw_qpolynomial_fold_free(bound.pwf); + return bound.pwf_tight; + } + + bound.pwf = isl_pw_qpolynomial_fold_fold(bound.pwf, bound.pwf_tight); + + return bound.pwf; +error: + isl_pw_qpolynomial_fold_free(bound.pwf_tight); + isl_pw_qpolynomial_fold_free(bound.pwf); + isl_pw_qpolynomial_fold_free(pwf); + isl_space_free(bound.dim); + return NULL; +} + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_bound( + __isl_take isl_pw_qpolynomial *pwqp, enum isl_fold type, int *tight) +{ + isl_pw_qpolynomial_fold *pwf; + + pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial(type, pwqp); + return isl_pw_qpolynomial_fold_bound(pwf, tight); +} + +struct isl_union_bound_data { + enum isl_fold type; + int tight; + isl_union_pw_qpolynomial_fold *res; +}; + +static isl_stat bound_pw(__isl_take isl_pw_qpolynomial *pwqp, void *user) +{ + struct isl_union_bound_data *data = user; + isl_pw_qpolynomial_fold *pwf; + + pwf = isl_pw_qpolynomial_bound(pwqp, data->type, + data->tight ? &data->tight : NULL); + data->res = isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold( + data->res, pwf); + + return isl_stat_ok; +} + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_bound( + __isl_take isl_union_pw_qpolynomial *upwqp, + enum isl_fold type, int *tight) +{ + isl_space *dim; + struct isl_union_bound_data data = { type, 1, NULL }; + + if (!upwqp) + return NULL; + + if (!tight) + data.tight = 0; + + dim = isl_union_pw_qpolynomial_get_space(upwqp); + data.res = isl_union_pw_qpolynomial_fold_zero(dim, type); + if (isl_union_pw_qpolynomial_foreach_pw_qpolynomial(upwqp, + &bound_pw, &data) < 0) + goto error; + + isl_union_pw_qpolynomial_free(upwqp); + if (tight) + *tight = data.tight; + + return data.res; +error: + isl_union_pw_qpolynomial_free(upwqp); + isl_union_pw_qpolynomial_fold_free(data.res); + return NULL; +} Index: lib/Analysis/isl/isl_coalesce.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_coalesce.c @@ -0,0 +1,3194 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012-2013 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include "isl_map_private.h" +#include +#include +#include "isl_tab.h" +#include +#include +#include +#include +#include + +#define STATUS_ERROR -1 +#define STATUS_REDUNDANT 1 +#define STATUS_VALID 2 +#define STATUS_SEPARATE 3 +#define STATUS_CUT 4 +#define STATUS_ADJ_EQ 5 +#define STATUS_ADJ_INEQ 6 + +static int status_in(isl_int *ineq, struct isl_tab *tab) +{ + enum isl_ineq_type type = isl_tab_ineq_type(tab, ineq); + switch (type) { + default: + case isl_ineq_error: return STATUS_ERROR; + case isl_ineq_redundant: return STATUS_VALID; + case isl_ineq_separate: return STATUS_SEPARATE; + case isl_ineq_cut: return STATUS_CUT; + case isl_ineq_adj_eq: return STATUS_ADJ_EQ; + case isl_ineq_adj_ineq: return STATUS_ADJ_INEQ; + } +} + +/* Compute the position of the equalities of basic map "bmap_i" + * with respect to the basic map represented by "tab_j". + * The resulting array has twice as many entries as the number + * of equalities corresponding to the two inequalties to which + * each equality corresponds. + */ +static int *eq_status_in(__isl_keep isl_basic_map *bmap_i, + struct isl_tab *tab_j) +{ + int k, l; + int *eq = isl_calloc_array(bmap_i->ctx, int, 2 * bmap_i->n_eq); + unsigned dim; + + if (!eq) + return NULL; + + dim = isl_basic_map_total_dim(bmap_i); + for (k = 0; k < bmap_i->n_eq; ++k) { + for (l = 0; l < 2; ++l) { + isl_seq_neg(bmap_i->eq[k], bmap_i->eq[k], 1+dim); + eq[2 * k + l] = status_in(bmap_i->eq[k], tab_j); + if (eq[2 * k + l] == STATUS_ERROR) + goto error; + } + if (eq[2 * k] == STATUS_SEPARATE || + eq[2 * k + 1] == STATUS_SEPARATE) + break; + } + + return eq; +error: + free(eq); + return NULL; +} + +/* Compute the position of the inequalities of basic map "bmap_i" + * (also represented by "tab_i", if not NULL) with respect to the basic map + * represented by "tab_j". + */ +static int *ineq_status_in(__isl_keep isl_basic_map *bmap_i, + struct isl_tab *tab_i, struct isl_tab *tab_j) +{ + int k; + unsigned n_eq = bmap_i->n_eq; + int *ineq = isl_calloc_array(bmap_i->ctx, int, bmap_i->n_ineq); + + if (!ineq) + return NULL; + + for (k = 0; k < bmap_i->n_ineq; ++k) { + if (tab_i && isl_tab_is_redundant(tab_i, n_eq + k)) { + ineq[k] = STATUS_REDUNDANT; + continue; + } + ineq[k] = status_in(bmap_i->ineq[k], tab_j); + if (ineq[k] == STATUS_ERROR) + goto error; + if (ineq[k] == STATUS_SEPARATE) + break; + } + + return ineq; +error: + free(ineq); + return NULL; +} + +static int any(int *con, unsigned len, int status) +{ + int i; + + for (i = 0; i < len ; ++i) + if (con[i] == status) + return 1; + return 0; +} + +static int count(int *con, unsigned len, int status) +{ + int i; + int c = 0; + + for (i = 0; i < len ; ++i) + if (con[i] == status) + c++; + return c; +} + +static int all(int *con, unsigned len, int status) +{ + int i; + + for (i = 0; i < len ; ++i) { + if (con[i] == STATUS_REDUNDANT) + continue; + if (con[i] != status) + return 0; + } + return 1; +} + +/* Internal information associated to a basic map in a map + * that is to be coalesced by isl_map_coalesce. + * + * "bmap" is the basic map itself (or NULL if "removed" is set) + * "tab" is the corresponding tableau (or NULL if "removed" is set) + * "hull_hash" identifies the affine space in which "bmap" lives. + * "removed" is set if this basic map has been removed from the map + * "simplify" is set if this basic map may have some unknown integer + * divisions that were not present in the input basic maps. The basic + * map should then be simplified such that we may be able to find + * a definition among the constraints. + * + * "eq" and "ineq" are only set if we are currently trying to coalesce + * this basic map with another basic map, in which case they represent + * the position of the inequalities of this basic map with respect to + * the other basic map. The number of elements in the "eq" array + * is twice the number of equalities in the "bmap", corresponding + * to the two inequalities that make up each equality. + */ +struct isl_coalesce_info { + isl_basic_map *bmap; + struct isl_tab *tab; + uint32_t hull_hash; + int removed; + int simplify; + int *eq; + int *ineq; +}; + +/* Are all non-redundant constraints of the basic map represented by "info" + * either valid or cut constraints with respect to the other basic map? + */ +static int all_valid_or_cut(struct isl_coalesce_info *info) +{ + int i; + + for (i = 0; i < 2 * info->bmap->n_eq; ++i) { + if (info->eq[i] == STATUS_REDUNDANT) + continue; + if (info->eq[i] == STATUS_VALID) + continue; + if (info->eq[i] == STATUS_CUT) + continue; + return 0; + } + + for (i = 0; i < info->bmap->n_ineq; ++i) { + if (info->ineq[i] == STATUS_REDUNDANT) + continue; + if (info->ineq[i] == STATUS_VALID) + continue; + if (info->ineq[i] == STATUS_CUT) + continue; + return 0; + } + + return 1; +} + +/* Compute the hash of the (apparent) affine hull of info->bmap (with + * the existentially quantified variables removed) and store it + * in info->hash. + */ +static int coalesce_info_set_hull_hash(struct isl_coalesce_info *info) +{ + isl_basic_map *hull; + unsigned n_div; + + hull = isl_basic_map_copy(info->bmap); + hull = isl_basic_map_plain_affine_hull(hull); + n_div = isl_basic_map_dim(hull, isl_dim_div); + hull = isl_basic_map_drop_constraints_involving_dims(hull, + isl_dim_div, 0, n_div); + info->hull_hash = isl_basic_map_get_hash(hull); + isl_basic_map_free(hull); + + return hull ? 0 : -1; +} + +/* Free all the allocated memory in an array + * of "n" isl_coalesce_info elements. + */ +static void clear_coalesce_info(int n, struct isl_coalesce_info *info) +{ + int i; + + if (!info) + return; + + for (i = 0; i < n; ++i) { + isl_basic_map_free(info[i].bmap); + isl_tab_free(info[i].tab); + } + + free(info); +} + +/* Drop the basic map represented by "info". + * That is, clear the memory associated to the entry and + * mark it as having been removed. + */ +static void drop(struct isl_coalesce_info *info) +{ + info->bmap = isl_basic_map_free(info->bmap); + isl_tab_free(info->tab); + info->tab = NULL; + info->removed = 1; +} + +/* Exchange the information in "info1" with that in "info2". + */ +static void exchange(struct isl_coalesce_info *info1, + struct isl_coalesce_info *info2) +{ + struct isl_coalesce_info info; + + info = *info1; + *info1 = *info2; + *info2 = info; +} + +/* This type represents the kind of change that has been performed + * while trying to coalesce two basic maps. + * + * isl_change_none: nothing was changed + * isl_change_drop_first: the first basic map was removed + * isl_change_drop_second: the second basic map was removed + * isl_change_fuse: the two basic maps were replaced by a new basic map. + */ +enum isl_change { + isl_change_error = -1, + isl_change_none = 0, + isl_change_drop_first, + isl_change_drop_second, + isl_change_fuse, +}; + +/* Update "change" based on an interchange of the first and the second + * basic map. That is, interchange isl_change_drop_first and + * isl_change_drop_second. + */ +static enum isl_change invert_change(enum isl_change change) +{ + switch (change) { + case isl_change_error: + return isl_change_error; + case isl_change_none: + return isl_change_none; + case isl_change_drop_first: + return isl_change_drop_second; + case isl_change_drop_second: + return isl_change_drop_first; + case isl_change_fuse: + return isl_change_fuse; + } + + return isl_change_error; +} + +/* Add the valid constraints of the basic map represented by "info" + * to "bmap". "len" is the size of the constraints. + * If only one of the pair of inequalities that make up an equality + * is valid, then add that inequality. + */ +static __isl_give isl_basic_map *add_valid_constraints( + __isl_take isl_basic_map *bmap, struct isl_coalesce_info *info, + unsigned len) +{ + int k, l; + + if (!bmap) + return NULL; + + for (k = 0; k < info->bmap->n_eq; ++k) { + if (info->eq[2 * k] == STATUS_VALID && + info->eq[2 * k + 1] == STATUS_VALID) { + l = isl_basic_map_alloc_equality(bmap); + if (l < 0) + return isl_basic_map_free(bmap); + isl_seq_cpy(bmap->eq[l], info->bmap->eq[k], len); + } else if (info->eq[2 * k] == STATUS_VALID) { + l = isl_basic_map_alloc_inequality(bmap); + if (l < 0) + return isl_basic_map_free(bmap); + isl_seq_neg(bmap->ineq[l], info->bmap->eq[k], len); + } else if (info->eq[2 * k + 1] == STATUS_VALID) { + l = isl_basic_map_alloc_inequality(bmap); + if (l < 0) + return isl_basic_map_free(bmap); + isl_seq_cpy(bmap->ineq[l], info->bmap->eq[k], len); + } + } + + for (k = 0; k < info->bmap->n_ineq; ++k) { + if (info->ineq[k] != STATUS_VALID) + continue; + l = isl_basic_map_alloc_inequality(bmap); + if (l < 0) + return isl_basic_map_free(bmap); + isl_seq_cpy(bmap->ineq[l], info->bmap->ineq[k], len); + } + + return bmap; +} + +/* Is "bmap" defined by a number of (non-redundant) constraints that + * is greater than the number of constraints of basic maps i and j combined? + * Equalities are counted as two inequalities. + */ +static int number_of_constraints_increases(int i, int j, + struct isl_coalesce_info *info, + __isl_keep isl_basic_map *bmap, struct isl_tab *tab) +{ + int k, n_old, n_new; + + n_old = 2 * info[i].bmap->n_eq + info[i].bmap->n_ineq; + n_old += 2 * info[j].bmap->n_eq + info[j].bmap->n_ineq; + + n_new = 2 * bmap->n_eq; + for (k = 0; k < bmap->n_ineq; ++k) + if (!isl_tab_is_redundant(tab, bmap->n_eq + k)) + ++n_new; + + return n_new > n_old; +} + +/* Replace the pair of basic maps i and j by the basic map bounded + * by the valid constraints in both basic maps and the constraints + * in extra (if not NULL). + * Place the fused basic map in the position that is the smallest of i and j. + * + * If "detect_equalities" is set, then look for equalities encoded + * as pairs of inequalities. + * If "check_number" is set, then the original basic maps are only + * replaced if the total number of constraints does not increase. + * While the number of integer divisions in the two basic maps + * is assumed to be the same, the actual definitions may be different. + * We only copy the definition from one of the basic map if it is + * the same as that of the other basic map. Otherwise, we mark + * the integer division as unknown and simplify the basic map + * in an attempt to recover the integer division definition. + */ +static enum isl_change fuse(int i, int j, struct isl_coalesce_info *info, + __isl_keep isl_mat *extra, int detect_equalities, int check_number) +{ + int k, l; + struct isl_basic_map *fused = NULL; + struct isl_tab *fused_tab = NULL; + unsigned total = isl_basic_map_total_dim(info[i].bmap); + unsigned extra_rows = extra ? extra->n_row : 0; + unsigned n_eq, n_ineq; + int simplify = 0; + + if (j < i) + return fuse(j, i, info, extra, detect_equalities, check_number); + + n_eq = info[i].bmap->n_eq + info[j].bmap->n_eq; + n_ineq = info[i].bmap->n_ineq + info[j].bmap->n_ineq; + fused = isl_basic_map_alloc_space(isl_space_copy(info[i].bmap->dim), + info[i].bmap->n_div, n_eq, n_eq + n_ineq + extra_rows); + fused = add_valid_constraints(fused, &info[i], 1 + total); + fused = add_valid_constraints(fused, &info[j], 1 + total); + if (!fused) + goto error; + + for (k = 0; k < info[i].bmap->n_div; ++k) { + int l = isl_basic_map_alloc_div(fused); + if (l < 0) + goto error; + if (isl_seq_eq(info[i].bmap->div[k], info[j].bmap->div[k], + 1 + 1 + total)) { + isl_seq_cpy(fused->div[l], info[i].bmap->div[k], + 1 + 1 + total); + } else { + isl_int_set_si(fused->div[l][0], 0); + simplify = 1; + } + } + + for (k = 0; k < extra_rows; ++k) { + l = isl_basic_map_alloc_inequality(fused); + if (l < 0) + goto error; + isl_seq_cpy(fused->ineq[l], extra->row[k], 1 + total); + } + + if (detect_equalities) + fused = isl_basic_map_detect_inequality_pairs(fused, NULL); + fused = isl_basic_map_gauss(fused, NULL); + if (simplify || info[j].simplify) { + fused = isl_basic_map_simplify(fused); + info[i].simplify = 0; + } + fused = isl_basic_map_finalize(fused); + if (ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_RATIONAL) && + ISL_F_ISSET(info[j].bmap, ISL_BASIC_MAP_RATIONAL)) + ISL_F_SET(fused, ISL_BASIC_MAP_RATIONAL); + + fused_tab = isl_tab_from_basic_map(fused, 0); + if (isl_tab_detect_redundant(fused_tab) < 0) + goto error; + + if (check_number && + number_of_constraints_increases(i, j, info, fused, fused_tab)) { + isl_tab_free(fused_tab); + isl_basic_map_free(fused); + return isl_change_none; + } + + isl_basic_map_free(info[i].bmap); + info[i].bmap = fused; + isl_tab_free(info[i].tab); + info[i].tab = fused_tab; + drop(&info[j]); + + return isl_change_fuse; +error: + isl_tab_free(fused_tab); + isl_basic_map_free(fused); + return isl_change_error; +} + +/* Given a pair of basic maps i and j such that all constraints are either + * "valid" or "cut", check if the facets corresponding to the "cut" + * constraints of i lie entirely within basic map j. + * If so, replace the pair by the basic map consisting of the valid + * constraints in both basic maps. + * Checking whether the facet lies entirely within basic map j + * is performed by checking whether the constraints of basic map j + * are valid for the facet. These tests are performed on a rational + * tableau to avoid the theoretical possibility that a constraint + * that was considered to be a cut constraint for the entire basic map i + * happens to be considered to be a valid constraint for the facet, + * even though it cuts off the same rational points. + * + * To see that we are not introducing any extra points, call the + * two basic maps A and B and the resulting map U and let x + * be an element of U \setminus ( A \cup B ). + * A line connecting x with an element of A \cup B meets a facet F + * of either A or B. Assume it is a facet of B and let c_1 be + * the corresponding facet constraint. We have c_1(x) < 0 and + * so c_1 is a cut constraint. This implies that there is some + * (possibly rational) point x' satisfying the constraints of A + * and the opposite of c_1 as otherwise c_1 would have been marked + * valid for A. The line connecting x and x' meets a facet of A + * in a (possibly rational) point that also violates c_1, but this + * is impossible since all cut constraints of B are valid for all + * cut facets of A. + * In case F is a facet of A rather than B, then we can apply the + * above reasoning to find a facet of B separating x from A \cup B first. + */ +static enum isl_change check_facets(int i, int j, + struct isl_coalesce_info *info) +{ + int k, l; + struct isl_tab_undo *snap, *snap2; + unsigned n_eq = info[i].bmap->n_eq; + + snap = isl_tab_snap(info[i].tab); + if (isl_tab_mark_rational(info[i].tab) < 0) + return isl_change_error; + snap2 = isl_tab_snap(info[i].tab); + + for (k = 0; k < info[i].bmap->n_ineq; ++k) { + if (info[i].ineq[k] != STATUS_CUT) + continue; + if (isl_tab_select_facet(info[i].tab, n_eq + k) < 0) + return isl_change_error; + for (l = 0; l < info[j].bmap->n_ineq; ++l) { + int stat; + if (info[j].ineq[l] != STATUS_CUT) + continue; + stat = status_in(info[j].bmap->ineq[l], info[i].tab); + if (stat < 0) + return isl_change_error; + if (stat != STATUS_VALID) + break; + } + if (isl_tab_rollback(info[i].tab, snap2) < 0) + return isl_change_error; + if (l < info[j].bmap->n_ineq) + break; + } + + if (k < info[i].bmap->n_ineq) { + if (isl_tab_rollback(info[i].tab, snap) < 0) + return isl_change_error; + return isl_change_none; + } + return fuse(i, j, info, NULL, 0, 0); +} + +/* Check if info->bmap contains the basic map represented + * by the tableau "tab". + * For each equality, we check both the constraint itself + * (as an inequality) and its negation. Make sure the + * equality is returned to its original state before returning. + */ +static int contains(struct isl_coalesce_info *info, struct isl_tab *tab) +{ + int k; + unsigned dim; + isl_basic_map *bmap = info->bmap; + + dim = isl_basic_map_total_dim(bmap); + for (k = 0; k < bmap->n_eq; ++k) { + int stat; + isl_seq_neg(bmap->eq[k], bmap->eq[k], 1 + dim); + stat = status_in(bmap->eq[k], tab); + isl_seq_neg(bmap->eq[k], bmap->eq[k], 1 + dim); + if (stat < 0) + return -1; + if (stat != STATUS_VALID) + return 0; + stat = status_in(bmap->eq[k], tab); + if (stat < 0) + return -1; + if (stat != STATUS_VALID) + return 0; + } + + for (k = 0; k < bmap->n_ineq; ++k) { + int stat; + if (info->ineq[k] == STATUS_REDUNDANT) + continue; + stat = status_in(bmap->ineq[k], tab); + if (stat < 0) + return -1; + if (stat != STATUS_VALID) + return 0; + } + return 1; +} + +/* Basic map "i" has an inequality (say "k") that is adjacent + * to some inequality of basic map "j". All the other inequalities + * are valid for "j". + * Check if basic map "j" forms an extension of basic map "i". + * + * Note that this function is only called if some of the equalities or + * inequalities of basic map "j" do cut basic map "i". The function is + * correct even if there are no such cut constraints, but in that case + * the additional checks performed by this function are overkill. + * + * In particular, we replace constraint k, say f >= 0, by constraint + * f <= -1, add the inequalities of "j" that are valid for "i" + * and check if the result is a subset of basic map "j". + * If so, then we know that this result is exactly equal to basic map "j" + * since all its constraints are valid for basic map "j". + * By combining the valid constraints of "i" (all equalities and all + * inequalities except "k") and the valid constraints of "j" we therefore + * obtain a basic map that is equal to their union. + * In this case, there is no need to perform a rollback of the tableau + * since it is going to be destroyed in fuse(). + * + * + * |\__ |\__ + * | \__ | \__ + * | \_ => | \__ + * |_______| _ |_________\ + * + * + * |\ |\ + * | \ | \ + * | \ | \ + * | | | \ + * | ||\ => | \ + * | || \ | \ + * | || | | | + * |__||_/ |_____/ + */ +static enum isl_change is_adj_ineq_extension(int i, int j, + struct isl_coalesce_info *info) +{ + int k; + struct isl_tab_undo *snap; + unsigned n_eq = info[i].bmap->n_eq; + unsigned total = isl_basic_map_total_dim(info[i].bmap); + int r; + int super; + + if (isl_tab_extend_cons(info[i].tab, 1 + info[j].bmap->n_ineq) < 0) + return isl_change_error; + + for (k = 0; k < info[i].bmap->n_ineq; ++k) + if (info[i].ineq[k] == STATUS_ADJ_INEQ) + break; + if (k >= info[i].bmap->n_ineq) + isl_die(isl_basic_map_get_ctx(info[i].bmap), isl_error_internal, + "info[i].ineq should have exactly one STATUS_ADJ_INEQ", + return isl_change_error); + + snap = isl_tab_snap(info[i].tab); + + if (isl_tab_unrestrict(info[i].tab, n_eq + k) < 0) + return isl_change_error; + + isl_seq_neg(info[i].bmap->ineq[k], info[i].bmap->ineq[k], 1 + total); + isl_int_sub_ui(info[i].bmap->ineq[k][0], info[i].bmap->ineq[k][0], 1); + r = isl_tab_add_ineq(info[i].tab, info[i].bmap->ineq[k]); + isl_seq_neg(info[i].bmap->ineq[k], info[i].bmap->ineq[k], 1 + total); + isl_int_sub_ui(info[i].bmap->ineq[k][0], info[i].bmap->ineq[k][0], 1); + if (r < 0) + return isl_change_error; + + for (k = 0; k < info[j].bmap->n_ineq; ++k) { + if (info[j].ineq[k] != STATUS_VALID) + continue; + if (isl_tab_add_ineq(info[i].tab, info[j].bmap->ineq[k]) < 0) + return isl_change_error; + } + + super = contains(&info[j], info[i].tab); + if (super < 0) + return isl_change_error; + if (super) + return fuse(i, j, info, NULL, 0, 0); + + if (isl_tab_rollback(info[i].tab, snap) < 0) + return isl_change_error; + + return isl_change_none; +} + + +/* Both basic maps have at least one inequality with and adjacent + * (but opposite) inequality in the other basic map. + * Check that there are no cut constraints and that there is only + * a single pair of adjacent inequalities. + * If so, we can replace the pair by a single basic map described + * by all but the pair of adjacent inequalities. + * Any additional points introduced lie strictly between the two + * adjacent hyperplanes and can therefore be integral. + * + * ____ _____ + * / ||\ / \ + * / || \ / \ + * \ || \ => \ \ + * \ || / \ / + * \___||_/ \_____/ + * + * The test for a single pair of adjancent inequalities is important + * for avoiding the combination of two basic maps like the following + * + * /| + * / | + * /__| + * _____ + * | | + * | | + * |___| + * + * If there are some cut constraints on one side, then we may + * still be able to fuse the two basic maps, but we need to perform + * some additional checks in is_adj_ineq_extension. + */ +static enum isl_change check_adj_ineq(int i, int j, + struct isl_coalesce_info *info) +{ + int count_i, count_j; + int cut_i, cut_j; + + count_i = count(info[i].ineq, info[i].bmap->n_ineq, STATUS_ADJ_INEQ); + count_j = count(info[j].ineq, info[j].bmap->n_ineq, STATUS_ADJ_INEQ); + + if (count_i != 1 && count_j != 1) + return isl_change_none; + + cut_i = any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_CUT) || + any(info[i].ineq, info[i].bmap->n_ineq, STATUS_CUT); + cut_j = any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_CUT) || + any(info[j].ineq, info[j].bmap->n_ineq, STATUS_CUT); + + if (!cut_i && !cut_j && count_i == 1 && count_j == 1) + return fuse(i, j, info, NULL, 0, 0); + + if (count_i == 1 && !cut_i) + return is_adj_ineq_extension(i, j, info); + + if (count_j == 1 && !cut_j) + return is_adj_ineq_extension(j, i, info); + + return isl_change_none; +} + +/* Given an affine transformation matrix "T", does row "row" represent + * anything other than a unit vector (possibly shifted by a constant) + * that is not involved in any of the other rows? + * + * That is, if a constraint involves the variable corresponding to + * the row, then could its preimage by "T" have any coefficients + * that are different from those in the original constraint? + */ +static int not_unique_unit_row(__isl_keep isl_mat *T, int row) +{ + int i, j; + int len = T->n_col - 1; + + i = isl_seq_first_non_zero(T->row[row] + 1, len); + if (i < 0) + return 1; + if (!isl_int_is_one(T->row[row][1 + i]) && + !isl_int_is_negone(T->row[row][1 + i])) + return 1; + + j = isl_seq_first_non_zero(T->row[row] + 1 + i + 1, len - (i + 1)); + if (j >= 0) + return 1; + + for (j = 1; j < T->n_row; ++j) { + if (j == row) + continue; + if (!isl_int_is_zero(T->row[j][1 + i])) + return 1; + } + + return 0; +} + +/* Does inequality constraint "ineq" of "bmap" involve any of + * the variables marked in "affected"? + * "total" is the total number of variables, i.e., the number + * of entries in "affected". + */ +static int is_affected(__isl_keep isl_basic_map *bmap, int ineq, int *affected, + int total) +{ + int i; + + for (i = 0; i < total; ++i) { + if (!affected[i]) + continue; + if (!isl_int_is_zero(bmap->ineq[ineq][1 + i])) + return 1; + } + + return 0; +} + +/* Given the compressed version of inequality constraint "ineq" + * of info->bmap in "v", check if the constraint can be tightened, + * where the compression is based on an equality constraint valid + * for info->tab. + * If so, add the tightened version of the inequality constraint + * to info->tab. "v" may be modified by this function. + * + * That is, if the compressed constraint is of the form + * + * m f() + c >= 0 + * + * with 0 < c < m, then it is equivalent to + * + * f() >= 0 + * + * This means that c can also be subtracted from the original, + * uncompressed constraint without affecting the integer points + * in info->tab. Add this tightened constraint as an extra row + * to info->tab to make this information explicitly available. + */ +static __isl_give isl_vec *try_tightening(struct isl_coalesce_info *info, + int ineq, __isl_take isl_vec *v) +{ + isl_ctx *ctx; + int r; + + if (!v) + return NULL; + + ctx = isl_vec_get_ctx(v); + isl_seq_gcd(v->el + 1, v->size - 1, &ctx->normalize_gcd); + if (isl_int_is_zero(ctx->normalize_gcd) || + isl_int_is_one(ctx->normalize_gcd)) { + return v; + } + + v = isl_vec_cow(v); + if (!v) + return NULL; + + isl_int_fdiv_r(v->el[0], v->el[0], ctx->normalize_gcd); + if (isl_int_is_zero(v->el[0])) + return v; + + if (isl_tab_extend_cons(info->tab, 1) < 0) + return isl_vec_free(v); + + isl_int_sub(info->bmap->ineq[ineq][0], + info->bmap->ineq[ineq][0], v->el[0]); + r = isl_tab_add_ineq(info->tab, info->bmap->ineq[ineq]); + isl_int_add(info->bmap->ineq[ineq][0], + info->bmap->ineq[ineq][0], v->el[0]); + + if (r < 0) + return isl_vec_free(v); + + return v; +} + +/* Tighten the constraints on the facet represented by info->tab. + * In particular, on input, info->tab represents the result + * of replacing constraint k of info->bmap, i.e., f_k >= 0, + * by the adjacent equality, i.e., f_k + 1 = 0. + * + * Compute a variable compression from the equality constraint f_k + 1 = 0 + * and use it to tighten the other constraints of info->bmap, + * updating info->tab (and leaving info->bmap untouched). + * The compression handles essentially two cases, one where a variable + * is assigned a fixed value and can therefore be eliminated, and one + * where one variable is a shifted multiple of some other variable and + * can therefore be replaced by that multiple. + * Gaussian elimination would also work for the first case, but for + * the second case, the effectiveness would depend on the order + * of the variables. + * After compression, some of the constraints may have coefficients + * with a common divisor. If this divisor does not divide the constant + * term, then the constraint can be tightened. + * The tightening is performed on the tableau info->tab by introducing + * extra (temporary) constraints. + * + * Only constraints that are possibly affected by the compression are + * considered. In particular, if the constraint only involves variables + * that are directly mapped to a distinct set of other variables, then + * no common divisor can be introduced and no tightening can occur. + */ +static isl_stat tighten_on_relaxed_facet(struct isl_coalesce_info *info, + int k) +{ + unsigned total; + isl_ctx *ctx; + isl_vec *v = NULL; + isl_mat *T; + int i; + int *affected; + + ctx = isl_basic_map_get_ctx(info->bmap); + total = isl_basic_map_total_dim(info->bmap); + isl_int_add_ui(info->bmap->ineq[k][0], info->bmap->ineq[k][0], 1); + T = isl_mat_sub_alloc6(ctx, info->bmap->ineq, k, 1, 0, 1 + total); + T = isl_mat_variable_compression(T, NULL); + isl_int_sub_ui(info->bmap->ineq[k][0], info->bmap->ineq[k][0], 1); + if (!T) + return isl_stat_error; + if (T->n_col == 0) { + isl_mat_free(T); + return isl_stat_ok; + } + + affected = isl_alloc_array(ctx, int, total); + if (!affected) + goto error; + + for (i = 0; i < total; ++i) + affected[i] = not_unique_unit_row(T, 1 + i); + + for (i = 0; i < info->bmap->n_ineq; ++i) { + if (i == k) + continue; + if (!is_affected(info->bmap, i, affected, total)) + continue; + v = isl_vec_alloc(ctx, 1 + total); + if (!v) + goto error; + isl_seq_cpy(v->el, info->bmap->ineq[i], 1 + total); + v = isl_vec_mat_product(v, isl_mat_copy(T)); + v = try_tightening(info, i, v); + isl_vec_free(v); + if (!v) + goto error; + } + + isl_mat_free(T); + free(affected); + return isl_stat_ok; +error: + isl_mat_free(T); + free(affected); + return isl_stat_error; +} + +/* Basic map "i" has an inequality "k" that is adjacent to some equality + * of basic map "j". All the other inequalities are valid for "j". + * Check if basic map "j" forms an extension of basic map "i". + * + * In particular, we relax constraint "k", compute the corresponding + * facet and check whether it is included in the other basic map. + * Before testing for inclusion, the constraints on the facet + * are tightened to increase the chance of an inclusion being detected. + * If the facet is included, we know that relaxing the constraint extends + * the basic map with exactly the other basic map (we already know that this + * other basic map is included in the extension, because there + * were no "cut" inequalities in "i") and we can replace the + * two basic maps by this extension. + * Each integer division that does not have exactly the same + * definition in "i" and "j" is marked unknown and the basic map + * is scheduled to be simplified in an attempt to recover + * the integer division definition. + * Place this extension in the position that is the smallest of i and j. + * ____ _____ + * / || / | + * / || / | + * \ || => \ | + * \ || \ | + * \___|| \____| + */ +static enum isl_change is_adj_eq_extension(int i, int j, int k, + struct isl_coalesce_info *info) +{ + int change = isl_change_none; + int super; + struct isl_tab_undo *snap, *snap2; + unsigned n_eq = info[i].bmap->n_eq; + + if (isl_tab_is_equality(info[i].tab, n_eq + k)) + return isl_change_none; + + snap = isl_tab_snap(info[i].tab); + if (isl_tab_relax(info[i].tab, n_eq + k) < 0) + return isl_change_error; + snap2 = isl_tab_snap(info[i].tab); + if (isl_tab_select_facet(info[i].tab, n_eq + k) < 0) + return isl_change_error; + if (tighten_on_relaxed_facet(&info[i], k) < 0) + return isl_change_error; + super = contains(&info[j], info[i].tab); + if (super < 0) + return isl_change_error; + if (super) { + int l; + unsigned total; + + if (isl_tab_rollback(info[i].tab, snap2) < 0) + return isl_change_error; + info[i].bmap = isl_basic_map_cow(info[i].bmap); + if (!info[i].bmap) + return isl_change_error; + total = isl_basic_map_total_dim(info[i].bmap); + for (l = 0; l < info[i].bmap->n_div; ++l) + if (!isl_seq_eq(info[i].bmap->div[l], + info[j].bmap->div[l], 1 + 1 + total)) { + isl_int_set_si(info[i].bmap->div[l][0], 0); + info[i].simplify = 1; + } + isl_int_add_ui(info[i].bmap->ineq[k][0], + info[i].bmap->ineq[k][0], 1); + ISL_F_SET(info[i].bmap, ISL_BASIC_MAP_FINAL); + drop(&info[j]); + if (j < i) + exchange(&info[i], &info[j]); + change = isl_change_fuse; + } else + if (isl_tab_rollback(info[i].tab, snap) < 0) + return isl_change_error; + + return change; +} + +/* Data structure that keeps track of the wrapping constraints + * and of information to bound the coefficients of those constraints. + * + * bound is set if we want to apply a bound on the coefficients + * mat contains the wrapping constraints + * max is the bound on the coefficients (if bound is set) + */ +struct isl_wraps { + int bound; + isl_mat *mat; + isl_int max; +}; + +/* Update wraps->max to be greater than or equal to the coefficients + * in the equalities and inequalities of info->bmap that can be removed + * if we end up applying wrapping. + */ +static void wraps_update_max(struct isl_wraps *wraps, + struct isl_coalesce_info *info) +{ + int k; + isl_int max_k; + unsigned total = isl_basic_map_total_dim(info->bmap); + + isl_int_init(max_k); + + for (k = 0; k < info->bmap->n_eq; ++k) { + if (info->eq[2 * k] == STATUS_VALID && + info->eq[2 * k + 1] == STATUS_VALID) + continue; + isl_seq_abs_max(info->bmap->eq[k] + 1, total, &max_k); + if (isl_int_abs_gt(max_k, wraps->max)) + isl_int_set(wraps->max, max_k); + } + + for (k = 0; k < info->bmap->n_ineq; ++k) { + if (info->ineq[k] == STATUS_VALID || + info->ineq[k] == STATUS_REDUNDANT) + continue; + isl_seq_abs_max(info->bmap->ineq[k] + 1, total, &max_k); + if (isl_int_abs_gt(max_k, wraps->max)) + isl_int_set(wraps->max, max_k); + } + + isl_int_clear(max_k); +} + +/* Initialize the isl_wraps data structure. + * If we want to bound the coefficients of the wrapping constraints, + * we set wraps->max to the largest coefficient + * in the equalities and inequalities that can be removed if we end up + * applying wrapping. + */ +static void wraps_init(struct isl_wraps *wraps, __isl_take isl_mat *mat, + struct isl_coalesce_info *info, int i, int j) +{ + isl_ctx *ctx; + + wraps->bound = 0; + wraps->mat = mat; + if (!mat) + return; + ctx = isl_mat_get_ctx(mat); + wraps->bound = isl_options_get_coalesce_bounded_wrapping(ctx); + if (!wraps->bound) + return; + isl_int_init(wraps->max); + isl_int_set_si(wraps->max, 0); + wraps_update_max(wraps, &info[i]); + wraps_update_max(wraps, &info[j]); +} + +/* Free the contents of the isl_wraps data structure. + */ +static void wraps_free(struct isl_wraps *wraps) +{ + isl_mat_free(wraps->mat); + if (wraps->bound) + isl_int_clear(wraps->max); +} + +/* Is the wrapping constraint in row "row" allowed? + * + * If wraps->bound is set, we check that none of the coefficients + * is greater than wraps->max. + */ +static int allow_wrap(struct isl_wraps *wraps, int row) +{ + int i; + + if (!wraps->bound) + return 1; + + for (i = 1; i < wraps->mat->n_col; ++i) + if (isl_int_abs_gt(wraps->mat->row[row][i], wraps->max)) + return 0; + + return 1; +} + +/* Wrap "ineq" (or its opposite if "negate" is set) around "bound" + * to include "set" and add the result in position "w" of "wraps". + * "len" is the total number of coefficients in "bound" and "ineq". + * Return 1 on success, 0 on failure and -1 on error. + * Wrapping can fail if the result of wrapping is equal to "bound" + * or if we want to bound the sizes of the coefficients and + * the wrapped constraint does not satisfy this bound. + */ +static int add_wrap(struct isl_wraps *wraps, int w, isl_int *bound, + isl_int *ineq, unsigned len, __isl_keep isl_set *set, int negate) +{ + isl_seq_cpy(wraps->mat->row[w], bound, len); + if (negate) { + isl_seq_neg(wraps->mat->row[w + 1], ineq, len); + ineq = wraps->mat->row[w + 1]; + } + if (!isl_set_wrap_facet(set, wraps->mat->row[w], ineq)) + return -1; + if (isl_seq_eq(wraps->mat->row[w], bound, len)) + return 0; + if (!allow_wrap(wraps, w)) + return 0; + return 1; +} + +/* For each constraint in info->bmap that is not redundant (as determined + * by info->tab) and that is not a valid constraint for the other basic map, + * wrap the constraint around "bound" such that it includes the whole + * set "set" and append the resulting constraint to "wraps". + * Note that the constraints that are valid for the other basic map + * will be added to the combined basic map by default, so there is + * no need to wrap them. + * The caller wrap_in_facets even relies on this function not wrapping + * any constraints that are already valid. + * "wraps" is assumed to have been pre-allocated to the appropriate size. + * wraps->n_row is the number of actual wrapped constraints that have + * been added. + * If any of the wrapping problems results in a constraint that is + * identical to "bound", then this means that "set" is unbounded in such + * way that no wrapping is possible. If this happens then wraps->n_row + * is reset to zero. + * Similarly, if we want to bound the coefficients of the wrapping + * constraints and a newly added wrapping constraint does not + * satisfy the bound, then wraps->n_row is also reset to zero. + */ +static int add_wraps(struct isl_wraps *wraps, struct isl_coalesce_info *info, + isl_int *bound, __isl_keep isl_set *set) +{ + int l, m; + int w; + int added; + isl_basic_map *bmap = info->bmap; + unsigned len = 1 + isl_basic_map_total_dim(bmap); + + w = wraps->mat->n_row; + + for (l = 0; l < bmap->n_ineq; ++l) { + if (info->ineq[l] == STATUS_VALID || + info->ineq[l] == STATUS_REDUNDANT) + continue; + if (isl_seq_is_neg(bound, bmap->ineq[l], len)) + continue; + if (isl_seq_eq(bound, bmap->ineq[l], len)) + continue; + if (isl_tab_is_redundant(info->tab, bmap->n_eq + l)) + continue; + + added = add_wrap(wraps, w, bound, bmap->ineq[l], len, set, 0); + if (added < 0) + return -1; + if (!added) + goto unbounded; + ++w; + } + for (l = 0; l < bmap->n_eq; ++l) { + if (isl_seq_is_neg(bound, bmap->eq[l], len)) + continue; + if (isl_seq_eq(bound, bmap->eq[l], len)) + continue; + + for (m = 0; m < 2; ++m) { + if (info->eq[2 * l + m] == STATUS_VALID) + continue; + added = add_wrap(wraps, w, bound, bmap->eq[l], len, + set, !m); + if (added < 0) + return -1; + if (!added) + goto unbounded; + ++w; + } + } + + wraps->mat->n_row = w; + return 0; +unbounded: + wraps->mat->n_row = 0; + return 0; +} + +/* Check if the constraints in "wraps" from "first" until the last + * are all valid for the basic set represented by "tab". + * If not, wraps->n_row is set to zero. + */ +static int check_wraps(__isl_keep isl_mat *wraps, int first, + struct isl_tab *tab) +{ + int i; + + for (i = first; i < wraps->n_row; ++i) { + enum isl_ineq_type type; + type = isl_tab_ineq_type(tab, wraps->row[i]); + if (type == isl_ineq_error) + return -1; + if (type == isl_ineq_redundant) + continue; + wraps->n_row = 0; + return 0; + } + + return 0; +} + +/* Return a set that corresponds to the non-redundant constraints + * (as recorded in tab) of bmap. + * + * It's important to remove the redundant constraints as some + * of the other constraints may have been modified after the + * constraints were marked redundant. + * In particular, a constraint may have been relaxed. + * Redundant constraints are ignored when a constraint is relaxed + * and should therefore continue to be ignored ever after. + * Otherwise, the relaxation might be thwarted by some of + * these constraints. + * + * Update the underlying set to ensure that the dimension doesn't change. + * Otherwise the integer divisions could get dropped if the tab + * turns out to be empty. + */ +static __isl_give isl_set *set_from_updated_bmap(__isl_keep isl_basic_map *bmap, + struct isl_tab *tab) +{ + isl_basic_set *bset; + + bmap = isl_basic_map_copy(bmap); + bset = isl_basic_map_underlying_set(bmap); + bset = isl_basic_set_cow(bset); + bset = isl_basic_set_update_from_tab(bset, tab); + return isl_set_from_basic_set(bset); +} + +/* Wrap the constraints of info->bmap that bound the facet defined + * by inequality "k" around (the opposite of) this inequality to + * include "set". "bound" may be used to store the negated inequality. + * Since the wrapped constraints are not guaranteed to contain the whole + * of info->bmap, we check them in check_wraps. + * If any of the wrapped constraints turn out to be invalid, then + * check_wraps will reset wrap->n_row to zero. + */ +static int add_wraps_around_facet(struct isl_wraps *wraps, + struct isl_coalesce_info *info, int k, isl_int *bound, + __isl_keep isl_set *set) +{ + struct isl_tab_undo *snap; + int n; + unsigned total = isl_basic_map_total_dim(info->bmap); + + snap = isl_tab_snap(info->tab); + + if (isl_tab_select_facet(info->tab, info->bmap->n_eq + k) < 0) + return -1; + if (isl_tab_detect_redundant(info->tab) < 0) + return -1; + + isl_seq_neg(bound, info->bmap->ineq[k], 1 + total); + + n = wraps->mat->n_row; + if (add_wraps(wraps, info, bound, set) < 0) + return -1; + + if (isl_tab_rollback(info->tab, snap) < 0) + return -1; + if (check_wraps(wraps->mat, n, info->tab) < 0) + return -1; + + return 0; +} + +/* Given a basic set i with a constraint k that is adjacent to + * basic set j, check if we can wrap + * both the facet corresponding to k (if "wrap_facet" is set) and basic map j + * (always) around their ridges to include the other set. + * If so, replace the pair of basic sets by their union. + * + * All constraints of i (except k) are assumed to be valid or + * cut constraints for j. + * Wrapping the cut constraints to include basic map j may result + * in constraints that are no longer valid of basic map i + * we have to check that the resulting wrapping constraints are valid for i. + * If "wrap_facet" is not set, then all constraints of i (except k) + * are assumed to be valid for j. + * ____ _____ + * / | / \ + * / || / | + * \ || => \ | + * \ || \ | + * \___|| \____| + * + */ +static enum isl_change can_wrap_in_facet(int i, int j, int k, + struct isl_coalesce_info *info, int wrap_facet) +{ + enum isl_change change = isl_change_none; + struct isl_wraps wraps; + isl_ctx *ctx; + isl_mat *mat; + struct isl_set *set_i = NULL; + struct isl_set *set_j = NULL; + struct isl_vec *bound = NULL; + unsigned total = isl_basic_map_total_dim(info[i].bmap); + + set_i = set_from_updated_bmap(info[i].bmap, info[i].tab); + set_j = set_from_updated_bmap(info[j].bmap, info[j].tab); + ctx = isl_basic_map_get_ctx(info[i].bmap); + mat = isl_mat_alloc(ctx, 2 * (info[i].bmap->n_eq + info[j].bmap->n_eq) + + info[i].bmap->n_ineq + info[j].bmap->n_ineq, + 1 + total); + wraps_init(&wraps, mat, info, i, j); + bound = isl_vec_alloc(ctx, 1 + total); + if (!set_i || !set_j || !wraps.mat || !bound) + goto error; + + isl_seq_cpy(bound->el, info[i].bmap->ineq[k], 1 + total); + isl_int_add_ui(bound->el[0], bound->el[0], 1); + + isl_seq_cpy(wraps.mat->row[0], bound->el, 1 + total); + wraps.mat->n_row = 1; + + if (add_wraps(&wraps, &info[j], bound->el, set_i) < 0) + goto error; + if (!wraps.mat->n_row) + goto unbounded; + + if (wrap_facet) { + if (add_wraps_around_facet(&wraps, &info[i], k, + bound->el, set_j) < 0) + goto error; + if (!wraps.mat->n_row) + goto unbounded; + } + + change = fuse(i, j, info, wraps.mat, 0, 0); + +unbounded: + wraps_free(&wraps); + + isl_set_free(set_i); + isl_set_free(set_j); + + isl_vec_free(bound); + + return change; +error: + wraps_free(&wraps); + isl_vec_free(bound); + isl_set_free(set_i); + isl_set_free(set_j); + return isl_change_error; +} + +/* Given a cut constraint t(x) >= 0 of basic map i, stored in row "w" + * of wrap.mat, replace it by its relaxed version t(x) + 1 >= 0, and + * add wrapping constraints to wrap.mat for all constraints + * of basic map j that bound the part of basic map j that sticks out + * of the cut constraint. + * "set_i" is the underlying set of basic map i. + * If any wrapping fails, then wraps->mat.n_row is reset to zero. + * + * In particular, we first intersect basic map j with t(x) + 1 = 0. + * If the result is empty, then t(x) >= 0 was actually a valid constraint + * (with respect to the integer points), so we add t(x) >= 0 instead. + * Otherwise, we wrap the constraints of basic map j that are not + * redundant in this intersection and that are not already valid + * for basic map i over basic map i. + * Note that it is sufficient to wrap the constraints to include + * basic map i, because we will only wrap the constraints that do + * not include basic map i already. The wrapped constraint will + * therefore be more relaxed compared to the original constraint. + * Since the original constraint is valid for basic map j, so is + * the wrapped constraint. + */ +static isl_stat wrap_in_facet(struct isl_wraps *wraps, int w, + struct isl_coalesce_info *info_j, __isl_keep isl_set *set_i, + struct isl_tab_undo *snap) +{ + isl_int_add_ui(wraps->mat->row[w][0], wraps->mat->row[w][0], 1); + if (isl_tab_add_eq(info_j->tab, wraps->mat->row[w]) < 0) + return isl_stat_error; + if (isl_tab_detect_redundant(info_j->tab) < 0) + return isl_stat_error; + + if (info_j->tab->empty) + isl_int_sub_ui(wraps->mat->row[w][0], wraps->mat->row[w][0], 1); + else if (add_wraps(wraps, info_j, wraps->mat->row[w], set_i) < 0) + return isl_stat_error; + + if (isl_tab_rollback(info_j->tab, snap) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Given a pair of basic maps i and j such that j sticks out + * of i at n cut constraints, each time by at most one, + * try to compute wrapping constraints and replace the two + * basic maps by a single basic map. + * The other constraints of i are assumed to be valid for j. + * "set_i" is the underlying set of basic map i. + * "wraps" has been initialized to be of the right size. + * + * For each cut constraint t(x) >= 0 of i, we add the relaxed version + * t(x) + 1 >= 0, along with wrapping constraints for all constraints + * of basic map j that bound the part of basic map j that sticks out + * of the cut constraint. + * + * If any wrapping fails, i.e., if we cannot wrap to touch + * the union, then we give up. + * Otherwise, the pair of basic maps is replaced by their union. + */ +static enum isl_change try_wrap_in_facets(int i, int j, + struct isl_coalesce_info *info, struct isl_wraps *wraps, + __isl_keep isl_set *set_i) +{ + int k, l, w; + unsigned total; + struct isl_tab_undo *snap; + + total = isl_basic_map_total_dim(info[i].bmap); + + snap = isl_tab_snap(info[j].tab); + + wraps->mat->n_row = 0; + + for (k = 0; k < info[i].bmap->n_eq; ++k) { + for (l = 0; l < 2; ++l) { + if (info[i].eq[2 * k + l] != STATUS_CUT) + continue; + w = wraps->mat->n_row++; + if (l == 0) + isl_seq_neg(wraps->mat->row[w], + info[i].bmap->eq[k], 1 + total); + else + isl_seq_cpy(wraps->mat->row[w], + info[i].bmap->eq[k], 1 + total); + if (wrap_in_facet(wraps, w, &info[j], set_i, snap) < 0) + return isl_change_error; + + if (!wraps->mat->n_row) + return isl_change_none; + } + } + + for (k = 0; k < info[i].bmap->n_ineq; ++k) { + if (info[i].ineq[k] != STATUS_CUT) + continue; + w = wraps->mat->n_row++; + isl_seq_cpy(wraps->mat->row[w], + info[i].bmap->ineq[k], 1 + total); + if (wrap_in_facet(wraps, w, &info[j], set_i, snap) < 0) + return isl_change_error; + + if (!wraps->mat->n_row) + return isl_change_none; + } + + return fuse(i, j, info, wraps->mat, 0, 1); +} + +/* Given a pair of basic maps i and j such that j sticks out + * of i at n cut constraints, each time by at most one, + * try to compute wrapping constraints and replace the two + * basic maps by a single basic map. + * The other constraints of i are assumed to be valid for j. + * + * The core computation is performed by try_wrap_in_facets. + * This function simply extracts an underlying set representation + * of basic map i and initializes the data structure for keeping + * track of wrapping constraints. + */ +static enum isl_change wrap_in_facets(int i, int j, int n, + struct isl_coalesce_info *info) +{ + enum isl_change change = isl_change_none; + struct isl_wraps wraps; + isl_ctx *ctx; + isl_mat *mat; + isl_set *set_i = NULL; + unsigned total = isl_basic_map_total_dim(info[i].bmap); + int max_wrap; + + if (isl_tab_extend_cons(info[j].tab, 1) < 0) + return isl_change_error; + + max_wrap = 1 + 2 * info[j].bmap->n_eq + info[j].bmap->n_ineq; + max_wrap *= n; + + set_i = set_from_updated_bmap(info[i].bmap, info[i].tab); + ctx = isl_basic_map_get_ctx(info[i].bmap); + mat = isl_mat_alloc(ctx, max_wrap, 1 + total); + wraps_init(&wraps, mat, info, i, j); + if (!set_i || !wraps.mat) + goto error; + + change = try_wrap_in_facets(i, j, info, &wraps, set_i); + + wraps_free(&wraps); + isl_set_free(set_i); + + return change; +error: + wraps_free(&wraps); + isl_set_free(set_i); + return isl_change_error; +} + +/* Return the effect of inequality "ineq" on the tableau "tab", + * after relaxing the constant term of "ineq" by one. + */ +static enum isl_ineq_type type_of_relaxed(struct isl_tab *tab, isl_int *ineq) +{ + enum isl_ineq_type type; + + isl_int_add_ui(ineq[0], ineq[0], 1); + type = isl_tab_ineq_type(tab, ineq); + isl_int_sub_ui(ineq[0], ineq[0], 1); + + return type; +} + +/* Given two basic sets i and j, + * check if relaxing all the cut constraints of i by one turns + * them into valid constraint for j and check if we can wrap in + * the bits that are sticking out. + * If so, replace the pair by their union. + * + * We first check if all relaxed cut inequalities of i are valid for j + * and then try to wrap in the intersections of the relaxed cut inequalities + * with j. + * + * During this wrapping, we consider the points of j that lie at a distance + * of exactly 1 from i. In particular, we ignore the points that lie in + * between this lower-dimensional space and the basic map i. + * We can therefore only apply this to integer maps. + * ____ _____ + * / ___|_ / \ + * / | | / | + * \ | | => \ | + * \|____| \ | + * \___| \____/ + * + * _____ ______ + * | ____|_ | \ + * | | | | | + * | | | => | | + * |_| | | | + * |_____| \______| + * + * _______ + * | | + * | |\ | + * | | \ | + * | | \ | + * | | \| + * | | \ + * | |_____\ + * | | + * |_______| + * + * Wrapping can fail if the result of wrapping one of the facets + * around its edges does not produce any new facet constraint. + * In particular, this happens when we try to wrap in unbounded sets. + * + * _______________________________________________________________________ + * | + * | ___ + * | | | + * |_| |_________________________________________________________________ + * |___| + * + * The following is not an acceptable result of coalescing the above two + * sets as it includes extra integer points. + * _______________________________________________________________________ + * | + * | + * | + * | + * \______________________________________________________________________ + */ +static enum isl_change can_wrap_in_set(int i, int j, + struct isl_coalesce_info *info) +{ + int k, l; + int n; + unsigned total; + + if (ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_RATIONAL) || + ISL_F_ISSET(info[j].bmap, ISL_BASIC_MAP_RATIONAL)) + return isl_change_none; + + n = count(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_CUT); + n += count(info[i].ineq, info[i].bmap->n_ineq, STATUS_CUT); + if (n == 0) + return isl_change_none; + + total = isl_basic_map_total_dim(info[i].bmap); + for (k = 0; k < info[i].bmap->n_eq; ++k) { + for (l = 0; l < 2; ++l) { + enum isl_ineq_type type; + + if (info[i].eq[2 * k + l] != STATUS_CUT) + continue; + + if (l == 0) + isl_seq_neg(info[i].bmap->eq[k], + info[i].bmap->eq[k], 1 + total); + type = type_of_relaxed(info[j].tab, + info[i].bmap->eq[k]); + if (l == 0) + isl_seq_neg(info[i].bmap->eq[k], + info[i].bmap->eq[k], 1 + total); + if (type == isl_ineq_error) + return isl_change_error; + if (type != isl_ineq_redundant) + return isl_change_none; + } + } + + for (k = 0; k < info[i].bmap->n_ineq; ++k) { + enum isl_ineq_type type; + + if (info[i].ineq[k] != STATUS_CUT) + continue; + + type = type_of_relaxed(info[j].tab, info[i].bmap->ineq[k]); + if (type == isl_ineq_error) + return isl_change_error; + if (type != isl_ineq_redundant) + return isl_change_none; + } + + return wrap_in_facets(i, j, n, info); +} + +/* Check if either i or j has only cut constraints that can + * be used to wrap in (a facet of) the other basic set. + * if so, replace the pair by their union. + */ +static enum isl_change check_wrap(int i, int j, struct isl_coalesce_info *info) +{ + enum isl_change change = isl_change_none; + + change = can_wrap_in_set(i, j, info); + if (change != isl_change_none) + return change; + + change = can_wrap_in_set(j, i, info); + return change; +} + +/* At least one of the basic maps has an equality that is adjacent + * to inequality. Make sure that only one of the basic maps has + * such an equality and that the other basic map has exactly one + * inequality adjacent to an equality. + * If the other basic map does not have such an inequality, then + * check if all its constraints are either valid or cut constraints + * and, if so, try wrapping in the first map into the second. + * + * We call the basic map that has the inequality "i" and the basic + * map that has the equality "j". + * If "i" has any "cut" (in)equality, then relaxing the inequality + * by one would not result in a basic map that contains the other + * basic map. However, it may still be possible to wrap in the other + * basic map. + */ +static enum isl_change check_adj_eq(int i, int j, + struct isl_coalesce_info *info) +{ + enum isl_change change = isl_change_none; + int k; + int any_cut; + + if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_ADJ_INEQ) && + any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_ADJ_INEQ)) + /* ADJ EQ TOO MANY */ + return isl_change_none; + + if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_ADJ_INEQ)) + return check_adj_eq(j, i, info); + + /* j has an equality adjacent to an inequality in i */ + + if (count(info[i].ineq, info[i].bmap->n_ineq, STATUS_ADJ_EQ) != 1) { + if (all_valid_or_cut(&info[i])) + return can_wrap_in_set(i, j, info); + return isl_change_none; + } + if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_CUT)) + return isl_change_none; + any_cut = any(info[i].ineq, info[i].bmap->n_ineq, STATUS_CUT); + if (any(info[j].ineq, info[j].bmap->n_ineq, STATUS_ADJ_EQ) || + any(info[i].ineq, info[i].bmap->n_ineq, STATUS_ADJ_INEQ) || + any(info[j].ineq, info[j].bmap->n_ineq, STATUS_ADJ_INEQ)) + /* ADJ EQ TOO MANY */ + return isl_change_none; + + for (k = 0; k < info[i].bmap->n_ineq; ++k) + if (info[i].ineq[k] == STATUS_ADJ_EQ) + break; + + if (!any_cut) { + change = is_adj_eq_extension(i, j, k, info); + if (change != isl_change_none) + return change; + } + + change = can_wrap_in_facet(i, j, k, info, any_cut); + + return change; +} + +/* The two basic maps lie on adjacent hyperplanes. In particular, + * basic map "i" has an equality that lies parallel to basic map "j". + * Check if we can wrap the facets around the parallel hyperplanes + * to include the other set. + * + * We perform basically the same operations as can_wrap_in_facet, + * except that we don't need to select a facet of one of the sets. + * _ + * \\ \\ + * \\ => \\ + * \ \| + * + * If there is more than one equality of "i" adjacent to an equality of "j", + * then the result will satisfy one or more equalities that are a linear + * combination of these equalities. These will be encoded as pairs + * of inequalities in the wrapping constraints and need to be made + * explicit. + */ +static enum isl_change check_eq_adj_eq(int i, int j, + struct isl_coalesce_info *info) +{ + int k; + enum isl_change change = isl_change_none; + int detect_equalities = 0; + struct isl_wraps wraps; + isl_ctx *ctx; + isl_mat *mat; + struct isl_set *set_i = NULL; + struct isl_set *set_j = NULL; + struct isl_vec *bound = NULL; + unsigned total = isl_basic_map_total_dim(info[i].bmap); + + if (count(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_ADJ_EQ) != 1) + detect_equalities = 1; + + for (k = 0; k < 2 * info[i].bmap->n_eq ; ++k) + if (info[i].eq[k] == STATUS_ADJ_EQ) + break; + + set_i = set_from_updated_bmap(info[i].bmap, info[i].tab); + set_j = set_from_updated_bmap(info[j].bmap, info[j].tab); + ctx = isl_basic_map_get_ctx(info[i].bmap); + mat = isl_mat_alloc(ctx, 2 * (info[i].bmap->n_eq + info[j].bmap->n_eq) + + info[i].bmap->n_ineq + info[j].bmap->n_ineq, + 1 + total); + wraps_init(&wraps, mat, info, i, j); + bound = isl_vec_alloc(ctx, 1 + total); + if (!set_i || !set_j || !wraps.mat || !bound) + goto error; + + if (k % 2 == 0) + isl_seq_neg(bound->el, info[i].bmap->eq[k / 2], 1 + total); + else + isl_seq_cpy(bound->el, info[i].bmap->eq[k / 2], 1 + total); + isl_int_add_ui(bound->el[0], bound->el[0], 1); + + isl_seq_cpy(wraps.mat->row[0], bound->el, 1 + total); + wraps.mat->n_row = 1; + + if (add_wraps(&wraps, &info[j], bound->el, set_i) < 0) + goto error; + if (!wraps.mat->n_row) + goto unbounded; + + isl_int_sub_ui(bound->el[0], bound->el[0], 1); + isl_seq_neg(bound->el, bound->el, 1 + total); + + isl_seq_cpy(wraps.mat->row[wraps.mat->n_row], bound->el, 1 + total); + wraps.mat->n_row++; + + if (add_wraps(&wraps, &info[i], bound->el, set_j) < 0) + goto error; + if (!wraps.mat->n_row) + goto unbounded; + + change = fuse(i, j, info, wraps.mat, detect_equalities, 0); + + if (0) { +error: change = isl_change_error; + } +unbounded: + + wraps_free(&wraps); + isl_set_free(set_i); + isl_set_free(set_j); + isl_vec_free(bound); + + return change; +} + +/* Initialize the "eq" and "ineq" fields of "info". + */ +static void init_status(struct isl_coalesce_info *info) +{ + info->eq = info->ineq = NULL; +} + +/* Set info->eq to the positions of the equalities of info->bmap + * with respect to the basic map represented by "tab". + * If info->eq has already been computed, then do not compute it again. + */ +static void set_eq_status_in(struct isl_coalesce_info *info, + struct isl_tab *tab) +{ + if (info->eq) + return; + info->eq = eq_status_in(info->bmap, tab); +} + +/* Set info->ineq to the positions of the inequalities of info->bmap + * with respect to the basic map represented by "tab". + * If info->ineq has already been computed, then do not compute it again. + */ +static void set_ineq_status_in(struct isl_coalesce_info *info, + struct isl_tab *tab) +{ + if (info->ineq) + return; + info->ineq = ineq_status_in(info->bmap, info->tab, tab); +} + +/* Free the memory allocated by the "eq" and "ineq" fields of "info". + * This function assumes that init_status has been called on "info" first, + * after which the "eq" and "ineq" fields may or may not have been + * assigned a newly allocated array. + */ +static void clear_status(struct isl_coalesce_info *info) +{ + free(info->eq); + free(info->ineq); +} + +/* Check if the union of the given pair of basic maps + * can be represented by a single basic map. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * The two basic maps are assumed to live in the same local space. + * The "eq" and "ineq" fields of info[i] and info[j] are assumed + * to have been initialized by the caller, either to NULL or + * to valid information. + * + * We first check the effect of each constraint of one basic map + * on the other basic map. + * The constraint may be + * redundant the constraint is redundant in its own + * basic map and should be ignore and removed + * in the end + * valid all (integer) points of the other basic map + * satisfy the constraint + * separate no (integer) point of the other basic map + * satisfies the constraint + * cut some but not all points of the other basic map + * satisfy the constraint + * adj_eq the given constraint is adjacent (on the outside) + * to an equality of the other basic map + * adj_ineq the given constraint is adjacent (on the outside) + * to an inequality of the other basic map + * + * We consider seven cases in which we can replace the pair by a single + * basic map. We ignore all "redundant" constraints. + * + * 1. all constraints of one basic map are valid + * => the other basic map is a subset and can be removed + * + * 2. all constraints of both basic maps are either "valid" or "cut" + * and the facets corresponding to the "cut" constraints + * of one of the basic maps lies entirely inside the other basic map + * => the pair can be replaced by a basic map consisting + * of the valid constraints in both basic maps + * + * 3. there is a single pair of adjacent inequalities + * (all other constraints are "valid") + * => the pair can be replaced by a basic map consisting + * of the valid constraints in both basic maps + * + * 4. one basic map has a single adjacent inequality, while the other + * constraints are "valid". The other basic map has some + * "cut" constraints, but replacing the adjacent inequality by + * its opposite and adding the valid constraints of the other + * basic map results in a subset of the other basic map + * => the pair can be replaced by a basic map consisting + * of the valid constraints in both basic maps + * + * 5. there is a single adjacent pair of an inequality and an equality, + * the other constraints of the basic map containing the inequality are + * "valid". Moreover, if the inequality the basic map is relaxed + * and then turned into an equality, then resulting facet lies + * entirely inside the other basic map + * => the pair can be replaced by the basic map containing + * the inequality, with the inequality relaxed. + * + * 6. there is a single adjacent pair of an inequality and an equality, + * the other constraints of the basic map containing the inequality are + * "valid". Moreover, the facets corresponding to both + * the inequality and the equality can be wrapped around their + * ridges to include the other basic map + * => the pair can be replaced by a basic map consisting + * of the valid constraints in both basic maps together + * with all wrapping constraints + * + * 7. one of the basic maps extends beyond the other by at most one. + * Moreover, the facets corresponding to the cut constraints and + * the pieces of the other basic map at offset one from these cut + * constraints can be wrapped around their ridges to include + * the union of the two basic maps + * => the pair can be replaced by a basic map consisting + * of the valid constraints in both basic maps together + * with all wrapping constraints + * + * 8. the two basic maps live in adjacent hyperplanes. In principle + * such sets can always be combined through wrapping, but we impose + * that there is only one such pair, to avoid overeager coalescing. + * + * Throughout the computation, we maintain a collection of tableaus + * corresponding to the basic maps. When the basic maps are dropped + * or combined, the tableaus are modified accordingly. + */ +static enum isl_change coalesce_local_pair_reuse(int i, int j, + struct isl_coalesce_info *info) +{ + enum isl_change change = isl_change_none; + + set_eq_status_in(&info[i], info[j].tab); + if (info[i].bmap->n_eq && !info[i].eq) + goto error; + if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_ERROR)) + goto error; + if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_SEPARATE)) + goto done; + + set_eq_status_in(&info[j], info[i].tab); + if (info[j].bmap->n_eq && !info[j].eq) + goto error; + if (any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_ERROR)) + goto error; + if (any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_SEPARATE)) + goto done; + + set_ineq_status_in(&info[i], info[j].tab); + if (info[i].bmap->n_ineq && !info[i].ineq) + goto error; + if (any(info[i].ineq, info[i].bmap->n_ineq, STATUS_ERROR)) + goto error; + if (any(info[i].ineq, info[i].bmap->n_ineq, STATUS_SEPARATE)) + goto done; + + set_ineq_status_in(&info[j], info[i].tab); + if (info[j].bmap->n_ineq && !info[j].ineq) + goto error; + if (any(info[j].ineq, info[j].bmap->n_ineq, STATUS_ERROR)) + goto error; + if (any(info[j].ineq, info[j].bmap->n_ineq, STATUS_SEPARATE)) + goto done; + + if (all(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_VALID) && + all(info[i].ineq, info[i].bmap->n_ineq, STATUS_VALID)) { + drop(&info[j]); + change = isl_change_drop_second; + } else if (all(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_VALID) && + all(info[j].ineq, info[j].bmap->n_ineq, STATUS_VALID)) { + drop(&info[i]); + change = isl_change_drop_first; + } else if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_ADJ_EQ)) { + change = check_eq_adj_eq(i, j, info); + } else if (any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_ADJ_EQ)) { + change = check_eq_adj_eq(j, i, info); + } else if (any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_ADJ_INEQ) || + any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_ADJ_INEQ)) { + change = check_adj_eq(i, j, info); + } else if (any(info[i].ineq, info[i].bmap->n_ineq, STATUS_ADJ_EQ) || + any(info[j].ineq, info[j].bmap->n_ineq, STATUS_ADJ_EQ)) { + /* Can't happen */ + /* BAD ADJ INEQ */ + } else if (any(info[i].ineq, info[i].bmap->n_ineq, STATUS_ADJ_INEQ) || + any(info[j].ineq, info[j].bmap->n_ineq, STATUS_ADJ_INEQ)) { + change = check_adj_ineq(i, j, info); + } else { + if (!any(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_CUT) && + !any(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_CUT)) + change = check_facets(i, j, info); + if (change == isl_change_none) + change = check_wrap(i, j, info); + } + +done: + clear_status(&info[i]); + clear_status(&info[j]); + return change; +error: + clear_status(&info[i]); + clear_status(&info[j]); + return isl_change_error; +} + +/* Check if the union of the given pair of basic maps + * can be represented by a single basic map. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * The two basic maps are assumed to live in the same local space. + */ +static enum isl_change coalesce_local_pair(int i, int j, + struct isl_coalesce_info *info) +{ + init_status(&info[i]); + init_status(&info[j]); + return coalesce_local_pair_reuse(i, j, info); +} + +/* Shift the integer division at position "div" of the basic map + * represented by "info" by "shift". + * + * That is, if the integer division has the form + * + * floor(f(x)/d) + * + * then replace it by + * + * floor((f(x) + shift * d)/d) - shift + */ +static int shift_div(struct isl_coalesce_info *info, int div, isl_int shift) +{ + unsigned total; + + info->bmap = isl_basic_map_shift_div(info->bmap, div, 0, shift); + if (!info->bmap) + return -1; + + total = isl_basic_map_dim(info->bmap, isl_dim_all); + total -= isl_basic_map_dim(info->bmap, isl_dim_div); + if (isl_tab_shift_var(info->tab, total + div, shift) < 0) + return -1; + + return 0; +} + +/* Check if some of the divs in the basic map represented by "info1" + * are shifts of the corresponding divs in the basic map represented + * by "info2". If so, align them with those of "info2". + * Only do this if "info1" and "info2" have the same number + * of integer divisions. + * + * An integer division is considered to be a shift of another integer + * division if one is equal to the other plus a constant. + * + * In particular, for each pair of integer divisions, if both are known, + * have identical coefficients (apart from the constant term) and + * if the difference between the constant terms (taking into account + * the denominator) is an integer, then move the difference outside. + * That is, if one integer division is of the form + * + * floor((f(x) + c_1)/d) + * + * while the other is of the form + * + * floor((f(x) + c_2)/d) + * + * and n = (c_2 - c_1)/d is an integer, then replace the first + * integer division by + * + * floor((f(x) + c_1 + n * d)/d) - n = floor((f(x) + c_2)/d) - n + */ +static int harmonize_divs(struct isl_coalesce_info *info1, + struct isl_coalesce_info *info2) +{ + int i; + int total; + + if (!info1->bmap || !info2->bmap) + return -1; + + if (info1->bmap->n_div != info2->bmap->n_div) + return 0; + if (info1->bmap->n_div == 0) + return 0; + + total = isl_basic_map_total_dim(info1->bmap); + for (i = 0; i < info1->bmap->n_div; ++i) { + isl_int d; + int r = 0; + + if (isl_int_is_zero(info1->bmap->div[i][0]) || + isl_int_is_zero(info2->bmap->div[i][0])) + continue; + if (isl_int_ne(info1->bmap->div[i][0], info2->bmap->div[i][0])) + continue; + if (isl_int_eq(info1->bmap->div[i][1], info2->bmap->div[i][1])) + continue; + if (!isl_seq_eq(info1->bmap->div[i] + 2, + info2->bmap->div[i] + 2, total)) + continue; + isl_int_init(d); + isl_int_sub(d, info2->bmap->div[i][1], info1->bmap->div[i][1]); + if (isl_int_is_divisible_by(d, info1->bmap->div[i][0])) { + isl_int_divexact(d, d, info1->bmap->div[i][0]); + r = shift_div(info1, i, d); + } + isl_int_clear(d); + if (r < 0) + return -1; + } + + return 0; +} + +/* Do the two basic maps live in the same local space, i.e., + * do they have the same (known) divs? + * If either basic map has any unknown divs, then we can only assume + * that they do not live in the same local space. + */ +static int same_divs(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2) +{ + int i; + int known; + int total; + + if (!bmap1 || !bmap2) + return -1; + if (bmap1->n_div != bmap2->n_div) + return 0; + + if (bmap1->n_div == 0) + return 1; + + known = isl_basic_map_divs_known(bmap1); + if (known < 0 || !known) + return known; + known = isl_basic_map_divs_known(bmap2); + if (known < 0 || !known) + return known; + + total = isl_basic_map_total_dim(bmap1); + for (i = 0; i < bmap1->n_div; ++i) + if (!isl_seq_eq(bmap1->div[i], bmap2->div[i], 2 + total)) + return 0; + + return 1; +} + +/* Expand info->tab in the same way info->bmap was expanded in + * isl_basic_map_expand_divs using the expansion "exp" and + * update info->ineq with respect to the redundant constraints + * in the resulting tableau. + * In particular, introduce extra variables corresponding + * to the extra integer divisions and add the div constraints + * that were added to info->bmap after info->tab was created + * from the original info->bmap. + * info->ineq was computed without a tableau and therefore + * does not take into account the redundant constraints + * in the tableau. Mark them here. + */ +static isl_stat expand_tab(struct isl_coalesce_info *info, int *exp) +{ + unsigned total, pos, n_div; + int extra_var; + int i, n, j, n_ineq; + unsigned n_eq; + + total = isl_basic_map_dim(info->bmap, isl_dim_all); + n_div = isl_basic_map_dim(info->bmap, isl_dim_div); + pos = total - n_div; + extra_var = total - info->tab->n_var; + n = n_div - extra_var; + + if (isl_tab_extend_vars(info->tab, extra_var) < 0) + return isl_stat_error; + if (isl_tab_extend_cons(info->tab, 2 * extra_var) < 0) + return isl_stat_error; + + i = 0; + for (j = 0; j < n_div; ++j) { + if (i < n && exp[i] == j) { + ++i; + continue; + } + if (isl_tab_insert_var(info->tab, pos + j) < 0) + return isl_stat_error; + } + + n_ineq = info->tab->n_con - info->tab->n_eq; + for (i = n_ineq; i < info->bmap->n_ineq; ++i) + if (isl_tab_add_ineq(info->tab, info->bmap->ineq[i]) < 0) + return isl_stat_error; + + n_eq = info->bmap->n_eq; + for (i = 0; i < info->bmap->n_ineq; ++i) { + if (isl_tab_is_redundant(info->tab, n_eq + i)) + info->ineq[i] = STATUS_REDUNDANT; + } + + return isl_stat_ok; +} + +/* Check if the union of the basic maps represented by info[i] and info[j] + * can be represented by a single basic map, + * after expanding the divs of info[i] to match those of info[j]. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * + * The caller has already checked for info[j] being a subset of info[i]. + * If some of the divs of info[j] are unknown, then the expanded info[i] + * will not have the corresponding div constraints. The other patterns + * therefore cannot apply. Skip the computation in this case. + * + * The expansion is performed using the divs "div" and expansion "exp" + * computed by the caller. + * info[i].bmap has already been expanded and the result is passed in + * as "bmap". + * The "eq" and "ineq" fields of info[i] reflect the status of + * the constraints of the expanded "bmap" with respect to info[j].tab. + * However, inequality constraints that are redundant in info[i].tab + * have not yet been marked as such because no tableau was available. + * + * Replace info[i].bmap by "bmap" and expand info[i].tab as well, + * updating info[i].ineq with respect to the redundant constraints. + * Then try and coalesce the expanded info[i] with info[j], + * reusing the information in info[i].eq and info[i].ineq. + * If this does not result in any coalescing or if it results in info[j] + * getting dropped (which should not happen in practice, since the case + * of info[j] being a subset of info[i] has already been checked by + * the caller), then revert info[i] to its original state. + */ +static enum isl_change coalesce_expand_tab_divs(__isl_take isl_basic_map *bmap, + int i, int j, struct isl_coalesce_info *info, __isl_keep isl_mat *div, + int *exp) +{ + isl_bool known; + isl_basic_map *bmap_i; + struct isl_tab_undo *snap; + enum isl_change change = isl_change_none; + + known = isl_basic_map_divs_known(info[j].bmap); + if (known < 0 || !known) { + clear_status(&info[i]); + isl_basic_map_free(bmap); + return known < 0 ? isl_change_error : isl_change_none; + } + + bmap_i = info[i].bmap; + info[i].bmap = isl_basic_map_copy(bmap); + snap = isl_tab_snap(info[i].tab); + if (!info[i].bmap || expand_tab(&info[i], exp) < 0) + change = isl_change_error; + + init_status(&info[j]); + if (change == isl_change_none) + change = coalesce_local_pair_reuse(i, j, info); + else + clear_status(&info[i]); + if (change != isl_change_none && change != isl_change_drop_second) { + isl_basic_map_free(bmap_i); + } else { + isl_basic_map_free(info[i].bmap); + info[i].bmap = bmap_i; + + if (isl_tab_rollback(info[i].tab, snap) < 0) + change = isl_change_error; + } + + isl_basic_map_free(bmap); + return change; +} + +/* Check if the union of "bmap" and the basic map represented by info[j] + * can be represented by a single basic map, + * after expanding the divs of "bmap" to match those of info[j]. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * + * In particular, check if the expanded "bmap" contains the basic map + * represented by the tableau info[j].tab. + * The expansion is performed using the divs "div" and expansion "exp" + * computed by the caller. + * Then we check if all constraints of the expanded "bmap" are valid for + * info[j].tab. + * + * If "i" is not equal to -1, then "bmap" is equal to info[i].bmap. + * In this case, the positions of the constraints of info[i].bmap + * with respect to the basic map represented by info[j] are stored + * in info[i]. + * + * If the expanded "bmap" does not contain the basic map + * represented by the tableau info[j].tab and if "i" is not -1, + * i.e., if the original "bmap" is info[i].bmap, then expand info[i].tab + * as well and check if that results in coalescing. + */ +static enum isl_change coalesce_with_expanded_divs( + __isl_keep isl_basic_map *bmap, int i, int j, + struct isl_coalesce_info *info, __isl_keep isl_mat *div, int *exp) +{ + enum isl_change change = isl_change_none; + struct isl_coalesce_info info_local, *info_i; + + info_i = i >= 0 ? &info[i] : &info_local; + init_status(info_i); + bmap = isl_basic_map_copy(bmap); + bmap = isl_basic_map_expand_divs(bmap, isl_mat_copy(div), exp); + + if (!bmap) + goto error; + + info_i->eq = eq_status_in(bmap, info[j].tab); + if (bmap->n_eq && !info_i->eq) + goto error; + if (any(info_i->eq, 2 * bmap->n_eq, STATUS_ERROR)) + goto error; + if (any(info_i->eq, 2 * bmap->n_eq, STATUS_SEPARATE)) + goto done; + + info_i->ineq = ineq_status_in(bmap, NULL, info[j].tab); + if (bmap->n_ineq && !info_i->ineq) + goto error; + if (any(info_i->ineq, bmap->n_ineq, STATUS_ERROR)) + goto error; + if (any(info_i->ineq, bmap->n_ineq, STATUS_SEPARATE)) + goto done; + + if (all(info_i->eq, 2 * bmap->n_eq, STATUS_VALID) && + all(info_i->ineq, bmap->n_ineq, STATUS_VALID)) { + drop(&info[j]); + change = isl_change_drop_second; + } + + if (change == isl_change_none && i != -1) + return coalesce_expand_tab_divs(bmap, i, j, info, div, exp); + +done: + isl_basic_map_free(bmap); + clear_status(info_i); + return change; +error: + isl_basic_map_free(bmap); + clear_status(info_i); + return isl_change_error; +} + +/* Check if the union of "bmap_i" and the basic map represented by info[j] + * can be represented by a single basic map, + * after aligning the divs of "bmap_i" to match those of info[j]. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * + * In particular, check if "bmap_i" contains the basic map represented by + * info[j] after aligning the divs of "bmap_i" to those of info[j]. + * Note that this can only succeed if the number of divs of "bmap_i" + * is smaller than (or equal to) the number of divs of info[j]. + * + * We first check if the divs of "bmap_i" are all known and form a subset + * of those of info[j].bmap. If so, we pass control over to + * coalesce_with_expanded_divs. + * + * If "i" is not equal to -1, then "bmap" is equal to info[i].bmap. + */ +static enum isl_change coalesce_after_aligning_divs( + __isl_keep isl_basic_map *bmap_i, int i, int j, + struct isl_coalesce_info *info) +{ + int known; + isl_mat *div_i, *div_j, *div; + int *exp1 = NULL; + int *exp2 = NULL; + isl_ctx *ctx; + enum isl_change change; + + known = isl_basic_map_divs_known(bmap_i); + if (known < 0 || !known) + return known; + + ctx = isl_basic_map_get_ctx(bmap_i); + + div_i = isl_basic_map_get_divs(bmap_i); + div_j = isl_basic_map_get_divs(info[j].bmap); + + if (!div_i || !div_j) + goto error; + + exp1 = isl_alloc_array(ctx, int, div_i->n_row); + exp2 = isl_alloc_array(ctx, int, div_j->n_row); + if ((div_i->n_row && !exp1) || (div_j->n_row && !exp2)) + goto error; + + div = isl_merge_divs(div_i, div_j, exp1, exp2); + if (!div) + goto error; + + if (div->n_row == div_j->n_row) + change = coalesce_with_expanded_divs(bmap_i, + i, j, info, div, exp1); + else + change = isl_change_none; + + isl_mat_free(div); + + isl_mat_free(div_i); + isl_mat_free(div_j); + + free(exp2); + free(exp1); + + return change; +error: + isl_mat_free(div_i); + isl_mat_free(div_j); + free(exp1); + free(exp2); + return isl_change_error; +} + +/* Check if basic map "j" is a subset of basic map "i" after + * exploiting the extra equalities of "j" to simplify the divs of "i". + * If so, remove basic map "j" and return isl_change_drop_second. + * + * If "j" does not have any equalities or if they are the same + * as those of "i", then we cannot exploit them to simplify the divs. + * Similarly, if there are no divs in "i", then they cannot be simplified. + * If, on the other hand, the affine hulls of "i" and "j" do not intersect, + * then "j" cannot be a subset of "i". + * + * Otherwise, we intersect "i" with the affine hull of "j" and then + * check if "j" is a subset of the result after aligning the divs. + * If so, then "j" is definitely a subset of "i" and can be removed. + * Note that if after intersection with the affine hull of "j". + * "i" still has more divs than "j", then there is no way we can + * align the divs of "i" to those of "j". + */ +static enum isl_change coalesce_subset_with_equalities(int i, int j, + struct isl_coalesce_info *info) +{ + isl_basic_map *hull_i, *hull_j, *bmap_i; + int equal, empty; + enum isl_change change; + + if (info[j].bmap->n_eq == 0) + return isl_change_none; + if (info[i].bmap->n_div == 0) + return isl_change_none; + + hull_i = isl_basic_map_copy(info[i].bmap); + hull_i = isl_basic_map_plain_affine_hull(hull_i); + hull_j = isl_basic_map_copy(info[j].bmap); + hull_j = isl_basic_map_plain_affine_hull(hull_j); + + hull_j = isl_basic_map_intersect(hull_j, isl_basic_map_copy(hull_i)); + equal = isl_basic_map_plain_is_equal(hull_i, hull_j); + empty = isl_basic_map_plain_is_empty(hull_j); + isl_basic_map_free(hull_i); + + if (equal < 0 || equal || empty < 0 || empty) { + isl_basic_map_free(hull_j); + if (equal < 0 || empty < 0) + return isl_change_error; + return isl_change_none; + } + + bmap_i = isl_basic_map_copy(info[i].bmap); + bmap_i = isl_basic_map_intersect(bmap_i, hull_j); + if (!bmap_i) + return isl_change_error; + + if (bmap_i->n_div > info[j].bmap->n_div) { + isl_basic_map_free(bmap_i); + return isl_change_none; + } + + change = coalesce_after_aligning_divs(bmap_i, -1, j, info); + + isl_basic_map_free(bmap_i); + + return change; +} + +/* Check if the union of and the basic maps represented by info[i] and info[j] + * can be represented by a single basic map, by aligning or equating + * their integer divisions. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * + * Note that we only perform any test if the number of divs is different + * in the two basic maps. In case the number of divs is the same, + * we have already established that the divs are different + * in the two basic maps. + * In particular, if the number of divs of basic map i is smaller than + * the number of divs of basic map j, then we check if j is a subset of i + * and vice versa. + */ +static enum isl_change coalesce_divs(int i, int j, + struct isl_coalesce_info *info) +{ + enum isl_change change = isl_change_none; + + if (info[i].bmap->n_div < info[j].bmap->n_div) + change = coalesce_after_aligning_divs(info[i].bmap, i, j, info); + if (change != isl_change_none) + return change; + + if (info[j].bmap->n_div < info[i].bmap->n_div) + change = coalesce_after_aligning_divs(info[j].bmap, j, i, info); + if (change != isl_change_none) + return invert_change(change); + + change = coalesce_subset_with_equalities(i, j, info); + if (change != isl_change_none) + return change; + + change = coalesce_subset_with_equalities(j, i, info); + if (change != isl_change_none) + return invert_change(change); + + return isl_change_none; +} + +/* Does "bmap" involve any divs that themselves refer to divs? + */ +static int has_nested_div(__isl_keep isl_basic_map *bmap) +{ + int i; + unsigned total; + unsigned n_div; + + total = isl_basic_map_dim(bmap, isl_dim_all); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + total -= n_div; + + for (i = 0; i < n_div; ++i) + if (isl_seq_first_non_zero(bmap->div[i] + 2 + total, + n_div) != -1) + return 1; + + return 0; +} + +/* Return a list of affine expressions, one for each integer division + * in "bmap_i". For each integer division that also appears in "bmap_j", + * the affine expression is set to NaN. The number of NaNs in the list + * is equal to the number of integer divisions in "bmap_j". + * For the other integer divisions of "bmap_i", the corresponding + * element in the list is a purely affine expression equal to the integer + * division in "hull". + * If no such list can be constructed, then the number of elements + * in the returned list is smaller than the number of integer divisions + * in "bmap_i". + */ +static __isl_give isl_aff_list *set_up_substitutions( + __isl_keep isl_basic_map *bmap_i, __isl_keep isl_basic_map *bmap_j, + __isl_take isl_basic_map *hull) +{ + unsigned n_div_i, n_div_j, total; + isl_ctx *ctx; + isl_local_space *ls; + isl_basic_set *wrap_hull; + isl_aff *aff_nan; + isl_aff_list *list; + int i, j; + + if (!hull) + return NULL; + + ctx = isl_basic_map_get_ctx(hull); + + n_div_i = isl_basic_map_dim(bmap_i, isl_dim_div); + n_div_j = isl_basic_map_dim(bmap_j, isl_dim_div); + total = isl_basic_map_total_dim(bmap_i) - n_div_i; + + ls = isl_basic_map_get_local_space(bmap_i); + ls = isl_local_space_wrap(ls); + wrap_hull = isl_basic_map_wrap(hull); + + aff_nan = isl_aff_nan_on_domain(isl_local_space_copy(ls)); + list = isl_aff_list_alloc(ctx, n_div_i); + + j = 0; + for (i = 0; i < n_div_i; ++i) { + isl_aff *aff; + + if (j < n_div_j && + isl_seq_eq(bmap_i->div[i], bmap_j->div[j], 2 + total)) { + ++j; + list = isl_aff_list_add(list, isl_aff_copy(aff_nan)); + continue; + } + if (n_div_i - i <= n_div_j - j) + break; + + aff = isl_local_space_get_div(ls, i); + aff = isl_aff_substitute_equalities(aff, + isl_basic_set_copy(wrap_hull)); + aff = isl_aff_floor(aff); + if (!aff) + goto error; + if (isl_aff_dim(aff, isl_dim_div) != 0) { + isl_aff_free(aff); + break; + } + + list = isl_aff_list_add(list, aff); + } + + isl_aff_free(aff_nan); + isl_local_space_free(ls); + isl_basic_set_free(wrap_hull); + + return list; +error: + isl_aff_free(aff_nan); + isl_local_space_free(ls); + isl_basic_set_free(wrap_hull); + isl_aff_list_free(list); + return NULL; +} + +/* Add variables to info->bmap and info->tab corresponding to the elements + * in "list" that are not set to NaN. + * "extra_var" is the number of these elements. + * "dim" is the offset in the variables of "tab" where we should + * start considering the elements in "list". + * When this function returns, the total number of variables in "tab" + * is equal to "dim" plus the number of elements in "list". + * + * The newly added existentially quantified variables are not given + * an explicit representation because the corresponding div constraints + * do not appear in info->bmap. These constraints are not added + * to info->bmap because for internal consistency, they would need to + * be added to info->tab as well, where they could combine with the equality + * that is added later to result in constraints that do not hold + * in the original input. + */ +static int add_sub_vars(struct isl_coalesce_info *info, + __isl_keep isl_aff_list *list, int dim, int extra_var) +{ + int i, j, n, d; + isl_space *space; + + space = isl_basic_map_get_space(info->bmap); + info->bmap = isl_basic_map_cow(info->bmap); + info->bmap = isl_basic_map_extend_space(info->bmap, space, + extra_var, 0, 0); + if (!info->bmap) + return -1; + n = isl_aff_list_n_aff(list); + for (i = 0; i < n; ++i) { + int is_nan; + isl_aff *aff; + + aff = isl_aff_list_get_aff(list, i); + is_nan = isl_aff_is_nan(aff); + isl_aff_free(aff); + if (is_nan < 0) + return -1; + if (is_nan) + continue; + + if (isl_tab_insert_var(info->tab, dim + i) < 0) + return -1; + d = isl_basic_map_alloc_div(info->bmap); + if (d < 0) + return -1; + info->bmap = isl_basic_map_mark_div_unknown(info->bmap, d); + if (!info->bmap) + return -1; + for (j = d; j > i; --j) + isl_basic_map_swap_div(info->bmap, j - 1, j); + } + + return 0; +} + +/* For each element in "list" that is not set to NaN, fix the corresponding + * variable in "tab" to the purely affine expression defined by the element. + * "dim" is the offset in the variables of "tab" where we should + * start considering the elements in "list". + * + * This function assumes that a sufficient number of rows and + * elements in the constraint array are available in the tableau. + */ +static int add_sub_equalities(struct isl_tab *tab, + __isl_keep isl_aff_list *list, int dim) +{ + int i, n; + isl_ctx *ctx; + isl_vec *sub; + isl_aff *aff; + + n = isl_aff_list_n_aff(list); + + ctx = isl_tab_get_ctx(tab); + sub = isl_vec_alloc(ctx, 1 + dim + n); + if (!sub) + return -1; + isl_seq_clr(sub->el + 1 + dim, n); + + for (i = 0; i < n; ++i) { + aff = isl_aff_list_get_aff(list, i); + if (!aff) + goto error; + if (isl_aff_is_nan(aff)) { + isl_aff_free(aff); + continue; + } + isl_seq_cpy(sub->el, aff->v->el + 1, 1 + dim); + isl_int_neg(sub->el[1 + dim + i], aff->v->el[0]); + if (isl_tab_add_eq(tab, sub->el) < 0) + goto error; + isl_int_set_si(sub->el[1 + dim + i], 0); + isl_aff_free(aff); + } + + isl_vec_free(sub); + return 0; +error: + isl_aff_free(aff); + isl_vec_free(sub); + return -1; +} + +/* Add variables to info->tab and info->bmap corresponding to the elements + * in "list" that are not set to NaN. The value of the added variable + * in info->tab is fixed to the purely affine expression defined by the element. + * "dim" is the offset in the variables of info->tab where we should + * start considering the elements in "list". + * When this function returns, the total number of variables in info->tab + * is equal to "dim" plus the number of elements in "list". + */ +static int add_subs(struct isl_coalesce_info *info, + __isl_keep isl_aff_list *list, int dim) +{ + int extra_var; + int n; + + if (!list) + return -1; + + n = isl_aff_list_n_aff(list); + extra_var = n - (info->tab->n_var - dim); + + if (isl_tab_extend_vars(info->tab, extra_var) < 0) + return -1; + if (isl_tab_extend_cons(info->tab, 2 * extra_var) < 0) + return -1; + if (add_sub_vars(info, list, dim, extra_var) < 0) + return -1; + + return add_sub_equalities(info->tab, list, dim); +} + +/* Coalesce basic map "j" into basic map "i" after adding the extra integer + * divisions in "i" but not in "j" to basic map "j", with values + * specified by "list". The total number of elements in "list" + * is equal to the number of integer divisions in "i", while the number + * of NaN elements in the list is equal to the number of integer divisions + * in "j". + * + * If no coalescing can be performed, then we need to revert basic map "j" + * to its original state. We do the same if basic map "i" gets dropped + * during the coalescing, even though this should not happen in practice + * since we have already checked for "j" being a subset of "i" + * before we reach this stage. + */ +static enum isl_change coalesce_with_subs(int i, int j, + struct isl_coalesce_info *info, __isl_keep isl_aff_list *list) +{ + isl_basic_map *bmap_j; + struct isl_tab_undo *snap; + unsigned dim; + enum isl_change change; + + bmap_j = isl_basic_map_copy(info[j].bmap); + snap = isl_tab_snap(info[j].tab); + + dim = isl_basic_map_dim(bmap_j, isl_dim_all); + dim -= isl_basic_map_dim(bmap_j, isl_dim_div); + if (add_subs(&info[j], list, dim) < 0) + goto error; + + change = coalesce_local_pair(i, j, info); + if (change != isl_change_none && change != isl_change_drop_first) { + isl_basic_map_free(bmap_j); + } else { + isl_basic_map_free(info[j].bmap); + info[j].bmap = bmap_j; + + if (isl_tab_rollback(info[j].tab, snap) < 0) + return isl_change_error; + } + + return change; +error: + isl_basic_map_free(bmap_j); + return isl_change_error; +} + +/* Check if we can coalesce basic map "j" into basic map "i" after copying + * those extra integer divisions in "i" that can be simplified away + * using the extra equalities in "j". + * All divs are assumed to be known and not contain any nested divs. + * + * We first check if there are any extra equalities in "j" that we + * can exploit. Then we check if every integer division in "i" + * either already appears in "j" or can be simplified using the + * extra equalities to a purely affine expression. + * If these tests succeed, then we try to coalesce the two basic maps + * by introducing extra dimensions in "j" corresponding to + * the extra integer divsisions "i" fixed to the corresponding + * purely affine expression. + */ +static enum isl_change check_coalesce_into_eq(int i, int j, + struct isl_coalesce_info *info) +{ + unsigned n_div_i, n_div_j; + isl_basic_map *hull_i, *hull_j; + int equal, empty; + isl_aff_list *list; + enum isl_change change; + + n_div_i = isl_basic_map_dim(info[i].bmap, isl_dim_div); + n_div_j = isl_basic_map_dim(info[j].bmap, isl_dim_div); + if (n_div_i <= n_div_j) + return isl_change_none; + if (info[j].bmap->n_eq == 0) + return isl_change_none; + + hull_i = isl_basic_map_copy(info[i].bmap); + hull_i = isl_basic_map_plain_affine_hull(hull_i); + hull_j = isl_basic_map_copy(info[j].bmap); + hull_j = isl_basic_map_plain_affine_hull(hull_j); + + hull_j = isl_basic_map_intersect(hull_j, isl_basic_map_copy(hull_i)); + equal = isl_basic_map_plain_is_equal(hull_i, hull_j); + empty = isl_basic_map_plain_is_empty(hull_j); + isl_basic_map_free(hull_i); + + if (equal < 0 || empty < 0) + goto error; + if (equal || empty) { + isl_basic_map_free(hull_j); + return isl_change_none; + } + + list = set_up_substitutions(info[i].bmap, info[j].bmap, hull_j); + if (!list) + return isl_change_error; + if (isl_aff_list_n_aff(list) < n_div_i) + change = isl_change_none; + else + change = coalesce_with_subs(i, j, info, list); + + isl_aff_list_free(list); + + return change; +error: + isl_basic_map_free(hull_j); + return isl_change_error; +} + +/* Check if we can coalesce basic maps "i" and "j" after copying + * those extra integer divisions in one of the basic maps that can + * be simplified away using the extra equalities in the other basic map. + * We require all divs to be known in both basic maps. + * Furthermore, to simplify the comparison of div expressions, + * we do not allow any nested integer divisions. + */ +static enum isl_change check_coalesce_eq(int i, int j, + struct isl_coalesce_info *info) +{ + int known, nested; + enum isl_change change; + + known = isl_basic_map_divs_known(info[i].bmap); + if (known < 0 || !known) + return known < 0 ? isl_change_error : isl_change_none; + known = isl_basic_map_divs_known(info[j].bmap); + if (known < 0 || !known) + return known < 0 ? isl_change_error : isl_change_none; + nested = has_nested_div(info[i].bmap); + if (nested < 0 || nested) + return nested < 0 ? isl_change_error : isl_change_none; + nested = has_nested_div(info[j].bmap); + if (nested < 0 || nested) + return nested < 0 ? isl_change_error : isl_change_none; + + change = check_coalesce_into_eq(i, j, info); + if (change != isl_change_none) + return change; + change = check_coalesce_into_eq(j, i, info); + if (change != isl_change_none) + return invert_change(change); + + return isl_change_none; +} + +/* Check if the union of the given pair of basic maps + * can be represented by a single basic map. + * If so, replace the pair by the single basic map and return + * isl_change_drop_first, isl_change_drop_second or isl_change_fuse. + * Otherwise, return isl_change_none. + * + * We first check if the two basic maps live in the same local space, + * after aligning the divs that differ by only an integer constant. + * If so, we do the complete check. Otherwise, we check if they have + * the same number of integer divisions and can be coalesced, if one is + * an obvious subset of the other or if the extra integer divisions + * of one basic map can be simplified away using the extra equalities + * of the other basic map. + */ +static enum isl_change coalesce_pair(int i, int j, + struct isl_coalesce_info *info) +{ + int same; + enum isl_change change; + + if (harmonize_divs(&info[i], &info[j]) < 0) + return isl_change_error; + same = same_divs(info[i].bmap, info[j].bmap); + if (same < 0) + return isl_change_error; + if (same) + return coalesce_local_pair(i, j, info); + + if (info[i].bmap->n_div == info[j].bmap->n_div) { + change = coalesce_local_pair(i, j, info); + if (change != isl_change_none) + return change; + } + + change = coalesce_divs(i, j, info); + if (change != isl_change_none) + return change; + + return check_coalesce_eq(i, j, info); +} + +/* Return the maximum of "a" and "b". + */ +static int isl_max(int a, int b) +{ + return a > b ? a : b; +} + +/* Pairwise coalesce the basic maps in the range [start1, end1[ of "info" + * with those in the range [start2, end2[, skipping basic maps + * that have been removed (either before or within this function). + * + * For each basic map i in the first range, we check if it can be coalesced + * with respect to any previously considered basic map j in the second range. + * If i gets dropped (because it was a subset of some j), then + * we can move on to the next basic map. + * If j gets dropped, we need to continue checking against the other + * previously considered basic maps. + * If the two basic maps got fused, then we recheck the fused basic map + * against the previously considered basic maps, starting at i + 1 + * (even if start2 is greater than i + 1). + */ +static int coalesce_range(isl_ctx *ctx, struct isl_coalesce_info *info, + int start1, int end1, int start2, int end2) +{ + int i, j; + + for (i = end1 - 1; i >= start1; --i) { + if (info[i].removed) + continue; + for (j = isl_max(i + 1, start2); j < end2; ++j) { + enum isl_change changed; + + if (info[j].removed) + continue; + if (info[i].removed) + isl_die(ctx, isl_error_internal, + "basic map unexpectedly removed", + return -1); + changed = coalesce_pair(i, j, info); + switch (changed) { + case isl_change_error: + return -1; + case isl_change_none: + case isl_change_drop_second: + continue; + case isl_change_drop_first: + j = end2; + break; + case isl_change_fuse: + j = i; + break; + } + } + } + + return 0; +} + +/* Pairwise coalesce the basic maps described by the "n" elements of "info". + * + * We consider groups of basic maps that live in the same apparent + * affine hull and we first coalesce within such a group before we + * coalesce the elements in the group with elements of previously + * considered groups. If a fuse happens during the second phase, + * then we also reconsider the elements within the group. + */ +static int coalesce(isl_ctx *ctx, int n, struct isl_coalesce_info *info) +{ + int start, end; + + for (end = n; end > 0; end = start) { + start = end - 1; + while (start >= 1 && + info[start - 1].hull_hash == info[start].hull_hash) + start--; + if (coalesce_range(ctx, info, start, end, start, end) < 0) + return -1; + if (coalesce_range(ctx, info, start, end, end, n) < 0) + return -1; + } + + return 0; +} + +/* Update the basic maps in "map" based on the information in "info". + * In particular, remove the basic maps that have been marked removed and + * update the others based on the information in the corresponding tableau. + * Since we detected implicit equalities without calling + * isl_basic_map_gauss, we need to do it now. + * Also call isl_basic_map_simplify if we may have lost the definition + * of one or more integer divisions. + */ +static __isl_give isl_map *update_basic_maps(__isl_take isl_map *map, + int n, struct isl_coalesce_info *info) +{ + int i; + + if (!map) + return NULL; + + for (i = n - 1; i >= 0; --i) { + if (info[i].removed) { + isl_basic_map_free(map->p[i]); + if (i != map->n - 1) + map->p[i] = map->p[map->n - 1]; + map->n--; + continue; + } + + info[i].bmap = isl_basic_map_update_from_tab(info[i].bmap, + info[i].tab); + info[i].bmap = isl_basic_map_gauss(info[i].bmap, NULL); + if (info[i].simplify) + info[i].bmap = isl_basic_map_simplify(info[i].bmap); + info[i].bmap = isl_basic_map_finalize(info[i].bmap); + if (!info[i].bmap) + return isl_map_free(map); + ISL_F_SET(info[i].bmap, ISL_BASIC_MAP_NO_IMPLICIT); + ISL_F_SET(info[i].bmap, ISL_BASIC_MAP_NO_REDUNDANT); + isl_basic_map_free(map->p[i]); + map->p[i] = info[i].bmap; + info[i].bmap = NULL; + } + + return map; +} + +/* For each pair of basic maps in the map, check if the union of the two + * can be represented by a single basic map. + * If so, replace the pair by the single basic map and start over. + * + * We factor out any (hidden) common factor from the constraint + * coefficients to improve the detection of adjacent constraints. + * + * Since we are constructing the tableaus of the basic maps anyway, + * we exploit them to detect implicit equalities and redundant constraints. + * This also helps the coalescing as it can ignore the redundant constraints. + * In order to avoid confusion, we make all implicit equalities explicit + * in the basic maps. We don't call isl_basic_map_gauss, though, + * as that may affect the number of constraints. + * This means that we have to call isl_basic_map_gauss at the end + * of the computation (in update_basic_maps) to ensure that + * the basic maps are not left in an unexpected state. + * For each basic map, we also compute the hash of the apparent affine hull + * for use in coalesce. + */ +struct isl_map *isl_map_coalesce(struct isl_map *map) +{ + int i; + unsigned n; + isl_ctx *ctx; + struct isl_coalesce_info *info = NULL; + + map = isl_map_remove_empty_parts(map); + if (!map) + return NULL; + + if (map->n <= 1) + return map; + + ctx = isl_map_get_ctx(map); + map = isl_map_sort_divs(map); + map = isl_map_cow(map); + + if (!map) + return NULL; + + n = map->n; + + info = isl_calloc_array(map->ctx, struct isl_coalesce_info, n); + if (!info) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_reduce_coefficients(map->p[i]); + if (!map->p[i]) + goto error; + info[i].bmap = isl_basic_map_copy(map->p[i]); + info[i].tab = isl_tab_from_basic_map(info[i].bmap, 0); + if (!info[i].tab) + goto error; + if (!ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_NO_IMPLICIT)) + if (isl_tab_detect_implicit_equalities(info[i].tab) < 0) + goto error; + info[i].bmap = isl_tab_make_equalities_explicit(info[i].tab, + info[i].bmap); + if (!info[i].bmap) + goto error; + if (!ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_NO_REDUNDANT)) + if (isl_tab_detect_redundant(info[i].tab) < 0) + goto error; + if (coalesce_info_set_hull_hash(&info[i]) < 0) + goto error; + } + for (i = map->n - 1; i >= 0; --i) + if (info[i].tab->empty) + drop(&info[i]); + + if (coalesce(ctx, n, info) < 0) + goto error; + + map = update_basic_maps(map, n, info); + + clear_coalesce_info(n, info); + + return map; +error: + clear_coalesce_info(n, info); + isl_map_free(map); + return NULL; +} + +/* For each pair of basic sets in the set, check if the union of the two + * can be represented by a single basic set. + * If so, replace the pair by the single basic set and start over. + */ +struct isl_set *isl_set_coalesce(struct isl_set *set) +{ + return (struct isl_set *)isl_map_coalesce((struct isl_map *)set); +} Index: lib/Analysis/isl/isl_config.h.cmake =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_config.h.cmake @@ -0,0 +1,56 @@ +/* define if your compiler has __attribute__ */ +#cmakedefine HAVE___ATTRIBUTE__ /**/ + +/* most gcc compilers know a function __attribute__((__warn_unused_result__)) */ +#define GCC_WARN_UNUSED_RESULT @GCC_WARN_UNUSED_RESULT@ + + +/* Define to 1 if you have the declaration of `ffs', and to 0 if you don't. */ +#define HAVE_DECL_FFS @HAVE_DECL_FFS@ + +/* Define to 1 if you have the declaration of `__builtin_ffs', and to 0 if you + don't. */ +#define HAVE_DECL___BUILTIN_FFS @HAVE_DECL___BUILTIN_FFS@ + +/* Define to 1 if you have the declaration of `_BitScanForward', and to 0 if + you don't. */ +#define HAVE_DECL__BITSCANFORWARD @HAVE_DECL__BITSCANFORWARD@ + + +/* Define to 1 if you have the declaration of `strcasecmp', and to 0 if you + don't. */ +#define HAVE_DECL_STRCASECMP @HAVE_DECL_STRCASECMP@ + +/* Define to 1 if you have the declaration of `_stricmp', and to 0 if you + don't. */ +#define HAVE_DECL__STRICMP @HAVE_DECL__STRICMP@ + + +/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you + don't. */ +#define HAVE_DECL_STRNCASECMP @HAVE_DECL_STRNCASECMP@ + +/* Define to 1 if you have the declaration of `_strnicmp', and to 0 if you + don't. */ +#define HAVE_DECL__STRNICMP @HAVE_DECL__STRNICMP@ + + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF @HAVE_DECL_SNPRINTF@ + +/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you + don't. */ +#define HAVE_DECL__SNPRINTF @HAVE_DECL__SNPRINTF@ + + +/* use gmp to implement isl_int */ +#cmakedefine USE_GMP_FOR_MP + +/* use imath to implement isl_int */ +#cmakedefine USE_IMATH_FOR_MP + +/* Use small integer optimization */ +#cmakedefine USE_SMALL_INT_OPT + +#include Index: lib/Analysis/isl/isl_config.h.in =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_config.h.in @@ -0,0 +1,203 @@ +/* isl_config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if HeaderSearchOptions::AddPath takes 4 arguments */ +#undef ADDPATH_TAKES_4_ARGUMENTS + +/* Clang installation prefix */ +#undef CLANG_PREFIX + +/* Define if CompilerInstance::createDiagnostics takes argc and argv */ +#undef CREATEDIAGNOSTICS_TAKES_ARG + +/* Define if CompilerInstance::createPreprocessor takes TranslationUnitKind */ +#undef CREATEPREPROCESSOR_TAKES_TUKIND + +/* Define if TargetInfo::CreateTargetInfo takes pointer */ +#undef CREATETARGETINFO_TAKES_POINTER + +/* Define if TargetInfo::CreateTargetInfo takes shared_ptr */ +#undef CREATETARGETINFO_TAKES_SHARED_PTR + +/* Define if Driver constructor takes default image name */ +#undef DRIVER_CTOR_TAKES_DEFAULTIMAGENAME + +/* Define to Diagnostic for older versions of clang */ +#undef DiagnosticsEngine + +/* most gcc compilers know a function __attribute__((__warn_unused_result__)) + */ +#undef GCC_WARN_UNUSED_RESULT + +/* Define if llvm/ADT/OwningPtr.h exists */ +#undef HAVE_ADT_OWNINGPTR_H + +/* Define if clang/Basic/DiagnosticOptions.h exists */ +#undef HAVE_BASIC_DIAGNOSTICOPTIONS_H + +/* Define if Driver constructor takes CXXIsProduction argument */ +#undef HAVE_CXXISPRODUCTION + +/* Define to 1 if you have the declaration of `ffs', and to 0 if you don't. */ +#undef HAVE_DECL_FFS + +/* Define to 1 if you have the declaration of `mp_get_memory_functions', and + to 0 if you don't. */ +#undef HAVE_DECL_MP_GET_MEMORY_FUNCTIONS + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#undef HAVE_DECL_SNPRINTF + +/* Define to 1 if you have the declaration of `strcasecmp', and to 0 if you + don't. */ +#undef HAVE_DECL_STRCASECMP + +/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you + don't. */ +#undef HAVE_DECL_STRNCASECMP + +/* Define to 1 if you have the declaration of `_BitScanForward', and to 0 if + you don't. */ +#undef HAVE_DECL__BITSCANFORWARD + +/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you + don't. */ +#undef HAVE_DECL__SNPRINTF + +/* Define to 1 if you have the declaration of `_stricmp', and to 0 if you + don't. */ +#undef HAVE_DECL__STRICMP + +/* Define to 1 if you have the declaration of `_strnicmp', and to 0 if you + don't. */ +#undef HAVE_DECL__STRNICMP + +/* Define to 1 if you have the declaration of `__builtin_ffs', and to 0 if you + don't. */ +#undef HAVE_DECL___BUILTIN_FFS + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if Driver constructor takes IsProduction argument */ +#undef HAVE_ISPRODUCTION + +/* Define to 1 if you have the `gmp' library (-lgmp). */ +#undef HAVE_LIBGMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if SourceManager has a setMainFileID method */ +#undef HAVE_SETMAINFILEID + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* define if your compiler has __attribute__ */ +#undef HAVE___ATTRIBUTE__ + +/* Return type of HandleTopLevelDeclReturn */ +#undef HandleTopLevelDeclContinue + +/* Return type of HandleTopLevelDeclReturn */ +#undef HandleTopLevelDeclReturn + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `char', as computed by sizeof. */ +#undef SIZEOF_CHAR + +/* The size of `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* The size of `void*', as computed by sizeof. */ +#undef SIZEOF_VOIDP + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if Driver::BuildCompilation takes ArrayRef */ +#undef USE_ARRAYREF + +/* use gmp to implement isl_int */ +#undef USE_GMP_FOR_MP + +/* use imath to implement isl_int */ +#undef USE_IMATH_FOR_MP + +/* Use small integer optimization */ +#undef USE_SMALL_INT_OPT + +/* Version number of package */ +#undef VERSION + +/* Define to getParamType for newer versions of clang */ +#undef getArgType + +/* Define to getHostTriple for older versions of clang */ +#undef getDefaultTargetTriple + +/* Define to getInstantiationLineNumber for older versions of clang */ +#undef getExpansionLineNumber + +/* Define to getNumParams for newer versions of clang */ +#undef getNumArgs + +/* Define to getResultType for older versions of clang */ +#undef getReturnType + +/* Define to InitializeBuiltins for older versions of clang */ +#undef initializeBuiltins + +#include Index: lib/Analysis/isl/isl_config_post.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_config_post.h @@ -0,0 +1,38 @@ +#ifndef HAVE___ATTRIBUTE__ +#define __attribute__(x) +#endif + +#if HAVE_DECL_FFS +#include +#endif + +#if (HAVE_DECL_FFS==0) && (HAVE_DECL___BUILTIN_FFS==1) +#define ffs __builtin_ffs +#endif + +#if !HAVE_DECL_FFS && !HAVE_DECL___BUILTIN_FFS && HAVE_DECL__BITSCANFORWARD +int isl_ffs(int i); +#define ffs isl_ffs +#endif + +#if HAVE_DECL_STRCASECMP || HAVE_DECL_STRNCASECMP +#include +#endif + +#if !HAVE_DECL_STRCASECMP && HAVE_DECL__STRICMP +#define strcasecmp _stricmp +#endif + +#if !HAVE_DECL_STRNCASECMP && HAVE_DECL__STRNICMP +#define strncasecmp _strnicmp +#endif + +#if !HAVE_DECL_SNPRINTF && HAVE_DECL__SNPRINTF +#define snprintf _snprintf +#endif + +#ifdef GCC_WARN_UNUSED_RESULT +#define WARN_UNUSED GCC_WARN_UNUSED_RESULT +#else +#define WARN_UNUSED +#endif Index: lib/Analysis/isl/isl_constraint.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_constraint.c @@ -0,0 +1,1451 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef BASE +#define BASE constraint + +#include + +isl_ctx *isl_constraint_get_ctx(__isl_keep isl_constraint *c) +{ + return c ? isl_local_space_get_ctx(c->ls) : NULL; +} + +static unsigned n(struct isl_constraint *c, enum isl_dim_type type) +{ + return isl_local_space_dim(c->ls, type); +} + +static unsigned offset(struct isl_constraint *c, enum isl_dim_type type) +{ + return isl_local_space_offset(c->ls, type); +} + +static unsigned basic_map_offset(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type) +{ + return type == isl_dim_div ? 1 + isl_space_dim(bmap->dim, isl_dim_all) + : 1 + isl_space_offset(bmap->dim, type); +} + +static unsigned basic_set_offset(struct isl_basic_set *bset, + enum isl_dim_type type) +{ + isl_space *dim = bset->dim; + switch (type) { + case isl_dim_param: return 1; + case isl_dim_in: return 1 + dim->nparam; + case isl_dim_out: return 1 + dim->nparam + dim->n_in; + case isl_dim_div: return 1 + dim->nparam + dim->n_in + dim->n_out; + default: return 0; + } +} + +__isl_give isl_constraint *isl_constraint_alloc_vec(int eq, + __isl_take isl_local_space *ls, __isl_take isl_vec *v) +{ + isl_constraint *constraint; + + if (!ls || !v) + goto error; + + constraint = isl_alloc_type(isl_vec_get_ctx(v), isl_constraint); + if (!constraint) + goto error; + + constraint->ref = 1; + constraint->eq = eq; + constraint->ls = ls; + constraint->v = v; + + return constraint; +error: + isl_local_space_free(ls); + isl_vec_free(v); + return NULL; +} + +__isl_give isl_constraint *isl_constraint_alloc(int eq, + __isl_take isl_local_space *ls) +{ + isl_ctx *ctx; + isl_vec *v; + + if (!ls) + return NULL; + + ctx = isl_local_space_get_ctx(ls); + v = isl_vec_alloc(ctx, 1 + isl_local_space_dim(ls, isl_dim_all)); + v = isl_vec_clr(v); + return isl_constraint_alloc_vec(eq, ls, v); +} + +struct isl_constraint *isl_basic_map_constraint(struct isl_basic_map *bmap, + isl_int **line) +{ + int eq; + isl_ctx *ctx; + isl_vec *v; + isl_local_space *ls = NULL; + isl_constraint *constraint; + + if (!bmap || !line) + goto error; + + eq = line >= bmap->eq; + + ctx = isl_basic_map_get_ctx(bmap); + ls = isl_basic_map_get_local_space(bmap); + v = isl_vec_alloc(ctx, 1 + isl_local_space_dim(ls, isl_dim_all)); + if (!v) + goto error; + isl_seq_cpy(v->el, line[0], v->size); + constraint = isl_constraint_alloc_vec(eq, ls, v); + + isl_basic_map_free(bmap); + return constraint; +error: + isl_local_space_free(ls); + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_constraint *isl_basic_set_constraint(struct isl_basic_set *bset, + isl_int **line) +{ + return isl_basic_map_constraint((struct isl_basic_map *)bset, line); +} + +__isl_give isl_constraint *isl_constraint_alloc_equality( + __isl_take isl_local_space *ls) +{ + return isl_constraint_alloc(1, ls); +} + +__isl_give isl_constraint *isl_constraint_alloc_inequality( + __isl_take isl_local_space *ls) +{ + return isl_constraint_alloc(0, ls); +} + +struct isl_constraint *isl_constraint_dup(struct isl_constraint *c) +{ + if (!c) + return NULL; + + return isl_constraint_alloc_vec(c->eq, isl_local_space_copy(c->ls), + isl_vec_copy(c->v)); +} + +struct isl_constraint *isl_constraint_cow(struct isl_constraint *c) +{ + if (!c) + return NULL; + + if (c->ref == 1) + return c; + c->ref--; + return isl_constraint_dup(c); +} + +struct isl_constraint *isl_constraint_copy(struct isl_constraint *constraint) +{ + if (!constraint) + return NULL; + + constraint->ref++; + return constraint; +} + +__isl_null isl_constraint *isl_constraint_free(__isl_take isl_constraint *c) +{ + if (!c) + return NULL; + + if (--c->ref > 0) + return NULL; + + isl_local_space_free(c->ls); + isl_vec_free(c->v); + free(c); + + return NULL; +} + +/* Return the number of constraints in "bmap", i.e., the + * number of times isl_basic_map_foreach_constraint will + * call the callback. + */ +int isl_basic_map_n_constraint(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return -1; + + return bmap->n_eq + bmap->n_ineq; +} + +/* Return the number of constraints in "bset", i.e., the + * number of times isl_basic_set_foreach_constraint will + * call the callback. + */ +int isl_basic_set_n_constraint(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_n_constraint(bset); +} + +isl_stat isl_basic_map_foreach_constraint(__isl_keep isl_basic_map *bmap, + isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user) +{ + int i; + struct isl_constraint *c; + + if (!bmap) + return isl_stat_error; + + isl_assert(bmap->ctx, ISL_F_ISSET(bmap, ISL_BASIC_MAP_FINAL), + return isl_stat_error); + + for (i = 0; i < bmap->n_eq; ++i) { + c = isl_basic_map_constraint(isl_basic_map_copy(bmap), + &bmap->eq[i]); + if (!c) + return isl_stat_error; + if (fn(c, user) < 0) + return isl_stat_error; + } + + for (i = 0; i < bmap->n_ineq; ++i) { + c = isl_basic_map_constraint(isl_basic_map_copy(bmap), + &bmap->ineq[i]); + if (!c) + return isl_stat_error; + if (fn(c, user) < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +isl_stat isl_basic_set_foreach_constraint(__isl_keep isl_basic_set *bset, + isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user) +{ + return isl_basic_map_foreach_constraint((isl_basic_map *)bset, fn, user); +} + +/* Add the constraint to the list that "user" points to, if it is not + * a div constraint. + */ +static isl_stat collect_constraint(__isl_take isl_constraint *constraint, + void *user) +{ + isl_constraint_list **list = user; + + if (isl_constraint_is_div_constraint(constraint)) + isl_constraint_free(constraint); + else + *list = isl_constraint_list_add(*list, constraint); + + return isl_stat_ok; +} + +/* Return a list of constraints that, when combined, are equivalent + * to "bmap". The input is required to have only known divs. + * + * There is no need to include the div constraints as they are + * implied by the div expressions. + */ +__isl_give isl_constraint_list *isl_basic_map_get_constraint_list( + __isl_keep isl_basic_map *bmap) +{ + int n; + int known; + isl_ctx *ctx; + isl_constraint_list *list; + + known = isl_basic_map_divs_known(bmap); + if (known < 0) + return NULL; + ctx = isl_basic_map_get_ctx(bmap); + if (!known) + isl_die(ctx, isl_error_invalid, + "input involves unknown divs", return NULL); + + n = isl_basic_map_n_constraint(bmap); + list = isl_constraint_list_alloc(ctx, n); + if (isl_basic_map_foreach_constraint(bmap, + &collect_constraint, &list) < 0) + list = isl_constraint_list_free(list); + + return list; +} + +/* Return a list of constraints that, when combined, are equivalent + * to "bset". The input is required to have only known divs. + */ +__isl_give isl_constraint_list *isl_basic_set_get_constraint_list( + __isl_keep isl_basic_set *bset) +{ + return isl_basic_map_get_constraint_list(bset); +} + +int isl_constraint_is_equal(struct isl_constraint *constraint1, + struct isl_constraint *constraint2) +{ + int equal; + + if (!constraint1 || !constraint2) + return 0; + if (constraint1->eq != constraint2->eq) + return 0; + equal = isl_local_space_is_equal(constraint1->ls, constraint2->ls); + if (equal < 0 || !equal) + return equal; + return isl_vec_is_equal(constraint1->v, constraint2->v); +} + +struct isl_basic_map *isl_basic_map_add_constraint( + struct isl_basic_map *bmap, struct isl_constraint *constraint) +{ + isl_ctx *ctx; + isl_space *dim; + int equal_space; + + if (!bmap || !constraint) + goto error; + + ctx = isl_constraint_get_ctx(constraint); + dim = isl_constraint_get_space(constraint); + equal_space = isl_space_is_equal(bmap->dim, dim); + isl_space_free(dim); + isl_assert(ctx, equal_space, goto error); + + bmap = isl_basic_map_intersect(bmap, + isl_basic_map_from_constraint(constraint)); + return bmap; +error: + isl_basic_map_free(bmap); + isl_constraint_free(constraint); + return NULL; +} + +struct isl_basic_set *isl_basic_set_add_constraint( + struct isl_basic_set *bset, struct isl_constraint *constraint) +{ + return (struct isl_basic_set *) + isl_basic_map_add_constraint((struct isl_basic_map *)bset, + constraint); +} + +__isl_give isl_map *isl_map_add_constraint(__isl_take isl_map *map, + __isl_take isl_constraint *constraint) +{ + isl_basic_map *bmap; + + bmap = isl_basic_map_from_constraint(constraint); + map = isl_map_intersect(map, isl_map_from_basic_map(bmap)); + + return map; +} + +__isl_give isl_set *isl_set_add_constraint(__isl_take isl_set *set, + __isl_take isl_constraint *constraint) +{ + return isl_map_add_constraint(set, constraint); +} + +__isl_give isl_space *isl_constraint_get_space( + __isl_keep isl_constraint *constraint) +{ + return constraint ? isl_local_space_get_space(constraint->ls) : NULL; +} + +__isl_give isl_local_space *isl_constraint_get_local_space( + __isl_keep isl_constraint *constraint) +{ + return constraint ? isl_local_space_copy(constraint->ls) : NULL; +} + +int isl_constraint_dim(struct isl_constraint *constraint, + enum isl_dim_type type) +{ + if (!constraint) + return -1; + return n(constraint, type); +} + +isl_bool isl_constraint_involves_dims(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + isl_ctx *ctx; + int *active = NULL; + isl_bool involves = isl_bool_false; + + if (!constraint) + return isl_bool_error; + if (n == 0) + return isl_bool_false; + + ctx = isl_constraint_get_ctx(constraint); + if (first + n > isl_constraint_dim(constraint, type)) + isl_die(ctx, isl_error_invalid, + "range out of bounds", return isl_bool_error); + + active = isl_local_space_get_active(constraint->ls, + constraint->v->el + 1); + if (!active) + goto error; + + first += isl_local_space_offset(constraint->ls, type) - 1; + for (i = 0; i < n; ++i) + if (active[first + i]) { + involves = isl_bool_true; + break; + } + + free(active); + + return involves; +error: + free(active); + return isl_bool_error; +} + +/* Does the given constraint represent a lower bound on the given + * dimension? + */ +isl_bool isl_constraint_is_lower_bound(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos) +{ + if (!constraint) + return isl_bool_error; + + if (pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid, + "position out of bounds", return isl_bool_error); + + pos += isl_local_space_offset(constraint->ls, type); + return isl_int_is_pos(constraint->v->el[pos]); +} + +/* Does the given constraint represent an upper bound on the given + * dimension? + */ +isl_bool isl_constraint_is_upper_bound(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos) +{ + if (!constraint) + return isl_bool_error; + + if (pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid, + "position out of bounds", return isl_bool_error); + + pos += isl_local_space_offset(constraint->ls, type); + return isl_int_is_neg(constraint->v->el[pos]); +} + +const char *isl_constraint_get_dim_name(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, unsigned pos) +{ + return constraint ? + isl_local_space_get_dim_name(constraint->ls, type, pos) : NULL; +} + +void isl_constraint_get_constant(struct isl_constraint *constraint, isl_int *v) +{ + if (!constraint) + return; + isl_int_set(*v, constraint->v->el[0]); +} + +/* Return the constant term of "constraint". + */ +__isl_give isl_val *isl_constraint_get_constant_val( + __isl_keep isl_constraint *constraint) +{ + isl_ctx *ctx; + + if (!constraint) + return NULL; + + ctx = isl_constraint_get_ctx(constraint); + return isl_val_int_from_isl_int(ctx, constraint->v->el[0]); +} + +void isl_constraint_get_coefficient(struct isl_constraint *constraint, + enum isl_dim_type type, int pos, isl_int *v) +{ + if (!constraint) + return; + + if (pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(constraint->v->ctx, isl_error_invalid, + "position out of bounds", return); + + pos += isl_local_space_offset(constraint->ls, type); + isl_int_set(*v, constraint->v->el[pos]); +} + +/* Return the coefficient of the variable of type "type" at position "pos" + * of "constraint". + */ +__isl_give isl_val *isl_constraint_get_coefficient_val( + __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos) +{ + isl_ctx *ctx; + + if (!constraint) + return NULL; + + ctx = isl_constraint_get_ctx(constraint); + if (pos < 0 || pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(ctx, isl_error_invalid, + "position out of bounds", return NULL); + + pos += isl_local_space_offset(constraint->ls, type); + return isl_val_int_from_isl_int(ctx, constraint->v->el[pos]); +} + +__isl_give isl_aff *isl_constraint_get_div(__isl_keep isl_constraint *constraint, + int pos) +{ + if (!constraint) + return NULL; + + return isl_local_space_get_div(constraint->ls, pos); +} + +__isl_give isl_constraint *isl_constraint_set_constant( + __isl_take isl_constraint *constraint, isl_int v) +{ + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + constraint->v = isl_vec_cow(constraint->v); + if (!constraint->v) + return isl_constraint_free(constraint); + + isl_int_set(constraint->v->el[0], v); + return constraint; +} + +/* Replace the constant term of "constraint" by "v". + */ +__isl_give isl_constraint *isl_constraint_set_constant_val( + __isl_take isl_constraint *constraint, __isl_take isl_val *v) +{ + constraint = isl_constraint_cow(constraint); + if (!constraint || !v) + goto error; + if (!isl_val_is_int(v)) + isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid, + "expecting integer value", goto error); + constraint->v = isl_vec_set_element_val(constraint->v, 0, v); + if (!constraint->v) + constraint = isl_constraint_free(constraint); + return constraint; +error: + isl_val_free(v); + return isl_constraint_free(constraint); +} + +__isl_give isl_constraint *isl_constraint_set_constant_si( + __isl_take isl_constraint *constraint, int v) +{ + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + constraint->v = isl_vec_cow(constraint->v); + if (!constraint->v) + return isl_constraint_free(constraint); + + isl_int_set_si(constraint->v->el[0], v); + return constraint; +} + +__isl_give isl_constraint *isl_constraint_set_coefficient( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, isl_int v) +{ + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + if (pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(constraint->v->ctx, isl_error_invalid, + "position out of bounds", + return isl_constraint_free(constraint)); + + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + constraint->v = isl_vec_cow(constraint->v); + if (!constraint->v) + return isl_constraint_free(constraint); + + pos += isl_local_space_offset(constraint->ls, type); + isl_int_set(constraint->v->el[pos], v); + + return constraint; +} + +/* Replace the coefficient of the variable of type "type" at position "pos" + * of "constraint" by "v". + */ +__isl_give isl_constraint *isl_constraint_set_coefficient_val( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, __isl_take isl_val *v) +{ + constraint = isl_constraint_cow(constraint); + if (!constraint || !v) + goto error; + if (!isl_val_is_int(v)) + isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid, + "expecting integer value", goto error); + + if (pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid, + "position out of bounds", goto error); + + pos += isl_local_space_offset(constraint->ls, type); + constraint->v = isl_vec_set_element_val(constraint->v, pos, v); + if (!constraint->v) + constraint = isl_constraint_free(constraint); + return constraint; +error: + isl_val_free(v); + return isl_constraint_free(constraint); +} + +__isl_give isl_constraint *isl_constraint_set_coefficient_si( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, int v) +{ + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + if (pos >= isl_local_space_dim(constraint->ls, type)) + isl_die(constraint->v->ctx, isl_error_invalid, + "position out of bounds", + return isl_constraint_free(constraint)); + + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + constraint->v = isl_vec_cow(constraint->v); + if (!constraint->v) + return isl_constraint_free(constraint); + + pos += isl_local_space_offset(constraint->ls, type); + isl_int_set_si(constraint->v->el[pos], v); + + return constraint; +} + +/* Drop any constraint from "bset" that is identical to "constraint". + * In particular, this means that the local spaces of "bset" and + * "constraint" need to be the same. + * + * We manually set ISL_BASIC_SET_FINAL instead of calling + * isl_basic_set_finalize because this function is called by CLooG, + * which does not expect any variables to disappear. + */ +__isl_give isl_basic_set *isl_basic_set_drop_constraint( + __isl_take isl_basic_set *bset, __isl_take isl_constraint *constraint) +{ + int i; + unsigned n; + isl_int **row; + unsigned total; + isl_local_space *ls1; + int equal; + int equality; + + if (!bset || !constraint) + goto error; + + ls1 = isl_basic_set_get_local_space(bset); + equal = isl_local_space_is_equal(ls1, constraint->ls); + isl_local_space_free(ls1); + if (equal < 0) + goto error; + if (!equal) { + isl_constraint_free(constraint); + return bset; + } + + bset = isl_basic_set_cow(bset); + if (!bset) + goto error; + + equality = isl_constraint_is_equality(constraint); + if (equality) { + n = bset->n_eq; + row = bset->eq; + } else { + n = bset->n_ineq; + row = bset->ineq; + } + + total = isl_constraint_dim(constraint, isl_dim_all); + for (i = 0; i < n; ++i) { + if (!isl_seq_eq(row[i], constraint->v->el, 1 + total)) + continue; + if (equality && isl_basic_set_drop_equality(bset, i) < 0) + goto error; + if (!equality && isl_basic_set_drop_inequality(bset, i) < 0) + goto error; + break; + } + + isl_constraint_free(constraint); + ISL_F_SET(bset, ISL_BASIC_SET_FINAL); + return bset; +error: + isl_constraint_free(constraint); + isl_basic_set_free(bset); + return NULL; +} + +struct isl_constraint *isl_constraint_negate(struct isl_constraint *constraint) +{ + isl_ctx *ctx; + + constraint = isl_constraint_cow(constraint); + if (!constraint) + return NULL; + + ctx = isl_constraint_get_ctx(constraint); + if (isl_constraint_is_equality(constraint)) + isl_die(ctx, isl_error_invalid, "cannot negate equality", + return isl_constraint_free(constraint)); + constraint->v = isl_vec_neg(constraint->v); + constraint->v = isl_vec_cow(constraint->v); + if (!constraint->v) + return isl_constraint_free(constraint); + isl_int_sub_ui(constraint->v->el[0], constraint->v->el[0], 1); + return constraint; +} + +isl_bool isl_constraint_is_equality(struct isl_constraint *constraint) +{ + if (!constraint) + return isl_bool_error; + return constraint->eq; +} + +int isl_constraint_is_div_constraint(__isl_keep isl_constraint *constraint) +{ + int i; + int n_div; + + if (!constraint) + return -1; + if (isl_constraint_is_equality(constraint)) + return 0; + n_div = isl_constraint_dim(constraint, isl_dim_div); + for (i = 0; i < n_div; ++i) { + if (isl_local_space_is_div_constraint(constraint->ls, + constraint->v->el, i)) + return 1; + } + + return 0; +} + +/* We manually set ISL_BASIC_SET_FINAL instead of calling + * isl_basic_map_finalize because we want to keep the position + * of the divs and we therefore do not want to throw away redundant divs. + * This is arguably a bit fragile. + */ +__isl_give isl_basic_map *isl_basic_map_from_constraint( + __isl_take isl_constraint *constraint) +{ + int k; + isl_local_space *ls; + struct isl_basic_map *bmap; + isl_int *c; + unsigned total; + + if (!constraint) + return NULL; + + ls = isl_local_space_copy(constraint->ls); + bmap = isl_basic_map_from_local_space(ls); + bmap = isl_basic_map_extend_constraints(bmap, 1, 1); + if (isl_constraint_is_equality(constraint)) { + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + c = bmap->eq[k]; + } + else { + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + c = bmap->ineq[k]; + } + total = isl_basic_map_total_dim(bmap); + isl_seq_cpy(c, constraint->v->el, 1 + total); + isl_constraint_free(constraint); + if (bmap) + ISL_F_SET(bmap, ISL_BASIC_SET_FINAL); + return bmap; +error: + isl_constraint_free(constraint); + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_set *isl_basic_set_from_constraint( + struct isl_constraint *constraint) +{ + if (!constraint) + return NULL; + + if (isl_constraint_dim(constraint, isl_dim_in) != 0) + isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid, + "not a set constraint", goto error); + return (isl_basic_set *)isl_basic_map_from_constraint(constraint); +error: + isl_constraint_free(constraint); + return NULL; +} + +/* Is the variable of "type" at position "pos" of "bmap" defined + * in terms of earlier dimensions through an equality? + * + * If so, and if c is not NULL, then return a copy of this equality in *c. + */ +int isl_basic_map_has_defining_equality( + __isl_keep isl_basic_map *bmap, enum isl_dim_type type, int pos, + __isl_give isl_constraint **c) +{ + int i; + unsigned offset; + unsigned total; + + if (!bmap) + return -1; + offset = basic_map_offset(bmap, type); + total = isl_basic_map_total_dim(bmap); + isl_assert(bmap->ctx, pos < isl_basic_map_dim(bmap, type), return -1); + for (i = 0; i < bmap->n_eq; ++i) { + if (isl_int_is_zero(bmap->eq[i][offset + pos]) || + isl_seq_first_non_zero(bmap->eq[i]+offset+pos+1, + 1+total-offset-pos-1) != -1) + continue; + if (c) + *c = isl_basic_map_constraint(isl_basic_map_copy(bmap), + &bmap->eq[i]); + return 1; + } + return 0; +} + +/* Is the variable of "type" at position "pos" of "bset" defined + * in terms of earlier dimensions through an equality? + * + * If so, and if c is not NULL, then return a copy of this equality in *c. + */ +int isl_basic_set_has_defining_equality( + __isl_keep isl_basic_set *bset, enum isl_dim_type type, int pos, + __isl_give isl_constraint **c) +{ + return isl_basic_map_has_defining_equality((isl_basic_map *)bset, + type, pos, c); +} + +int isl_basic_set_has_defining_inequalities( + struct isl_basic_set *bset, enum isl_dim_type type, int pos, + struct isl_constraint **lower, + struct isl_constraint **upper) +{ + int i, j; + unsigned offset; + unsigned total; + isl_int m; + isl_int **lower_line, **upper_line; + + if (!bset) + return -1; + offset = basic_set_offset(bset, type); + total = isl_basic_set_total_dim(bset); + isl_assert(bset->ctx, pos < isl_basic_set_dim(bset, type), return -1); + isl_int_init(m); + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_int_is_zero(bset->ineq[i][offset + pos])) + continue; + if (isl_int_is_one(bset->ineq[i][offset + pos])) + continue; + if (isl_int_is_negone(bset->ineq[i][offset + pos])) + continue; + if (isl_seq_first_non_zero(bset->ineq[i]+offset+pos+1, + 1+total-offset-pos-1) != -1) + continue; + for (j = i + 1; j < bset->n_ineq; ++j) { + if (!isl_seq_is_neg(bset->ineq[i]+1, bset->ineq[j]+1, + total)) + continue; + isl_int_add(m, bset->ineq[i][0], bset->ineq[j][0]); + if (isl_int_abs_ge(m, bset->ineq[i][offset+pos])) + continue; + + if (isl_int_is_pos(bset->ineq[i][offset+pos])) { + lower_line = &bset->ineq[i]; + upper_line = &bset->ineq[j]; + } else { + lower_line = &bset->ineq[j]; + upper_line = &bset->ineq[i]; + } + *lower = isl_basic_set_constraint( + isl_basic_set_copy(bset), lower_line); + *upper = isl_basic_set_constraint( + isl_basic_set_copy(bset), upper_line); + isl_int_clear(m); + return 1; + } + } + *lower = NULL; + *upper = NULL; + isl_int_clear(m); + return 0; +} + +/* Given two constraints "a" and "b" on the variable at position "abs_pos" + * (in "a" and "b"), add a constraint to "bset" that ensures that the + * bound implied by "a" is (strictly) larger than the bound implied by "b". + * + * If both constraints imply lower bounds, then this means that "a" is + * active in the result. + * If both constraints imply upper bounds, then this means that "b" is + * active in the result. + */ +static __isl_give isl_basic_set *add_larger_bound_constraint( + __isl_take isl_basic_set *bset, isl_int *a, isl_int *b, + unsigned abs_pos, int strict) +{ + int k; + isl_int t; + unsigned total; + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + + total = isl_basic_set_dim(bset, isl_dim_all); + + isl_int_init(t); + isl_int_neg(t, b[1 + abs_pos]); + + isl_seq_combine(bset->ineq[k], t, a, a[1 + abs_pos], b, 1 + abs_pos); + isl_seq_combine(bset->ineq[k] + 1 + abs_pos, + t, a + 1 + abs_pos + 1, a[1 + abs_pos], b + 1 + abs_pos + 1, + total - abs_pos); + + if (strict) + isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1); + + isl_int_clear(t); + + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Add constraints to "context" that ensure that "u" is the smallest + * (and therefore active) upper bound on "abs_pos" in "bset" and return + * the resulting basic set. + */ +static __isl_give isl_basic_set *set_smallest_upper_bound( + __isl_keep isl_basic_set *context, + __isl_keep isl_basic_set *bset, unsigned abs_pos, int n_upper, int u) +{ + int j; + + context = isl_basic_set_copy(context); + context = isl_basic_set_cow(context); + + context = isl_basic_set_extend_constraints(context, 0, n_upper - 1); + + for (j = 0; j < bset->n_ineq; ++j) { + if (j == u) + continue; + if (!isl_int_is_neg(bset->ineq[j][1 + abs_pos])) + continue; + context = add_larger_bound_constraint(context, + bset->ineq[j], bset->ineq[u], abs_pos, j > u); + } + + context = isl_basic_set_simplify(context); + context = isl_basic_set_finalize(context); + + return context; +} + +/* Add constraints to "context" that ensure that "u" is the largest + * (and therefore active) upper bound on "abs_pos" in "bset" and return + * the resulting basic set. + */ +static __isl_give isl_basic_set *set_largest_lower_bound( + __isl_keep isl_basic_set *context, + __isl_keep isl_basic_set *bset, unsigned abs_pos, int n_lower, int l) +{ + int j; + + context = isl_basic_set_copy(context); + context = isl_basic_set_cow(context); + + context = isl_basic_set_extend_constraints(context, 0, n_lower - 1); + + for (j = 0; j < bset->n_ineq; ++j) { + if (j == l) + continue; + if (!isl_int_is_pos(bset->ineq[j][1 + abs_pos])) + continue; + context = add_larger_bound_constraint(context, + bset->ineq[l], bset->ineq[j], abs_pos, j > l); + } + + context = isl_basic_set_simplify(context); + context = isl_basic_set_finalize(context); + + return context; +} + +static isl_stat foreach_upper_bound(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned abs_pos, + __isl_take isl_basic_set *context, int n_upper, + isl_stat (*fn)(__isl_take isl_constraint *lower, + __isl_take isl_constraint *upper, + __isl_take isl_basic_set *bset, void *user), void *user) +{ + isl_basic_set *context_i; + isl_constraint *upper = NULL; + int i; + + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_int_is_zero(bset->ineq[i][1 + abs_pos])) + continue; + + context_i = set_smallest_upper_bound(context, bset, + abs_pos, n_upper, i); + if (isl_basic_set_is_empty(context_i)) { + isl_basic_set_free(context_i); + continue; + } + upper = isl_basic_set_constraint(isl_basic_set_copy(bset), + &bset->ineq[i]); + if (!upper || !context_i) + goto error; + if (fn(NULL, upper, context_i, user) < 0) + break; + } + + isl_basic_set_free(context); + + if (i < bset->n_ineq) + return isl_stat_error; + + return isl_stat_ok; +error: + isl_constraint_free(upper); + isl_basic_set_free(context_i); + isl_basic_set_free(context); + return isl_stat_error; +} + +static isl_stat foreach_lower_bound(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned abs_pos, + __isl_take isl_basic_set *context, int n_lower, + isl_stat (*fn)(__isl_take isl_constraint *lower, + __isl_take isl_constraint *upper, + __isl_take isl_basic_set *bset, void *user), void *user) +{ + isl_basic_set *context_i; + isl_constraint *lower = NULL; + int i; + + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_int_is_zero(bset->ineq[i][1 + abs_pos])) + continue; + + context_i = set_largest_lower_bound(context, bset, + abs_pos, n_lower, i); + if (isl_basic_set_is_empty(context_i)) { + isl_basic_set_free(context_i); + continue; + } + lower = isl_basic_set_constraint(isl_basic_set_copy(bset), + &bset->ineq[i]); + if (!lower || !context_i) + goto error; + if (fn(lower, NULL, context_i, user) < 0) + break; + } + + isl_basic_set_free(context); + + if (i < bset->n_ineq) + return isl_stat_error; + + return isl_stat_ok; +error: + isl_constraint_free(lower); + isl_basic_set_free(context_i); + isl_basic_set_free(context); + return isl_stat_error; +} + +static isl_stat foreach_bound_pair(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned abs_pos, + __isl_take isl_basic_set *context, int n_lower, int n_upper, + isl_stat (*fn)(__isl_take isl_constraint *lower, + __isl_take isl_constraint *upper, + __isl_take isl_basic_set *bset, void *user), void *user) +{ + isl_basic_set *context_i, *context_j; + isl_constraint *lower = NULL; + isl_constraint *upper = NULL; + int i, j; + + for (i = 0; i < bset->n_ineq; ++i) { + if (!isl_int_is_pos(bset->ineq[i][1 + abs_pos])) + continue; + + context_i = set_largest_lower_bound(context, bset, + abs_pos, n_lower, i); + if (isl_basic_set_is_empty(context_i)) { + isl_basic_set_free(context_i); + continue; + } + + for (j = 0; j < bset->n_ineq; ++j) { + if (!isl_int_is_neg(bset->ineq[j][1 + abs_pos])) + continue; + + context_j = set_smallest_upper_bound(context_i, bset, + abs_pos, n_upper, j); + context_j = isl_basic_set_extend_constraints(context_j, + 0, 1); + context_j = add_larger_bound_constraint(context_j, + bset->ineq[i], bset->ineq[j], abs_pos, 0); + context_j = isl_basic_set_simplify(context_j); + context_j = isl_basic_set_finalize(context_j); + if (isl_basic_set_is_empty(context_j)) { + isl_basic_set_free(context_j); + continue; + } + lower = isl_basic_set_constraint(isl_basic_set_copy(bset), + &bset->ineq[i]); + upper = isl_basic_set_constraint(isl_basic_set_copy(bset), + &bset->ineq[j]); + if (!lower || !upper || !context_j) + goto error; + if (fn(lower, upper, context_j, user) < 0) + break; + } + + isl_basic_set_free(context_i); + + if (j < bset->n_ineq) + break; + } + + isl_basic_set_free(context); + + if (i < bset->n_ineq) + return isl_stat_error; + + return isl_stat_ok; +error: + isl_constraint_free(lower); + isl_constraint_free(upper); + isl_basic_set_free(context_i); + isl_basic_set_free(context_j); + isl_basic_set_free(context); + return isl_stat_error; +} + +/* For each pair of lower and upper bounds on the variable "pos" + * of type "type", call "fn" with these lower and upper bounds and the + * set of constraints on the remaining variables where these bounds + * are active, i.e., (stricly) larger/smaller than the other lower/upper bounds. + * + * If the designated variable is equal to an affine combination of the + * other variables then fn is called with both lower and upper + * set to the corresponding equality. + * + * If there is no lower (or upper) bound, then NULL is passed + * as the corresponding bound. + * + * We first check if the variable is involved in any equality. + * If not, we count the number of lower and upper bounds and + * act accordingly. + */ +isl_stat isl_basic_set_foreach_bound_pair(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, + isl_stat (*fn)(__isl_take isl_constraint *lower, + __isl_take isl_constraint *upper, + __isl_take isl_basic_set *bset, void *user), void *user) +{ + int i; + isl_constraint *lower = NULL; + isl_constraint *upper = NULL; + isl_basic_set *context = NULL; + unsigned abs_pos; + int n_lower, n_upper; + + if (!bset) + return isl_stat_error; + isl_assert(bset->ctx, pos < isl_basic_set_dim(bset, type), + return isl_stat_error); + isl_assert(bset->ctx, type == isl_dim_param || type == isl_dim_set, + return isl_stat_error); + + abs_pos = pos; + if (type == isl_dim_set) + abs_pos += isl_basic_set_dim(bset, isl_dim_param); + + for (i = 0; i < bset->n_eq; ++i) { + if (isl_int_is_zero(bset->eq[i][1 + abs_pos])) + continue; + + lower = isl_basic_set_constraint(isl_basic_set_copy(bset), + &bset->eq[i]); + upper = isl_constraint_copy(lower); + context = isl_basic_set_remove_dims(isl_basic_set_copy(bset), + type, pos, 1); + if (!lower || !upper || !context) + goto error; + return fn(lower, upper, context, user); + } + + n_lower = 0; + n_upper = 0; + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_int_is_pos(bset->ineq[i][1 + abs_pos])) + n_lower++; + else if (isl_int_is_neg(bset->ineq[i][1 + abs_pos])) + n_upper++; + } + + context = isl_basic_set_copy(bset); + context = isl_basic_set_cow(context); + if (!context) + goto error; + for (i = context->n_ineq - 1; i >= 0; --i) + if (!isl_int_is_zero(context->ineq[i][1 + abs_pos])) + isl_basic_set_drop_inequality(context, i); + + context = isl_basic_set_drop(context, type, pos, 1); + if (!n_lower && !n_upper) + return fn(NULL, NULL, context, user); + if (!n_lower) + return foreach_upper_bound(bset, type, abs_pos, context, n_upper, + fn, user); + if (!n_upper) + return foreach_lower_bound(bset, type, abs_pos, context, n_lower, + fn, user); + return foreach_bound_pair(bset, type, abs_pos, context, n_lower, n_upper, + fn, user); +error: + isl_constraint_free(lower); + isl_constraint_free(upper); + isl_basic_set_free(context); + return -1; +} + +__isl_give isl_aff *isl_constraint_get_bound( + __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos) +{ + isl_aff *aff; + isl_ctx *ctx; + + if (!constraint) + return NULL; + ctx = isl_constraint_get_ctx(constraint); + if (pos >= isl_constraint_dim(constraint, type)) + isl_die(ctx, isl_error_invalid, + "index out of bounds", return NULL); + if (isl_constraint_dim(constraint, isl_dim_in) != 0) + isl_die(ctx, isl_error_invalid, + "not a set constraint", return NULL); + + pos += offset(constraint, type); + if (isl_int_is_zero(constraint->v->el[pos])) + isl_die(ctx, isl_error_invalid, + "constraint does not define a bound on given dimension", + return NULL); + + aff = isl_aff_alloc(isl_local_space_copy(constraint->ls)); + if (!aff) + return NULL; + + if (isl_int_is_neg(constraint->v->el[pos])) + isl_seq_cpy(aff->v->el + 1, constraint->v->el, aff->v->size - 1); + else + isl_seq_neg(aff->v->el + 1, constraint->v->el, aff->v->size - 1); + isl_int_set_si(aff->v->el[1 + pos], 0); + isl_int_abs(aff->v->el[0], constraint->v->el[pos]); + + return aff; +} + +/* For an inequality constraint + * + * f >= 0 + * + * or an equality constraint + * + * f = 0 + * + * return the affine expression f. + */ +__isl_give isl_aff *isl_constraint_get_aff( + __isl_keep isl_constraint *constraint) +{ + isl_aff *aff; + + if (!constraint) + return NULL; + + aff = isl_aff_alloc(isl_local_space_copy(constraint->ls)); + if (!aff) + return NULL; + + isl_seq_cpy(aff->v->el + 1, constraint->v->el, aff->v->size - 1); + isl_int_set_si(aff->v->el[0], 1); + + return aff; +} + +/* Construct an inequality (eq = 0) or equality (eq = 1) constraint from "aff". + * In particular, construct aff >= 0 or aff = 0. + * + * The denominator of "aff" can be ignored. + */ +static __isl_give isl_constraint *isl_constraint_alloc_aff(int eq, + __isl_take isl_aff *aff) +{ + isl_local_space *ls; + isl_vec *v; + + if (!aff) + return NULL; + ls = isl_aff_get_domain_local_space(aff); + v = isl_vec_drop_els(isl_vec_copy(aff->v), 0, 1); + isl_aff_free(aff); + + return isl_constraint_alloc_vec(eq, ls, v); +} + +/* Construct an equality constraint equating the given affine expression + * to zero. + */ +__isl_give isl_constraint *isl_equality_from_aff(__isl_take isl_aff *aff) +{ + return isl_constraint_alloc_aff(1, aff); +} + +/* Construct an inequality constraint enforcing the given affine expression + * to be non-negative. + */ +__isl_give isl_constraint *isl_inequality_from_aff(__isl_take isl_aff *aff) +{ + return isl_constraint_alloc_aff(0, aff); +} + +/* Compare two isl_constraints. + * + * Return -1 if "c1" is "smaller" than "c2", 1 if "c1" is "greater" + * than "c2" and 0 if they are equal. + * + * The order is fairly arbitrary. We do consider constraints that only involve + * earlier dimensions as "smaller". + */ +int isl_constraint_plain_cmp(__isl_keep isl_constraint *c1, + __isl_keep isl_constraint *c2) +{ + int cmp; + int last1, last2; + + if (c1 == c2) + return 0; + if (!c1) + return -1; + if (!c2) + return 1; + cmp = isl_local_space_cmp(c1->ls, c2->ls); + if (cmp != 0) + return cmp; + + last1 = isl_seq_last_non_zero(c1->v->el + 1, c1->v->size - 1); + last2 = isl_seq_last_non_zero(c2->v->el + 1, c1->v->size - 1); + if (last1 != last2) + return last1 - last2; + + return isl_seq_cmp(c1->v->el, c2->v->el, c1->v->size); +} + +/* Compare two constraints based on their final (non-zero) coefficients. + * In particular, the constraint that involves later variables or + * that has a larger coefficient for a shared latest variable + * is considered "greater" than the other constraint. + * + * Return -1 if "c1" is "smaller" than "c2", 1 if "c1" is "greater" + * than "c2" and 0 if they are equal. + * + * If the constraints live in different local spaces, then we cannot + * really compare the constraints so we compare the local spaces instead. + */ +int isl_constraint_cmp_last_non_zero(__isl_keep isl_constraint *c1, + __isl_keep isl_constraint *c2) +{ + int cmp; + int last1, last2; + + if (c1 == c2) + return 0; + if (!c1) + return -1; + if (!c2) + return 1; + cmp = isl_local_space_cmp(c1->ls, c2->ls); + if (cmp != 0) + return cmp; + + last1 = isl_seq_last_non_zero(c1->v->el + 1, c1->v->size - 1); + last2 = isl_seq_last_non_zero(c2->v->el + 1, c1->v->size - 1); + if (last1 != last2) + return last1 - last2; + if (last1 == -1) + return 0; + return isl_int_abs_cmp(c1->v->el[1 + last1], c2->v->el[1 + last2]); +} Index: lib/Analysis/isl/isl_constraint_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_constraint_private.h @@ -0,0 +1,32 @@ +#ifndef ISL_CONSTRAINT_PRIVATE_H +#define ISL_CONSTRAINT_PRIVATE_H + +#include +#include +#include + +struct isl_constraint { + int ref; + + int eq; + isl_local_space *ls; + isl_vec *v; +}; + +#undef EL +#define EL isl_constraint + +#include + +struct isl_constraint *isl_basic_set_constraint(struct isl_basic_set *bset, + isl_int **line); + +void isl_constraint_get_coefficient(__isl_keep isl_constraint *constraint, + enum isl_dim_type type, int pos, isl_int *v); +__isl_give isl_constraint *isl_constraint_set_constant( + __isl_take isl_constraint *constraint, isl_int v); +__isl_give isl_constraint *isl_constraint_set_coefficient( + __isl_take isl_constraint *constraint, + enum isl_dim_type type, int pos, isl_int v); + +#endif Index: lib/Analysis/isl/isl_convex_hull.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_convex_hull.c @@ -0,0 +1,3121 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "isl_equalities.h" +#include "isl_tab.h" +#include + +static struct isl_basic_set *uset_convex_hull_wrap_bounded(struct isl_set *set); + +/* Return 1 if constraint c is redundant with respect to the constraints + * in bmap. If c is a lower [upper] bound in some variable and bmap + * does not have a lower [upper] bound in that variable, then c cannot + * be redundant and we do not need solve any lp. + */ +int isl_basic_map_constraint_is_redundant(struct isl_basic_map **bmap, + isl_int *c, isl_int *opt_n, isl_int *opt_d) +{ + enum isl_lp_result res; + unsigned total; + int i, j; + + if (!bmap) + return -1; + + total = isl_basic_map_total_dim(*bmap); + for (i = 0; i < total; ++i) { + int sign; + if (isl_int_is_zero(c[1+i])) + continue; + sign = isl_int_sgn(c[1+i]); + for (j = 0; j < (*bmap)->n_ineq; ++j) + if (sign == isl_int_sgn((*bmap)->ineq[j][1+i])) + break; + if (j == (*bmap)->n_ineq) + break; + } + if (i < total) + return 0; + + res = isl_basic_map_solve_lp(*bmap, 0, c, (*bmap)->ctx->one, + opt_n, opt_d, NULL); + if (res == isl_lp_unbounded) + return 0; + if (res == isl_lp_error) + return -1; + if (res == isl_lp_empty) { + *bmap = isl_basic_map_set_to_empty(*bmap); + return 0; + } + return !isl_int_is_neg(*opt_n); +} + +int isl_basic_set_constraint_is_redundant(struct isl_basic_set **bset, + isl_int *c, isl_int *opt_n, isl_int *opt_d) +{ + return isl_basic_map_constraint_is_redundant( + (struct isl_basic_map **)bset, c, opt_n, opt_d); +} + +/* Remove redundant + * constraints. If the minimal value along the normal of a constraint + * is the same if the constraint is removed, then the constraint is redundant. + * + * Since some constraints may be mutually redundant, sort the constraints + * first such that constraints that involve existentially quantified + * variables are considered for removal before those that do not. + * The sorting is also need for the use in map_simple_hull. + * + * Alternatively, we could have intersected the basic map with the + * corresponding equality and then checked if the dimension was that + * of a facet. + */ +__isl_give isl_basic_map *isl_basic_map_remove_redundancies( + __isl_take isl_basic_map *bmap) +{ + struct isl_tab *tab; + + if (!bmap) + return NULL; + + bmap = isl_basic_map_gauss(bmap, NULL); + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NO_REDUNDANT)) + return bmap; + if (bmap->n_ineq <= 1) + return bmap; + + bmap = isl_basic_map_sort_constraints(bmap); + tab = isl_tab_from_basic_map(bmap, 0); + if (isl_tab_detect_implicit_equalities(tab) < 0) + goto error; + if (isl_tab_detect_redundant(tab) < 0) + goto error; + bmap = isl_basic_map_update_from_tab(bmap, tab); + isl_tab_free(tab); + if (!bmap) + return NULL; + ISL_F_SET(bmap, ISL_BASIC_MAP_NO_IMPLICIT); + ISL_F_SET(bmap, ISL_BASIC_MAP_NO_REDUNDANT); + return bmap; +error: + isl_tab_free(tab); + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_remove_redundancies( + __isl_take isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_remove_redundancies((struct isl_basic_map *)bset); +} + +/* Remove redundant constraints in each of the basic maps. + */ +__isl_give isl_map *isl_map_remove_redundancies(__isl_take isl_map *map) +{ + return isl_map_inline_foreach_basic_map(map, + &isl_basic_map_remove_redundancies); +} + +__isl_give isl_set *isl_set_remove_redundancies(__isl_take isl_set *set) +{ + return isl_map_remove_redundancies(set); +} + +/* Check if the set set is bound in the direction of the affine + * constraint c and if so, set the constant term such that the + * resulting constraint is a bounding constraint for the set. + */ +static int uset_is_bound(struct isl_set *set, isl_int *c, unsigned len) +{ + int first; + int j; + isl_int opt; + isl_int opt_denom; + + isl_int_init(opt); + isl_int_init(opt_denom); + first = 1; + for (j = 0; j < set->n; ++j) { + enum isl_lp_result res; + + if (ISL_F_ISSET(set->p[j], ISL_BASIC_SET_EMPTY)) + continue; + + res = isl_basic_set_solve_lp(set->p[j], + 0, c, set->ctx->one, &opt, &opt_denom, NULL); + if (res == isl_lp_unbounded) + break; + if (res == isl_lp_error) + goto error; + if (res == isl_lp_empty) { + set->p[j] = isl_basic_set_set_to_empty(set->p[j]); + if (!set->p[j]) + goto error; + continue; + } + if (first || isl_int_is_neg(opt)) { + if (!isl_int_is_one(opt_denom)) + isl_seq_scale(c, c, opt_denom, len); + isl_int_sub(c[0], c[0], opt); + } + first = 0; + } + isl_int_clear(opt); + isl_int_clear(opt_denom); + return j >= set->n; +error: + isl_int_clear(opt); + isl_int_clear(opt_denom); + return -1; +} + +__isl_give isl_basic_map *isl_basic_map_set_rational( + __isl_take isl_basic_set *bmap) +{ + if (!bmap) + return NULL; + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + ISL_F_SET(bmap, ISL_BASIC_MAP_RATIONAL); + + return isl_basic_map_finalize(bmap); +} + +__isl_give isl_basic_set *isl_basic_set_set_rational( + __isl_take isl_basic_set *bset) +{ + return isl_basic_map_set_rational(bset); +} + +__isl_give isl_map *isl_map_set_rational(__isl_take isl_map *map) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_set_rational(map->p[i]); + if (!map->p[i]) + goto error; + } + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_set_rational(__isl_take isl_set *set) +{ + return isl_map_set_rational(set); +} + +static struct isl_basic_set *isl_basic_set_add_equality( + struct isl_basic_set *bset, isl_int *c) +{ + int i; + unsigned dim; + + if (!bset) + return NULL; + + if (ISL_F_ISSET(bset, ISL_BASIC_SET_EMPTY)) + return bset; + + isl_assert(bset->ctx, isl_basic_set_n_param(bset) == 0, goto error); + isl_assert(bset->ctx, bset->n_div == 0, goto error); + dim = isl_basic_set_n_dim(bset); + bset = isl_basic_set_cow(bset); + bset = isl_basic_set_extend(bset, 0, dim, 0, 1, 0); + i = isl_basic_set_alloc_equality(bset); + if (i < 0) + goto error; + isl_seq_cpy(bset->eq[i], c, 1 + dim); + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +static struct isl_set *isl_set_add_basic_set_equality(struct isl_set *set, isl_int *c) +{ + int i; + + set = isl_set_cow(set); + if (!set) + return NULL; + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_add_equality(set->p[i], c); + if (!set->p[i]) + goto error; + } + return set; +error: + isl_set_free(set); + return NULL; +} + +/* Given a union of basic sets, construct the constraints for wrapping + * a facet around one of its ridges. + * In particular, if each of n the d-dimensional basic sets i in "set" + * contains the origin, satisfies the constraints x_1 >= 0 and x_2 >= 0 + * and is defined by the constraints + * [ 1 ] + * A_i [ x ] >= 0 + * + * then the resulting set is of dimension n*(1+d) and has as constraints + * + * [ a_i ] + * A_i [ x_i ] >= 0 + * + * a_i >= 0 + * + * \sum_i x_{i,1} = 1 + */ +static struct isl_basic_set *wrap_constraints(struct isl_set *set) +{ + struct isl_basic_set *lp; + unsigned n_eq; + unsigned n_ineq; + int i, j, k; + unsigned dim, lp_dim; + + if (!set) + return NULL; + + dim = 1 + isl_set_n_dim(set); + n_eq = 1; + n_ineq = set->n; + for (i = 0; i < set->n; ++i) { + n_eq += set->p[i]->n_eq; + n_ineq += set->p[i]->n_ineq; + } + lp = isl_basic_set_alloc(set->ctx, 0, dim * set->n, 0, n_eq, n_ineq); + lp = isl_basic_set_set_rational(lp); + if (!lp) + return NULL; + lp_dim = isl_basic_set_n_dim(lp); + k = isl_basic_set_alloc_equality(lp); + isl_int_set_si(lp->eq[k][0], -1); + for (i = 0; i < set->n; ++i) { + isl_int_set_si(lp->eq[k][1+dim*i], 0); + isl_int_set_si(lp->eq[k][1+dim*i+1], 1); + isl_seq_clr(lp->eq[k]+1+dim*i+2, dim-2); + } + for (i = 0; i < set->n; ++i) { + k = isl_basic_set_alloc_inequality(lp); + isl_seq_clr(lp->ineq[k], 1+lp_dim); + isl_int_set_si(lp->ineq[k][1+dim*i], 1); + + for (j = 0; j < set->p[i]->n_eq; ++j) { + k = isl_basic_set_alloc_equality(lp); + isl_seq_clr(lp->eq[k], 1+dim*i); + isl_seq_cpy(lp->eq[k]+1+dim*i, set->p[i]->eq[j], dim); + isl_seq_clr(lp->eq[k]+1+dim*(i+1), dim*(set->n-i-1)); + } + + for (j = 0; j < set->p[i]->n_ineq; ++j) { + k = isl_basic_set_alloc_inequality(lp); + isl_seq_clr(lp->ineq[k], 1+dim*i); + isl_seq_cpy(lp->ineq[k]+1+dim*i, set->p[i]->ineq[j], dim); + isl_seq_clr(lp->ineq[k]+1+dim*(i+1), dim*(set->n-i-1)); + } + } + return lp; +} + +/* Given a facet "facet" of the convex hull of "set" and a facet "ridge" + * of that facet, compute the other facet of the convex hull that contains + * the ridge. + * + * We first transform the set such that the facet constraint becomes + * + * x_1 >= 0 + * + * I.e., the facet lies in + * + * x_1 = 0 + * + * and on that facet, the constraint that defines the ridge is + * + * x_2 >= 0 + * + * (This transformation is not strictly needed, all that is needed is + * that the ridge contains the origin.) + * + * Since the ridge contains the origin, the cone of the convex hull + * will be of the form + * + * x_1 >= 0 + * x_2 >= a x_1 + * + * with this second constraint defining the new facet. + * The constant a is obtained by settting x_1 in the cone of the + * convex hull to 1 and minimizing x_2. + * Now, each element in the cone of the convex hull is the sum + * of elements in the cones of the basic sets. + * If a_i is the dilation factor of basic set i, then the problem + * we need to solve is + * + * min \sum_i x_{i,2} + * st + * \sum_i x_{i,1} = 1 + * a_i >= 0 + * [ a_i ] + * A [ x_i ] >= 0 + * + * with + * [ 1 ] + * A_i [ x_i ] >= 0 + * + * the constraints of each (transformed) basic set. + * If a = n/d, then the constraint defining the new facet (in the transformed + * space) is + * + * -n x_1 + d x_2 >= 0 + * + * In the original space, we need to take the same combination of the + * corresponding constraints "facet" and "ridge". + * + * If a = -infty = "-1/0", then we just return the original facet constraint. + * This means that the facet is unbounded, but has a bounded intersection + * with the union of sets. + */ +isl_int *isl_set_wrap_facet(__isl_keep isl_set *set, + isl_int *facet, isl_int *ridge) +{ + int i; + isl_ctx *ctx; + struct isl_mat *T = NULL; + struct isl_basic_set *lp = NULL; + struct isl_vec *obj; + enum isl_lp_result res; + isl_int num, den; + unsigned dim; + + if (!set) + return NULL; + ctx = set->ctx; + set = isl_set_copy(set); + set = isl_set_set_rational(set); + + dim = 1 + isl_set_n_dim(set); + T = isl_mat_alloc(ctx, 3, dim); + if (!T) + goto error; + isl_int_set_si(T->row[0][0], 1); + isl_seq_clr(T->row[0]+1, dim - 1); + isl_seq_cpy(T->row[1], facet, dim); + isl_seq_cpy(T->row[2], ridge, dim); + T = isl_mat_right_inverse(T); + set = isl_set_preimage(set, T); + T = NULL; + if (!set) + goto error; + lp = wrap_constraints(set); + obj = isl_vec_alloc(ctx, 1 + dim*set->n); + if (!obj) + goto error; + isl_int_set_si(obj->block.data[0], 0); + for (i = 0; i < set->n; ++i) { + isl_seq_clr(obj->block.data + 1 + dim*i, 2); + isl_int_set_si(obj->block.data[1 + dim*i+2], 1); + isl_seq_clr(obj->block.data + 1 + dim*i+3, dim-3); + } + isl_int_init(num); + isl_int_init(den); + res = isl_basic_set_solve_lp(lp, 0, + obj->block.data, ctx->one, &num, &den, NULL); + if (res == isl_lp_ok) { + isl_int_neg(num, num); + isl_seq_combine(facet, num, facet, den, ridge, dim); + isl_seq_normalize(ctx, facet, dim); + } + isl_int_clear(num); + isl_int_clear(den); + isl_vec_free(obj); + isl_basic_set_free(lp); + isl_set_free(set); + if (res == isl_lp_error) + return NULL; + isl_assert(ctx, res == isl_lp_ok || res == isl_lp_unbounded, + return NULL); + return facet; +error: + isl_basic_set_free(lp); + isl_mat_free(T); + isl_set_free(set); + return NULL; +} + +/* Compute the constraint of a facet of "set". + * + * We first compute the intersection with a bounding constraint + * that is orthogonal to one of the coordinate axes. + * If the affine hull of this intersection has only one equality, + * we have found a facet. + * Otherwise, we wrap the current bounding constraint around + * one of the equalities of the face (one that is not equal to + * the current bounding constraint). + * This process continues until we have found a facet. + * The dimension of the intersection increases by at least + * one on each iteration, so termination is guaranteed. + */ +static __isl_give isl_mat *initial_facet_constraint(__isl_keep isl_set *set) +{ + struct isl_set *slice = NULL; + struct isl_basic_set *face = NULL; + int i; + unsigned dim = isl_set_n_dim(set); + int is_bound; + isl_mat *bounds = NULL; + + isl_assert(set->ctx, set->n > 0, goto error); + bounds = isl_mat_alloc(set->ctx, 1, 1 + dim); + if (!bounds) + return NULL; + + isl_seq_clr(bounds->row[0], dim); + isl_int_set_si(bounds->row[0][1 + dim - 1], 1); + is_bound = uset_is_bound(set, bounds->row[0], 1 + dim); + if (is_bound < 0) + goto error; + isl_assert(set->ctx, is_bound, goto error); + isl_seq_normalize(set->ctx, bounds->row[0], 1 + dim); + bounds->n_row = 1; + + for (;;) { + slice = isl_set_copy(set); + slice = isl_set_add_basic_set_equality(slice, bounds->row[0]); + face = isl_set_affine_hull(slice); + if (!face) + goto error; + if (face->n_eq == 1) { + isl_basic_set_free(face); + break; + } + for (i = 0; i < face->n_eq; ++i) + if (!isl_seq_eq(bounds->row[0], face->eq[i], 1 + dim) && + !isl_seq_is_neg(bounds->row[0], + face->eq[i], 1 + dim)) + break; + isl_assert(set->ctx, i < face->n_eq, goto error); + if (!isl_set_wrap_facet(set, bounds->row[0], face->eq[i])) + goto error; + isl_seq_normalize(set->ctx, bounds->row[0], bounds->n_col); + isl_basic_set_free(face); + } + + return bounds; +error: + isl_basic_set_free(face); + isl_mat_free(bounds); + return NULL; +} + +/* Given the bounding constraint "c" of a facet of the convex hull of "set", + * compute a hyperplane description of the facet, i.e., compute the facets + * of the facet. + * + * We compute an affine transformation that transforms the constraint + * + * [ 1 ] + * c [ x ] = 0 + * + * to the constraint + * + * z_1 = 0 + * + * by computing the right inverse U of a matrix that starts with the rows + * + * [ 1 0 ] + * [ c ] + * + * Then + * [ 1 ] [ 1 ] + * [ x ] = U [ z ] + * and + * [ 1 ] [ 1 ] + * [ z ] = Q [ x ] + * + * with Q = U^{-1} + * Since z_1 is zero, we can drop this variable as well as the corresponding + * column of U to obtain + * + * [ 1 ] [ 1 ] + * [ x ] = U' [ z' ] + * and + * [ 1 ] [ 1 ] + * [ z' ] = Q' [ x ] + * + * with Q' equal to Q, but without the corresponding row. + * After computing the facets of the facet in the z' space, + * we convert them back to the x space through Q. + */ +static struct isl_basic_set *compute_facet(struct isl_set *set, isl_int *c) +{ + struct isl_mat *m, *U, *Q; + struct isl_basic_set *facet = NULL; + struct isl_ctx *ctx; + unsigned dim; + + ctx = set->ctx; + set = isl_set_copy(set); + dim = isl_set_n_dim(set); + m = isl_mat_alloc(set->ctx, 2, 1 + dim); + if (!m) + goto error; + isl_int_set_si(m->row[0][0], 1); + isl_seq_clr(m->row[0]+1, dim); + isl_seq_cpy(m->row[1], c, 1+dim); + U = isl_mat_right_inverse(m); + Q = isl_mat_right_inverse(isl_mat_copy(U)); + U = isl_mat_drop_cols(U, 1, 1); + Q = isl_mat_drop_rows(Q, 1, 1); + set = isl_set_preimage(set, U); + facet = uset_convex_hull_wrap_bounded(set); + facet = isl_basic_set_preimage(facet, Q); + if (facet && facet->n_eq != 0) + isl_die(ctx, isl_error_internal, "unexpected equality", + return isl_basic_set_free(facet)); + return facet; +error: + isl_basic_set_free(facet); + isl_set_free(set); + return NULL; +} + +/* Given an initial facet constraint, compute the remaining facets. + * We do this by running through all facets found so far and computing + * the adjacent facets through wrapping, adding those facets that we + * hadn't already found before. + * + * For each facet we have found so far, we first compute its facets + * in the resulting convex hull. That is, we compute the ridges + * of the resulting convex hull contained in the facet. + * We also compute the corresponding facet in the current approximation + * of the convex hull. There is no need to wrap around the ridges + * in this facet since that would result in a facet that is already + * present in the current approximation. + * + * This function can still be significantly optimized by checking which of + * the facets of the basic sets are also facets of the convex hull and + * using all the facets so far to help in constructing the facets of the + * facets + * and/or + * using the technique in section "3.1 Ridge Generation" of + * "Extended Convex Hull" by Fukuda et al. + */ +static struct isl_basic_set *extend(struct isl_basic_set *hull, + struct isl_set *set) +{ + int i, j, f; + int k; + struct isl_basic_set *facet = NULL; + struct isl_basic_set *hull_facet = NULL; + unsigned dim; + + if (!hull) + return NULL; + + isl_assert(set->ctx, set->n > 0, goto error); + + dim = isl_set_n_dim(set); + + for (i = 0; i < hull->n_ineq; ++i) { + facet = compute_facet(set, hull->ineq[i]); + facet = isl_basic_set_add_equality(facet, hull->ineq[i]); + facet = isl_basic_set_gauss(facet, NULL); + facet = isl_basic_set_normalize_constraints(facet); + hull_facet = isl_basic_set_copy(hull); + hull_facet = isl_basic_set_add_equality(hull_facet, hull->ineq[i]); + hull_facet = isl_basic_set_gauss(hull_facet, NULL); + hull_facet = isl_basic_set_normalize_constraints(hull_facet); + if (!facet || !hull_facet) + goto error; + hull = isl_basic_set_cow(hull); + hull = isl_basic_set_extend_space(hull, + isl_space_copy(hull->dim), 0, 0, facet->n_ineq); + if (!hull) + goto error; + for (j = 0; j < facet->n_ineq; ++j) { + for (f = 0; f < hull_facet->n_ineq; ++f) + if (isl_seq_eq(facet->ineq[j], + hull_facet->ineq[f], 1 + dim)) + break; + if (f < hull_facet->n_ineq) + continue; + k = isl_basic_set_alloc_inequality(hull); + if (k < 0) + goto error; + isl_seq_cpy(hull->ineq[k], hull->ineq[i], 1+dim); + if (!isl_set_wrap_facet(set, hull->ineq[k], facet->ineq[j])) + goto error; + } + isl_basic_set_free(hull_facet); + isl_basic_set_free(facet); + } + hull = isl_basic_set_simplify(hull); + hull = isl_basic_set_finalize(hull); + return hull; +error: + isl_basic_set_free(hull_facet); + isl_basic_set_free(facet); + isl_basic_set_free(hull); + return NULL; +} + +/* Special case for computing the convex hull of a one dimensional set. + * We simply collect the lower and upper bounds of each basic set + * and the biggest of those. + */ +static struct isl_basic_set *convex_hull_1d(struct isl_set *set) +{ + struct isl_mat *c = NULL; + isl_int *lower = NULL; + isl_int *upper = NULL; + int i, j, k; + isl_int a, b; + struct isl_basic_set *hull; + + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_simplify(set->p[i]); + if (!set->p[i]) + goto error; + } + set = isl_set_remove_empty_parts(set); + if (!set) + goto error; + isl_assert(set->ctx, set->n > 0, goto error); + c = isl_mat_alloc(set->ctx, 2, 2); + if (!c) + goto error; + + if (set->p[0]->n_eq > 0) { + isl_assert(set->ctx, set->p[0]->n_eq == 1, goto error); + lower = c->row[0]; + upper = c->row[1]; + if (isl_int_is_pos(set->p[0]->eq[0][1])) { + isl_seq_cpy(lower, set->p[0]->eq[0], 2); + isl_seq_neg(upper, set->p[0]->eq[0], 2); + } else { + isl_seq_neg(lower, set->p[0]->eq[0], 2); + isl_seq_cpy(upper, set->p[0]->eq[0], 2); + } + } else { + for (j = 0; j < set->p[0]->n_ineq; ++j) { + if (isl_int_is_pos(set->p[0]->ineq[j][1])) { + lower = c->row[0]; + isl_seq_cpy(lower, set->p[0]->ineq[j], 2); + } else { + upper = c->row[1]; + isl_seq_cpy(upper, set->p[0]->ineq[j], 2); + } + } + } + + isl_int_init(a); + isl_int_init(b); + for (i = 0; i < set->n; ++i) { + struct isl_basic_set *bset = set->p[i]; + int has_lower = 0; + int has_upper = 0; + + for (j = 0; j < bset->n_eq; ++j) { + has_lower = 1; + has_upper = 1; + if (lower) { + isl_int_mul(a, lower[0], bset->eq[j][1]); + isl_int_mul(b, lower[1], bset->eq[j][0]); + if (isl_int_lt(a, b) && isl_int_is_pos(bset->eq[j][1])) + isl_seq_cpy(lower, bset->eq[j], 2); + if (isl_int_gt(a, b) && isl_int_is_neg(bset->eq[j][1])) + isl_seq_neg(lower, bset->eq[j], 2); + } + if (upper) { + isl_int_mul(a, upper[0], bset->eq[j][1]); + isl_int_mul(b, upper[1], bset->eq[j][0]); + if (isl_int_lt(a, b) && isl_int_is_pos(bset->eq[j][1])) + isl_seq_neg(upper, bset->eq[j], 2); + if (isl_int_gt(a, b) && isl_int_is_neg(bset->eq[j][1])) + isl_seq_cpy(upper, bset->eq[j], 2); + } + } + for (j = 0; j < bset->n_ineq; ++j) { + if (isl_int_is_pos(bset->ineq[j][1])) + has_lower = 1; + if (isl_int_is_neg(bset->ineq[j][1])) + has_upper = 1; + if (lower && isl_int_is_pos(bset->ineq[j][1])) { + isl_int_mul(a, lower[0], bset->ineq[j][1]); + isl_int_mul(b, lower[1], bset->ineq[j][0]); + if (isl_int_lt(a, b)) + isl_seq_cpy(lower, bset->ineq[j], 2); + } + if (upper && isl_int_is_neg(bset->ineq[j][1])) { + isl_int_mul(a, upper[0], bset->ineq[j][1]); + isl_int_mul(b, upper[1], bset->ineq[j][0]); + if (isl_int_gt(a, b)) + isl_seq_cpy(upper, bset->ineq[j], 2); + } + } + if (!has_lower) + lower = NULL; + if (!has_upper) + upper = NULL; + } + isl_int_clear(a); + isl_int_clear(b); + + hull = isl_basic_set_alloc(set->ctx, 0, 1, 0, 0, 2); + hull = isl_basic_set_set_rational(hull); + if (!hull) + goto error; + if (lower) { + k = isl_basic_set_alloc_inequality(hull); + isl_seq_cpy(hull->ineq[k], lower, 2); + } + if (upper) { + k = isl_basic_set_alloc_inequality(hull); + isl_seq_cpy(hull->ineq[k], upper, 2); + } + hull = isl_basic_set_finalize(hull); + isl_set_free(set); + isl_mat_free(c); + return hull; +error: + isl_set_free(set); + isl_mat_free(c); + return NULL; +} + +static struct isl_basic_set *convex_hull_0d(struct isl_set *set) +{ + struct isl_basic_set *convex_hull; + + if (!set) + return NULL; + + if (isl_set_is_empty(set)) + convex_hull = isl_basic_set_empty(isl_space_copy(set->dim)); + else + convex_hull = isl_basic_set_universe(isl_space_copy(set->dim)); + isl_set_free(set); + return convex_hull; +} + +/* Compute the convex hull of a pair of basic sets without any parameters or + * integer divisions using Fourier-Motzkin elimination. + * The convex hull is the set of all points that can be written as + * the sum of points from both basic sets (in homogeneous coordinates). + * We set up the constraints in a space with dimensions for each of + * the three sets and then project out the dimensions corresponding + * to the two original basic sets, retaining only those corresponding + * to the convex hull. + */ +static struct isl_basic_set *convex_hull_pair_elim(struct isl_basic_set *bset1, + struct isl_basic_set *bset2) +{ + int i, j, k; + struct isl_basic_set *bset[2]; + struct isl_basic_set *hull = NULL; + unsigned dim; + + if (!bset1 || !bset2) + goto error; + + dim = isl_basic_set_n_dim(bset1); + hull = isl_basic_set_alloc(bset1->ctx, 0, 2 + 3 * dim, 0, + 1 + dim + bset1->n_eq + bset2->n_eq, + 2 + bset1->n_ineq + bset2->n_ineq); + bset[0] = bset1; + bset[1] = bset2; + for (i = 0; i < 2; ++i) { + for (j = 0; j < bset[i]->n_eq; ++j) { + k = isl_basic_set_alloc_equality(hull); + if (k < 0) + goto error; + isl_seq_clr(hull->eq[k], (i+1) * (1+dim)); + isl_seq_clr(hull->eq[k]+(i+2)*(1+dim), (1-i)*(1+dim)); + isl_seq_cpy(hull->eq[k]+(i+1)*(1+dim), bset[i]->eq[j], + 1+dim); + } + for (j = 0; j < bset[i]->n_ineq; ++j) { + k = isl_basic_set_alloc_inequality(hull); + if (k < 0) + goto error; + isl_seq_clr(hull->ineq[k], (i+1) * (1+dim)); + isl_seq_clr(hull->ineq[k]+(i+2)*(1+dim), (1-i)*(1+dim)); + isl_seq_cpy(hull->ineq[k]+(i+1)*(1+dim), + bset[i]->ineq[j], 1+dim); + } + k = isl_basic_set_alloc_inequality(hull); + if (k < 0) + goto error; + isl_seq_clr(hull->ineq[k], 1+2+3*dim); + isl_int_set_si(hull->ineq[k][(i+1)*(1+dim)], 1); + } + for (j = 0; j < 1+dim; ++j) { + k = isl_basic_set_alloc_equality(hull); + if (k < 0) + goto error; + isl_seq_clr(hull->eq[k], 1+2+3*dim); + isl_int_set_si(hull->eq[k][j], -1); + isl_int_set_si(hull->eq[k][1+dim+j], 1); + isl_int_set_si(hull->eq[k][2*(1+dim)+j], 1); + } + hull = isl_basic_set_set_rational(hull); + hull = isl_basic_set_remove_dims(hull, isl_dim_set, dim, 2*(1+dim)); + hull = isl_basic_set_remove_redundancies(hull); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return hull; +error: + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + isl_basic_set_free(hull); + return NULL; +} + +/* Is the set bounded for each value of the parameters? + */ +int isl_basic_set_is_bounded(__isl_keep isl_basic_set *bset) +{ + struct isl_tab *tab; + int bounded; + + if (!bset) + return -1; + if (isl_basic_set_plain_is_empty(bset)) + return 1; + + tab = isl_tab_from_recession_cone(bset, 1); + bounded = isl_tab_cone_is_bounded(tab); + isl_tab_free(tab); + return bounded; +} + +/* Is the image bounded for each value of the parameters and + * the domain variables? + */ +int isl_basic_map_image_is_bounded(__isl_keep isl_basic_map *bmap) +{ + unsigned nparam = isl_basic_map_dim(bmap, isl_dim_param); + unsigned n_in = isl_basic_map_dim(bmap, isl_dim_in); + int bounded; + + bmap = isl_basic_map_copy(bmap); + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_move_dims(bmap, isl_dim_param, nparam, + isl_dim_in, 0, n_in); + bounded = isl_basic_set_is_bounded((isl_basic_set *)bmap); + isl_basic_map_free(bmap); + + return bounded; +} + +/* Is the set bounded for each value of the parameters? + */ +int isl_set_is_bounded(__isl_keep isl_set *set) +{ + int i; + + if (!set) + return -1; + + for (i = 0; i < set->n; ++i) { + int bounded = isl_basic_set_is_bounded(set->p[i]); + if (!bounded || bounded < 0) + return bounded; + } + return 1; +} + +/* Compute the lineality space of the convex hull of bset1 and bset2. + * + * We first compute the intersection of the recession cone of bset1 + * with the negative of the recession cone of bset2 and then compute + * the linear hull of the resulting cone. + */ +static struct isl_basic_set *induced_lineality_space( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + int i, k; + struct isl_basic_set *lin = NULL; + unsigned dim; + + if (!bset1 || !bset2) + goto error; + + dim = isl_basic_set_total_dim(bset1); + lin = isl_basic_set_alloc_space(isl_basic_set_get_space(bset1), 0, + bset1->n_eq + bset2->n_eq, + bset1->n_ineq + bset2->n_ineq); + lin = isl_basic_set_set_rational(lin); + if (!lin) + goto error; + for (i = 0; i < bset1->n_eq; ++i) { + k = isl_basic_set_alloc_equality(lin); + if (k < 0) + goto error; + isl_int_set_si(lin->eq[k][0], 0); + isl_seq_cpy(lin->eq[k] + 1, bset1->eq[i] + 1, dim); + } + for (i = 0; i < bset1->n_ineq; ++i) { + k = isl_basic_set_alloc_inequality(lin); + if (k < 0) + goto error; + isl_int_set_si(lin->ineq[k][0], 0); + isl_seq_cpy(lin->ineq[k] + 1, bset1->ineq[i] + 1, dim); + } + for (i = 0; i < bset2->n_eq; ++i) { + k = isl_basic_set_alloc_equality(lin); + if (k < 0) + goto error; + isl_int_set_si(lin->eq[k][0], 0); + isl_seq_neg(lin->eq[k] + 1, bset2->eq[i] + 1, dim); + } + for (i = 0; i < bset2->n_ineq; ++i) { + k = isl_basic_set_alloc_inequality(lin); + if (k < 0) + goto error; + isl_int_set_si(lin->ineq[k][0], 0); + isl_seq_neg(lin->ineq[k] + 1, bset2->ineq[i] + 1, dim); + } + + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return isl_basic_set_affine_hull(lin); +error: + isl_basic_set_free(lin); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return NULL; +} + +static struct isl_basic_set *uset_convex_hull(struct isl_set *set); + +/* Given a set and a linear space "lin" of dimension n > 0, + * project the linear space from the set, compute the convex hull + * and then map the set back to the original space. + * + * Let + * + * M x = 0 + * + * describe the linear space. We first compute the Hermite normal + * form H = M U of M = H Q, to obtain + * + * H Q x = 0 + * + * The last n rows of H will be zero, so the last n variables of x' = Q x + * are the one we want to project out. We do this by transforming each + * basic set A x >= b to A U x' >= b and then removing the last n dimensions. + * After computing the convex hull in x'_1, i.e., A' x'_1 >= b', + * we transform the hull back to the original space as A' Q_1 x >= b', + * with Q_1 all but the last n rows of Q. + */ +static struct isl_basic_set *modulo_lineality(struct isl_set *set, + struct isl_basic_set *lin) +{ + unsigned total = isl_basic_set_total_dim(lin); + unsigned lin_dim; + struct isl_basic_set *hull; + struct isl_mat *M, *U, *Q; + + if (!set || !lin) + goto error; + lin_dim = total - lin->n_eq; + M = isl_mat_sub_alloc6(set->ctx, lin->eq, 0, lin->n_eq, 1, total); + M = isl_mat_left_hermite(M, 0, &U, &Q); + if (!M) + goto error; + isl_mat_free(M); + isl_basic_set_free(lin); + + Q = isl_mat_drop_rows(Q, Q->n_row - lin_dim, lin_dim); + + U = isl_mat_lin_to_aff(U); + Q = isl_mat_lin_to_aff(Q); + + set = isl_set_preimage(set, U); + set = isl_set_remove_dims(set, isl_dim_set, total - lin_dim, lin_dim); + hull = uset_convex_hull(set); + hull = isl_basic_set_preimage(hull, Q); + + return hull; +error: + isl_basic_set_free(lin); + isl_set_free(set); + return NULL; +} + +/* Given two polyhedra with as constraints h_{ij} x >= 0 in homegeneous space, + * set up an LP for solving + * + * \sum_j \alpha_{1j} h_{1j} = \sum_j \alpha_{2j} h_{2j} + * + * \alpha{i0} corresponds to the (implicit) positivity constraint 1 >= 0 + * The next \alpha{ij} correspond to the equalities and come in pairs. + * The final \alpha{ij} correspond to the inequalities. + */ +static struct isl_basic_set *valid_direction_lp( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + isl_space *dim; + struct isl_basic_set *lp; + unsigned d; + int n; + int i, j, k; + + if (!bset1 || !bset2) + goto error; + d = 1 + isl_basic_set_total_dim(bset1); + n = 2 + + 2 * bset1->n_eq + bset1->n_ineq + 2 * bset2->n_eq + bset2->n_ineq; + dim = isl_space_set_alloc(bset1->ctx, 0, n); + lp = isl_basic_set_alloc_space(dim, 0, d, n); + if (!lp) + goto error; + for (i = 0; i < n; ++i) { + k = isl_basic_set_alloc_inequality(lp); + if (k < 0) + goto error; + isl_seq_clr(lp->ineq[k] + 1, n); + isl_int_set_si(lp->ineq[k][0], -1); + isl_int_set_si(lp->ineq[k][1 + i], 1); + } + for (i = 0; i < d; ++i) { + k = isl_basic_set_alloc_equality(lp); + if (k < 0) + goto error; + n = 0; + isl_int_set_si(lp->eq[k][n], 0); n++; + /* positivity constraint 1 >= 0 */ + isl_int_set_si(lp->eq[k][n], i == 0); n++; + for (j = 0; j < bset1->n_eq; ++j) { + isl_int_set(lp->eq[k][n], bset1->eq[j][i]); n++; + isl_int_neg(lp->eq[k][n], bset1->eq[j][i]); n++; + } + for (j = 0; j < bset1->n_ineq; ++j) { + isl_int_set(lp->eq[k][n], bset1->ineq[j][i]); n++; + } + /* positivity constraint 1 >= 0 */ + isl_int_set_si(lp->eq[k][n], -(i == 0)); n++; + for (j = 0; j < bset2->n_eq; ++j) { + isl_int_neg(lp->eq[k][n], bset2->eq[j][i]); n++; + isl_int_set(lp->eq[k][n], bset2->eq[j][i]); n++; + } + for (j = 0; j < bset2->n_ineq; ++j) { + isl_int_neg(lp->eq[k][n], bset2->ineq[j][i]); n++; + } + } + lp = isl_basic_set_gauss(lp, NULL); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return lp; +error: + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return NULL; +} + +/* Compute a vector s in the homogeneous space such that > 0 + * for all rays in the homogeneous space of the two cones that correspond + * to the input polyhedra bset1 and bset2. + * + * We compute s as a vector that satisfies + * + * s = \sum_j \alpha_{ij} h_{ij} for i = 1,2 (*) + * + * with h_{ij} the normals of the facets of polyhedron i + * (including the "positivity constraint" 1 >= 0) and \alpha_{ij} + * strictly positive numbers. For simplicity we impose \alpha_{ij} >= 1. + * We first set up an LP with as variables the \alpha{ij}. + * In this formulation, for each polyhedron i, + * the first constraint is the positivity constraint, followed by pairs + * of variables for the equalities, followed by variables for the inequalities. + * We then simply pick a feasible solution and compute s using (*). + * + * Note that we simply pick any valid direction and make no attempt + * to pick a "good" or even the "best" valid direction. + */ +static struct isl_vec *valid_direction( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + struct isl_basic_set *lp; + struct isl_tab *tab; + struct isl_vec *sample = NULL; + struct isl_vec *dir; + unsigned d; + int i; + int n; + + if (!bset1 || !bset2) + goto error; + lp = valid_direction_lp(isl_basic_set_copy(bset1), + isl_basic_set_copy(bset2)); + tab = isl_tab_from_basic_set(lp, 0); + sample = isl_tab_get_sample_value(tab); + isl_tab_free(tab); + isl_basic_set_free(lp); + if (!sample) + goto error; + d = isl_basic_set_total_dim(bset1); + dir = isl_vec_alloc(bset1->ctx, 1 + d); + if (!dir) + goto error; + isl_seq_clr(dir->block.data + 1, dir->size - 1); + n = 1; + /* positivity constraint 1 >= 0 */ + isl_int_set(dir->block.data[0], sample->block.data[n]); n++; + for (i = 0; i < bset1->n_eq; ++i) { + isl_int_sub(sample->block.data[n], + sample->block.data[n], sample->block.data[n+1]); + isl_seq_combine(dir->block.data, + bset1->ctx->one, dir->block.data, + sample->block.data[n], bset1->eq[i], 1 + d); + + n += 2; + } + for (i = 0; i < bset1->n_ineq; ++i) + isl_seq_combine(dir->block.data, + bset1->ctx->one, dir->block.data, + sample->block.data[n++], bset1->ineq[i], 1 + d); + isl_vec_free(sample); + isl_seq_normalize(bset1->ctx, dir->el, dir->size); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return dir; +error: + isl_vec_free(sample); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return NULL; +} + +/* Given a polyhedron b_i + A_i x >= 0 and a map T = S^{-1}, + * compute b_i' + A_i' x' >= 0, with + * + * [ b_i A_i ] [ y' ] [ y' ] + * [ 1 0 ] S^{-1} [ x' ] >= 0 or [ b_i' A_i' ] [ x' ] >= 0 + * + * In particular, add the "positivity constraint" and then perform + * the mapping. + */ +static struct isl_basic_set *homogeneous_map(struct isl_basic_set *bset, + struct isl_mat *T) +{ + int k; + + if (!bset) + goto error; + bset = isl_basic_set_extend_constraints(bset, 0, 1); + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_clr(bset->ineq[k] + 1, isl_basic_set_total_dim(bset)); + isl_int_set_si(bset->ineq[k][0], 1); + bset = isl_basic_set_preimage(bset, T); + return bset; +error: + isl_mat_free(T); + isl_basic_set_free(bset); + return NULL; +} + +/* Compute the convex hull of a pair of basic sets without any parameters or + * integer divisions, where the convex hull is known to be pointed, + * but the basic sets may be unbounded. + * + * We turn this problem into the computation of a convex hull of a pair + * _bounded_ polyhedra by "changing the direction of the homogeneous + * dimension". This idea is due to Matthias Koeppe. + * + * Consider the cones in homogeneous space that correspond to the + * input polyhedra. The rays of these cones are also rays of the + * polyhedra if the coordinate that corresponds to the homogeneous + * dimension is zero. That is, if the inner product of the rays + * with the homogeneous direction is zero. + * The cones in the homogeneous space can also be considered to + * correspond to other pairs of polyhedra by chosing a different + * homogeneous direction. To ensure that both of these polyhedra + * are bounded, we need to make sure that all rays of the cones + * correspond to vertices and not to rays. + * Let s be a direction such that > 0 for all rays r of both cones. + * Then using s as a homogeneous direction, we obtain a pair of polytopes. + * The vector s is computed in valid_direction. + * + * Note that we need to consider _all_ rays of the cones and not just + * the rays that correspond to rays in the polyhedra. If we were to + * only consider those rays and turn them into vertices, then we + * may inadvertently turn some vertices into rays. + * + * The standard homogeneous direction is the unit vector in the 0th coordinate. + * We therefore transform the two polyhedra such that the selected + * direction is mapped onto this standard direction and then proceed + * with the normal computation. + * Let S be a non-singular square matrix with s as its first row, + * then we want to map the polyhedra to the space + * + * [ y' ] [ y ] [ y ] [ y' ] + * [ x' ] = S [ x ] i.e., [ x ] = S^{-1} [ x' ] + * + * We take S to be the unimodular completion of s to limit the growth + * of the coefficients in the following computations. + * + * Let b_i + A_i x >= 0 be the constraints of polyhedron i. + * We first move to the homogeneous dimension + * + * b_i y + A_i x >= 0 [ b_i A_i ] [ y ] [ 0 ] + * y >= 0 or [ 1 0 ] [ x ] >= [ 0 ] + * + * Then we change directoin + * + * [ b_i A_i ] [ y' ] [ y' ] + * [ 1 0 ] S^{-1} [ x' ] >= 0 or [ b_i' A_i' ] [ x' ] >= 0 + * + * Then we compute the convex hull of the polytopes b_i' + A_i' x' >= 0 + * resulting in b' + A' x' >= 0, which we then convert back + * + * [ y ] [ y ] + * [ b' A' ] S [ x ] >= 0 or [ b A ] [ x ] >= 0 + * + * The polyhedron b + A x >= 0 is then the convex hull of the input polyhedra. + */ +static struct isl_basic_set *convex_hull_pair_pointed( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + struct isl_ctx *ctx = NULL; + struct isl_vec *dir = NULL; + struct isl_mat *T = NULL; + struct isl_mat *T2 = NULL; + struct isl_basic_set *hull; + struct isl_set *set; + + if (!bset1 || !bset2) + goto error; + ctx = isl_basic_set_get_ctx(bset1); + dir = valid_direction(isl_basic_set_copy(bset1), + isl_basic_set_copy(bset2)); + if (!dir) + goto error; + T = isl_mat_alloc(ctx, dir->size, dir->size); + if (!T) + goto error; + isl_seq_cpy(T->row[0], dir->block.data, dir->size); + T = isl_mat_unimodular_complete(T, 1); + T2 = isl_mat_right_inverse(isl_mat_copy(T)); + + bset1 = homogeneous_map(bset1, isl_mat_copy(T2)); + bset2 = homogeneous_map(bset2, T2); + set = isl_set_alloc_space(isl_basic_set_get_space(bset1), 2, 0); + set = isl_set_add_basic_set(set, bset1); + set = isl_set_add_basic_set(set, bset2); + hull = uset_convex_hull(set); + hull = isl_basic_set_preimage(hull, T); + + isl_vec_free(dir); + + return hull; +error: + isl_vec_free(dir); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return NULL; +} + +static struct isl_basic_set *uset_convex_hull_wrap(struct isl_set *set); +static struct isl_basic_set *modulo_affine_hull( + struct isl_set *set, struct isl_basic_set *affine_hull); + +/* Compute the convex hull of a pair of basic sets without any parameters or + * integer divisions. + * + * This function is called from uset_convex_hull_unbounded, which + * means that the complete convex hull is unbounded. Some pairs + * of basic sets may still be bounded, though. + * They may even lie inside a lower dimensional space, in which + * case they need to be handled inside their affine hull since + * the main algorithm assumes that the result is full-dimensional. + * + * If the convex hull of the two basic sets would have a non-trivial + * lineality space, we first project out this lineality space. + */ +static struct isl_basic_set *convex_hull_pair(struct isl_basic_set *bset1, + struct isl_basic_set *bset2) +{ + isl_basic_set *lin, *aff; + int bounded1, bounded2; + + if (bset1->ctx->opt->convex == ISL_CONVEX_HULL_FM) + return convex_hull_pair_elim(bset1, bset2); + + aff = isl_set_affine_hull(isl_basic_set_union(isl_basic_set_copy(bset1), + isl_basic_set_copy(bset2))); + if (!aff) + goto error; + if (aff->n_eq != 0) + return modulo_affine_hull(isl_basic_set_union(bset1, bset2), aff); + isl_basic_set_free(aff); + + bounded1 = isl_basic_set_is_bounded(bset1); + bounded2 = isl_basic_set_is_bounded(bset2); + + if (bounded1 < 0 || bounded2 < 0) + goto error; + + if (bounded1 && bounded2) + return uset_convex_hull_wrap(isl_basic_set_union(bset1, bset2)); + + if (bounded1 || bounded2) + return convex_hull_pair_pointed(bset1, bset2); + + lin = induced_lineality_space(isl_basic_set_copy(bset1), + isl_basic_set_copy(bset2)); + if (!lin) + goto error; + if (isl_basic_set_plain_is_universe(lin)) { + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return lin; + } + if (lin->n_eq < isl_basic_set_total_dim(lin)) { + struct isl_set *set; + set = isl_set_alloc_space(isl_basic_set_get_space(bset1), 2, 0); + set = isl_set_add_basic_set(set, bset1); + set = isl_set_add_basic_set(set, bset2); + return modulo_lineality(set, lin); + } + isl_basic_set_free(lin); + + return convex_hull_pair_pointed(bset1, bset2); +error: + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + return NULL; +} + +/* Compute the lineality space of a basic set. + * We currently do not allow the basic set to have any divs. + * We basically just drop the constants and turn every inequality + * into an equality. + */ +struct isl_basic_set *isl_basic_set_lineality_space(struct isl_basic_set *bset) +{ + int i, k; + struct isl_basic_set *lin = NULL; + unsigned dim; + + if (!bset) + goto error; + isl_assert(bset->ctx, bset->n_div == 0, goto error); + dim = isl_basic_set_total_dim(bset); + + lin = isl_basic_set_alloc_space(isl_basic_set_get_space(bset), 0, dim, 0); + if (!lin) + goto error; + for (i = 0; i < bset->n_eq; ++i) { + k = isl_basic_set_alloc_equality(lin); + if (k < 0) + goto error; + isl_int_set_si(lin->eq[k][0], 0); + isl_seq_cpy(lin->eq[k] + 1, bset->eq[i] + 1, dim); + } + lin = isl_basic_set_gauss(lin, NULL); + if (!lin) + goto error; + for (i = 0; i < bset->n_ineq && lin->n_eq < dim; ++i) { + k = isl_basic_set_alloc_equality(lin); + if (k < 0) + goto error; + isl_int_set_si(lin->eq[k][0], 0); + isl_seq_cpy(lin->eq[k] + 1, bset->ineq[i] + 1, dim); + lin = isl_basic_set_gauss(lin, NULL); + if (!lin) + goto error; + } + isl_basic_set_free(bset); + return lin; +error: + isl_basic_set_free(lin); + isl_basic_set_free(bset); + return NULL; +} + +/* Compute the (linear) hull of the lineality spaces of the basic sets in the + * "underlying" set "set". + */ +static struct isl_basic_set *uset_combined_lineality_space(struct isl_set *set) +{ + int i; + struct isl_set *lin = NULL; + + if (!set) + return NULL; + if (set->n == 0) { + isl_space *dim = isl_set_get_space(set); + isl_set_free(set); + return isl_basic_set_empty(dim); + } + + lin = isl_set_alloc_space(isl_set_get_space(set), set->n, 0); + for (i = 0; i < set->n; ++i) + lin = isl_set_add_basic_set(lin, + isl_basic_set_lineality_space(isl_basic_set_copy(set->p[i]))); + isl_set_free(set); + return isl_set_affine_hull(lin); +} + +/* Compute the convex hull of a set without any parameters or + * integer divisions. + * In each step, we combined two basic sets until only one + * basic set is left. + * The input basic sets are assumed not to have a non-trivial + * lineality space. If any of the intermediate results has + * a non-trivial lineality space, it is projected out. + */ +static struct isl_basic_set *uset_convex_hull_unbounded(struct isl_set *set) +{ + struct isl_basic_set *convex_hull = NULL; + + convex_hull = isl_set_copy_basic_set(set); + set = isl_set_drop_basic_set(set, convex_hull); + if (!set) + goto error; + while (set->n > 0) { + struct isl_basic_set *t; + t = isl_set_copy_basic_set(set); + if (!t) + goto error; + set = isl_set_drop_basic_set(set, t); + if (!set) + goto error; + convex_hull = convex_hull_pair(convex_hull, t); + if (set->n == 0) + break; + t = isl_basic_set_lineality_space(isl_basic_set_copy(convex_hull)); + if (!t) + goto error; + if (isl_basic_set_plain_is_universe(t)) { + isl_basic_set_free(convex_hull); + convex_hull = t; + break; + } + if (t->n_eq < isl_basic_set_total_dim(t)) { + set = isl_set_add_basic_set(set, convex_hull); + return modulo_lineality(set, t); + } + isl_basic_set_free(t); + } + isl_set_free(set); + return convex_hull; +error: + isl_set_free(set); + isl_basic_set_free(convex_hull); + return NULL; +} + +/* Compute an initial hull for wrapping containing a single initial + * facet. + * This function assumes that the given set is bounded. + */ +static struct isl_basic_set *initial_hull(struct isl_basic_set *hull, + struct isl_set *set) +{ + struct isl_mat *bounds = NULL; + unsigned dim; + int k; + + if (!hull) + goto error; + bounds = initial_facet_constraint(set); + if (!bounds) + goto error; + k = isl_basic_set_alloc_inequality(hull); + if (k < 0) + goto error; + dim = isl_set_n_dim(set); + isl_assert(set->ctx, 1 + dim == bounds->n_col, goto error); + isl_seq_cpy(hull->ineq[k], bounds->row[0], bounds->n_col); + isl_mat_free(bounds); + + return hull; +error: + isl_basic_set_free(hull); + isl_mat_free(bounds); + return NULL; +} + +struct max_constraint { + struct isl_mat *c; + int count; + int ineq; +}; + +static int max_constraint_equal(const void *entry, const void *val) +{ + struct max_constraint *a = (struct max_constraint *)entry; + isl_int *b = (isl_int *)val; + + return isl_seq_eq(a->c->row[0] + 1, b, a->c->n_col - 1); +} + +static void update_constraint(struct isl_ctx *ctx, struct isl_hash_table *table, + isl_int *con, unsigned len, int n, int ineq) +{ + struct isl_hash_table_entry *entry; + struct max_constraint *c; + uint32_t c_hash; + + c_hash = isl_seq_get_hash(con + 1, len); + entry = isl_hash_table_find(ctx, table, c_hash, max_constraint_equal, + con + 1, 0); + if (!entry) + return; + c = entry->data; + if (c->count < n) { + isl_hash_table_remove(ctx, table, entry); + return; + } + c->count++; + if (isl_int_gt(c->c->row[0][0], con[0])) + return; + if (isl_int_eq(c->c->row[0][0], con[0])) { + if (ineq) + c->ineq = ineq; + return; + } + c->c = isl_mat_cow(c->c); + isl_int_set(c->c->row[0][0], con[0]); + c->ineq = ineq; +} + +/* Check whether the constraint hash table "table" constains the constraint + * "con". + */ +static int has_constraint(struct isl_ctx *ctx, struct isl_hash_table *table, + isl_int *con, unsigned len, int n) +{ + struct isl_hash_table_entry *entry; + struct max_constraint *c; + uint32_t c_hash; + + c_hash = isl_seq_get_hash(con + 1, len); + entry = isl_hash_table_find(ctx, table, c_hash, max_constraint_equal, + con + 1, 0); + if (!entry) + return 0; + c = entry->data; + if (c->count < n) + return 0; + return isl_int_eq(c->c->row[0][0], con[0]); +} + +/* Check for inequality constraints of a basic set without equalities + * such that the same or more stringent copies of the constraint appear + * in all of the basic sets. Such constraints are necessarily facet + * constraints of the convex hull. + * + * If the resulting basic set is by chance identical to one of + * the basic sets in "set", then we know that this basic set contains + * all other basic sets and is therefore the convex hull of set. + * In this case we set *is_hull to 1. + */ +static struct isl_basic_set *common_constraints(struct isl_basic_set *hull, + struct isl_set *set, int *is_hull) +{ + int i, j, s, n; + int min_constraints; + int best; + struct max_constraint *constraints = NULL; + struct isl_hash_table *table = NULL; + unsigned total; + + *is_hull = 0; + + for (i = 0; i < set->n; ++i) + if (set->p[i]->n_eq == 0) + break; + if (i >= set->n) + return hull; + min_constraints = set->p[i]->n_ineq; + best = i; + for (i = best + 1; i < set->n; ++i) { + if (set->p[i]->n_eq != 0) + continue; + if (set->p[i]->n_ineq >= min_constraints) + continue; + min_constraints = set->p[i]->n_ineq; + best = i; + } + constraints = isl_calloc_array(hull->ctx, struct max_constraint, + min_constraints); + if (!constraints) + return hull; + table = isl_alloc_type(hull->ctx, struct isl_hash_table); + if (isl_hash_table_init(hull->ctx, table, min_constraints)) + goto error; + + total = isl_space_dim(set->dim, isl_dim_all); + for (i = 0; i < set->p[best]->n_ineq; ++i) { + constraints[i].c = isl_mat_sub_alloc6(hull->ctx, + set->p[best]->ineq + i, 0, 1, 0, 1 + total); + if (!constraints[i].c) + goto error; + constraints[i].ineq = 1; + } + for (i = 0; i < min_constraints; ++i) { + struct isl_hash_table_entry *entry; + uint32_t c_hash; + c_hash = isl_seq_get_hash(constraints[i].c->row[0] + 1, total); + entry = isl_hash_table_find(hull->ctx, table, c_hash, + max_constraint_equal, constraints[i].c->row[0] + 1, 1); + if (!entry) + goto error; + isl_assert(hull->ctx, !entry->data, goto error); + entry->data = &constraints[i]; + } + + n = 0; + for (s = 0; s < set->n; ++s) { + if (s == best) + continue; + + for (i = 0; i < set->p[s]->n_eq; ++i) { + isl_int *eq = set->p[s]->eq[i]; + for (j = 0; j < 2; ++j) { + isl_seq_neg(eq, eq, 1 + total); + update_constraint(hull->ctx, table, + eq, total, n, 0); + } + } + for (i = 0; i < set->p[s]->n_ineq; ++i) { + isl_int *ineq = set->p[s]->ineq[i]; + update_constraint(hull->ctx, table, ineq, total, n, + set->p[s]->n_eq == 0); + } + ++n; + } + + for (i = 0; i < min_constraints; ++i) { + if (constraints[i].count < n) + continue; + if (!constraints[i].ineq) + continue; + j = isl_basic_set_alloc_inequality(hull); + if (j < 0) + goto error; + isl_seq_cpy(hull->ineq[j], constraints[i].c->row[0], 1 + total); + } + + for (s = 0; s < set->n; ++s) { + if (set->p[s]->n_eq) + continue; + if (set->p[s]->n_ineq != hull->n_ineq) + continue; + for (i = 0; i < set->p[s]->n_ineq; ++i) { + isl_int *ineq = set->p[s]->ineq[i]; + if (!has_constraint(hull->ctx, table, ineq, total, n)) + break; + } + if (i == set->p[s]->n_ineq) + *is_hull = 1; + } + + isl_hash_table_clear(table); + for (i = 0; i < min_constraints; ++i) + isl_mat_free(constraints[i].c); + free(constraints); + free(table); + return hull; +error: + isl_hash_table_clear(table); + free(table); + if (constraints) + for (i = 0; i < min_constraints; ++i) + isl_mat_free(constraints[i].c); + free(constraints); + return hull; +} + +/* Create a template for the convex hull of "set" and fill it up + * obvious facet constraints, if any. If the result happens to + * be the convex hull of "set" then *is_hull is set to 1. + */ +static struct isl_basic_set *proto_hull(struct isl_set *set, int *is_hull) +{ + struct isl_basic_set *hull; + unsigned n_ineq; + int i; + + n_ineq = 1; + for (i = 0; i < set->n; ++i) { + n_ineq += set->p[i]->n_eq; + n_ineq += set->p[i]->n_ineq; + } + hull = isl_basic_set_alloc_space(isl_space_copy(set->dim), 0, 0, n_ineq); + hull = isl_basic_set_set_rational(hull); + if (!hull) + return NULL; + return common_constraints(hull, set, is_hull); +} + +static struct isl_basic_set *uset_convex_hull_wrap(struct isl_set *set) +{ + struct isl_basic_set *hull; + int is_hull; + + hull = proto_hull(set, &is_hull); + if (hull && !is_hull) { + if (hull->n_ineq == 0) + hull = initial_hull(hull, set); + hull = extend(hull, set); + } + isl_set_free(set); + + return hull; +} + +/* Compute the convex hull of a set without any parameters or + * integer divisions. Depending on whether the set is bounded, + * we pass control to the wrapping based convex hull or + * the Fourier-Motzkin elimination based convex hull. + * We also handle a few special cases before checking the boundedness. + */ +static struct isl_basic_set *uset_convex_hull(struct isl_set *set) +{ + struct isl_basic_set *convex_hull = NULL; + struct isl_basic_set *lin; + + if (isl_set_n_dim(set) == 0) + return convex_hull_0d(set); + + set = isl_set_coalesce(set); + set = isl_set_set_rational(set); + + if (!set) + goto error; + if (!set) + return NULL; + if (set->n == 1) { + convex_hull = isl_basic_set_copy(set->p[0]); + isl_set_free(set); + return convex_hull; + } + if (isl_set_n_dim(set) == 1) + return convex_hull_1d(set); + + if (isl_set_is_bounded(set) && + set->ctx->opt->convex == ISL_CONVEX_HULL_WRAP) + return uset_convex_hull_wrap(set); + + lin = uset_combined_lineality_space(isl_set_copy(set)); + if (!lin) + goto error; + if (isl_basic_set_plain_is_universe(lin)) { + isl_set_free(set); + return lin; + } + if (lin->n_eq < isl_basic_set_total_dim(lin)) + return modulo_lineality(set, lin); + isl_basic_set_free(lin); + + return uset_convex_hull_unbounded(set); +error: + isl_set_free(set); + isl_basic_set_free(convex_hull); + return NULL; +} + +/* This is the core procedure, where "set" is a "pure" set, i.e., + * without parameters or divs and where the convex hull of set is + * known to be full-dimensional. + */ +static struct isl_basic_set *uset_convex_hull_wrap_bounded(struct isl_set *set) +{ + struct isl_basic_set *convex_hull = NULL; + + if (!set) + goto error; + + if (isl_set_n_dim(set) == 0) { + convex_hull = isl_basic_set_universe(isl_space_copy(set->dim)); + isl_set_free(set); + convex_hull = isl_basic_set_set_rational(convex_hull); + return convex_hull; + } + + set = isl_set_set_rational(set); + set = isl_set_coalesce(set); + if (!set) + goto error; + if (set->n == 1) { + convex_hull = isl_basic_set_copy(set->p[0]); + isl_set_free(set); + convex_hull = isl_basic_map_remove_redundancies(convex_hull); + return convex_hull; + } + if (isl_set_n_dim(set) == 1) + return convex_hull_1d(set); + + return uset_convex_hull_wrap(set); +error: + isl_set_free(set); + return NULL; +} + +/* Compute the convex hull of set "set" with affine hull "affine_hull", + * We first remove the equalities (transforming the set), compute the + * convex hull of the transformed set and then add the equalities back + * (after performing the inverse transformation. + */ +static struct isl_basic_set *modulo_affine_hull( + struct isl_set *set, struct isl_basic_set *affine_hull) +{ + struct isl_mat *T; + struct isl_mat *T2; + struct isl_basic_set *dummy; + struct isl_basic_set *convex_hull; + + dummy = isl_basic_set_remove_equalities( + isl_basic_set_copy(affine_hull), &T, &T2); + if (!dummy) + goto error; + isl_basic_set_free(dummy); + set = isl_set_preimage(set, T); + convex_hull = uset_convex_hull(set); + convex_hull = isl_basic_set_preimage(convex_hull, T2); + convex_hull = isl_basic_set_intersect(convex_hull, affine_hull); + return convex_hull; +error: + isl_basic_set_free(affine_hull); + isl_set_free(set); + return NULL; +} + +/* Return an empty basic map living in the same space as "map". + */ +static __isl_give isl_basic_map *replace_map_by_empty_basic_map( + __isl_take isl_map *map) +{ + isl_space *space; + + space = isl_map_get_space(map); + isl_map_free(map); + return isl_basic_map_empty(space); +} + +/* Compute the convex hull of a map. + * + * The implementation was inspired by "Extended Convex Hull" by Fukuda et al., + * specifically, the wrapping of facets to obtain new facets. + */ +struct isl_basic_map *isl_map_convex_hull(struct isl_map *map) +{ + struct isl_basic_set *bset; + struct isl_basic_map *model = NULL; + struct isl_basic_set *affine_hull = NULL; + struct isl_basic_map *convex_hull = NULL; + struct isl_set *set = NULL; + + map = isl_map_detect_equalities(map); + map = isl_map_align_divs(map); + if (!map) + goto error; + + if (map->n == 0) + return replace_map_by_empty_basic_map(map); + + model = isl_basic_map_copy(map->p[0]); + set = isl_map_underlying_set(map); + if (!set) + goto error; + + affine_hull = isl_set_affine_hull(isl_set_copy(set)); + if (!affine_hull) + goto error; + if (affine_hull->n_eq != 0) + bset = modulo_affine_hull(set, affine_hull); + else { + isl_basic_set_free(affine_hull); + bset = uset_convex_hull(set); + } + + convex_hull = isl_basic_map_overlying_set(bset, model); + if (!convex_hull) + return NULL; + + ISL_F_SET(convex_hull, ISL_BASIC_MAP_NO_IMPLICIT); + ISL_F_SET(convex_hull, ISL_BASIC_MAP_ALL_EQUALITIES); + ISL_F_CLR(convex_hull, ISL_BASIC_MAP_RATIONAL); + return convex_hull; +error: + isl_set_free(set); + isl_basic_map_free(model); + return NULL; +} + +struct isl_basic_set *isl_set_convex_hull(struct isl_set *set) +{ + return (struct isl_basic_set *) + isl_map_convex_hull((struct isl_map *)set); +} + +__isl_give isl_basic_map *isl_map_polyhedral_hull(__isl_take isl_map *map) +{ + isl_basic_map *hull; + + hull = isl_map_convex_hull(map); + return isl_basic_map_remove_divs(hull); +} + +__isl_give isl_basic_set *isl_set_polyhedral_hull(__isl_take isl_set *set) +{ + return (isl_basic_set *)isl_map_polyhedral_hull((isl_map *)set); +} + +struct sh_data_entry { + struct isl_hash_table *table; + struct isl_tab *tab; +}; + +/* Holds the data needed during the simple hull computation. + * In particular, + * n the number of basic sets in the original set + * hull_table a hash table of already computed constraints + * in the simple hull + * p for each basic set, + * table a hash table of the constraints + * tab the tableau corresponding to the basic set + */ +struct sh_data { + struct isl_ctx *ctx; + unsigned n; + struct isl_hash_table *hull_table; + struct sh_data_entry p[1]; +}; + +static void sh_data_free(struct sh_data *data) +{ + int i; + + if (!data) + return; + isl_hash_table_free(data->ctx, data->hull_table); + for (i = 0; i < data->n; ++i) { + isl_hash_table_free(data->ctx, data->p[i].table); + isl_tab_free(data->p[i].tab); + } + free(data); +} + +struct ineq_cmp_data { + unsigned len; + isl_int *p; +}; + +static int has_ineq(const void *entry, const void *val) +{ + isl_int *row = (isl_int *)entry; + struct ineq_cmp_data *v = (struct ineq_cmp_data *)val; + + return isl_seq_eq(row + 1, v->p + 1, v->len) || + isl_seq_is_neg(row + 1, v->p + 1, v->len); +} + +static int hash_ineq(struct isl_ctx *ctx, struct isl_hash_table *table, + isl_int *ineq, unsigned len) +{ + uint32_t c_hash; + struct ineq_cmp_data v; + struct isl_hash_table_entry *entry; + + v.len = len; + v.p = ineq; + c_hash = isl_seq_get_hash(ineq + 1, len); + entry = isl_hash_table_find(ctx, table, c_hash, has_ineq, &v, 1); + if (!entry) + return - 1; + entry->data = ineq; + return 0; +} + +/* Fill hash table "table" with the constraints of "bset". + * Equalities are added as two inequalities. + * The value in the hash table is a pointer to the (in)equality of "bset". + */ +static int hash_basic_set(struct isl_hash_table *table, + struct isl_basic_set *bset) +{ + int i, j; + unsigned dim = isl_basic_set_total_dim(bset); + + for (i = 0; i < bset->n_eq; ++i) { + for (j = 0; j < 2; ++j) { + isl_seq_neg(bset->eq[i], bset->eq[i], 1 + dim); + if (hash_ineq(bset->ctx, table, bset->eq[i], dim) < 0) + return -1; + } + } + for (i = 0; i < bset->n_ineq; ++i) { + if (hash_ineq(bset->ctx, table, bset->ineq[i], dim) < 0) + return -1; + } + return 0; +} + +static struct sh_data *sh_data_alloc(struct isl_set *set, unsigned n_ineq) +{ + struct sh_data *data; + int i; + + data = isl_calloc(set->ctx, struct sh_data, + sizeof(struct sh_data) + + (set->n - 1) * sizeof(struct sh_data_entry)); + if (!data) + return NULL; + data->ctx = set->ctx; + data->n = set->n; + data->hull_table = isl_hash_table_alloc(set->ctx, n_ineq); + if (!data->hull_table) + goto error; + for (i = 0; i < set->n; ++i) { + data->p[i].table = isl_hash_table_alloc(set->ctx, + 2 * set->p[i]->n_eq + set->p[i]->n_ineq); + if (!data->p[i].table) + goto error; + if (hash_basic_set(data->p[i].table, set->p[i]) < 0) + goto error; + } + return data; +error: + sh_data_free(data); + return NULL; +} + +/* Check if inequality "ineq" is a bound for basic set "j" or if + * it can be relaxed (by increasing the constant term) to become + * a bound for that basic set. In the latter case, the constant + * term is updated. + * Relaxation of the constant term is only allowed if "shift" is set. + * + * Return 1 if "ineq" is a bound + * 0 if "ineq" may attain arbitrarily small values on basic set "j" + * -1 if some error occurred + */ +static int is_bound(struct sh_data *data, struct isl_set *set, int j, + isl_int *ineq, int shift) +{ + enum isl_lp_result res; + isl_int opt; + + if (!data->p[j].tab) { + data->p[j].tab = isl_tab_from_basic_set(set->p[j], 0); + if (!data->p[j].tab) + return -1; + } + + isl_int_init(opt); + + res = isl_tab_min(data->p[j].tab, ineq, data->ctx->one, + &opt, NULL, 0); + if (res == isl_lp_ok && isl_int_is_neg(opt)) { + if (shift) + isl_int_sub(ineq[0], ineq[0], opt); + else + res = isl_lp_unbounded; + } + + isl_int_clear(opt); + + return (res == isl_lp_ok || res == isl_lp_empty) ? 1 : + res == isl_lp_unbounded ? 0 : -1; +} + +/* Set the constant term of "ineq" to the maximum of those of the constraints + * in the basic sets of "set" following "i" that are parallel to "ineq". + * That is, if any of the basic sets of "set" following "i" have a more + * relaxed copy of "ineq", then replace "ineq" by the most relaxed copy. + * "c_hash" is the hash value of the linear part of "ineq". + * "v" has been set up for use by has_ineq. + * + * Note that the two inequality constraints corresponding to an equality are + * represented by the same inequality constraint in data->p[j].table + * (but with different hash values). This means the constraint (or at + * least its constant term) may need to be temporarily negated to get + * the actually hashed constraint. + */ +static void set_max_constant_term(struct sh_data *data, __isl_keep isl_set *set, + int i, isl_int *ineq, uint32_t c_hash, struct ineq_cmp_data *v) +{ + int j; + isl_ctx *ctx; + struct isl_hash_table_entry *entry; + + ctx = isl_set_get_ctx(set); + for (j = i + 1; j < set->n; ++j) { + int neg; + isl_int *ineq_j; + + entry = isl_hash_table_find(ctx, data->p[j].table, + c_hash, &has_ineq, v, 0); + if (!entry) + continue; + + ineq_j = entry->data; + neg = isl_seq_is_neg(ineq_j + 1, ineq + 1, v->len); + if (neg) + isl_int_neg(ineq_j[0], ineq_j[0]); + if (isl_int_gt(ineq_j[0], ineq[0])) + isl_int_set(ineq[0], ineq_j[0]); + if (neg) + isl_int_neg(ineq_j[0], ineq_j[0]); + } +} + +/* Check if inequality "ineq" from basic set "i" is or can be relaxed to + * become a bound on the whole set. If so, add the (relaxed) inequality + * to "hull". Relaxation is only allowed if "shift" is set. + * + * We first check if "hull" already contains a translate of the inequality. + * If so, we are done. + * Then, we check if any of the previous basic sets contains a translate + * of the inequality. If so, then we have already considered this + * inequality and we are done. + * Otherwise, for each basic set other than "i", we check if the inequality + * is a bound on the basic set, but first replace the constant term + * by the maximal value of any translate of the inequality in any + * of the following basic sets. + * For previous basic sets, we know that they do not contain a translate + * of the inequality, so we directly call is_bound. + * For following basic sets, we first check if a translate of the + * inequality appears in its description. If so, the constant term + * of the inequality has already been updated with respect to this + * translate and the inequality is therefore known to be a bound + * of this basic set. + */ +static struct isl_basic_set *add_bound(struct isl_basic_set *hull, + struct sh_data *data, struct isl_set *set, int i, isl_int *ineq, + int shift) +{ + uint32_t c_hash; + struct ineq_cmp_data v; + struct isl_hash_table_entry *entry; + int j, k; + + if (!hull) + return NULL; + + v.len = isl_basic_set_total_dim(hull); + v.p = ineq; + c_hash = isl_seq_get_hash(ineq + 1, v.len); + + entry = isl_hash_table_find(hull->ctx, data->hull_table, c_hash, + has_ineq, &v, 0); + if (entry) + return hull; + + for (j = 0; j < i; ++j) { + entry = isl_hash_table_find(hull->ctx, data->p[j].table, + c_hash, has_ineq, &v, 0); + if (entry) + break; + } + if (j < i) + return hull; + + k = isl_basic_set_alloc_inequality(hull); + if (k < 0) + goto error; + isl_seq_cpy(hull->ineq[k], ineq, 1 + v.len); + + set_max_constant_term(data, set, i, hull->ineq[k], c_hash, &v); + for (j = 0; j < i; ++j) { + int bound; + bound = is_bound(data, set, j, hull->ineq[k], shift); + if (bound < 0) + goto error; + if (!bound) + break; + } + if (j < i) { + isl_basic_set_free_inequality(hull, 1); + return hull; + } + + for (j = i + 1; j < set->n; ++j) { + int bound; + entry = isl_hash_table_find(hull->ctx, data->p[j].table, + c_hash, has_ineq, &v, 0); + if (entry) + continue; + bound = is_bound(data, set, j, hull->ineq[k], shift); + if (bound < 0) + goto error; + if (!bound) + break; + } + if (j < set->n) { + isl_basic_set_free_inequality(hull, 1); + return hull; + } + + entry = isl_hash_table_find(hull->ctx, data->hull_table, c_hash, + has_ineq, &v, 1); + if (!entry) + goto error; + entry->data = hull->ineq[k]; + + return hull; +error: + isl_basic_set_free(hull); + return NULL; +} + +/* Check if any inequality from basic set "i" is or can be relaxed to + * become a bound on the whole set. If so, add the (relaxed) inequality + * to "hull". Relaxation is only allowed if "shift" is set. + */ +static struct isl_basic_set *add_bounds(struct isl_basic_set *bset, + struct sh_data *data, struct isl_set *set, int i, int shift) +{ + int j, k; + unsigned dim = isl_basic_set_total_dim(bset); + + for (j = 0; j < set->p[i]->n_eq; ++j) { + for (k = 0; k < 2; ++k) { + isl_seq_neg(set->p[i]->eq[j], set->p[i]->eq[j], 1+dim); + bset = add_bound(bset, data, set, i, set->p[i]->eq[j], + shift); + } + } + for (j = 0; j < set->p[i]->n_ineq; ++j) + bset = add_bound(bset, data, set, i, set->p[i]->ineq[j], shift); + return bset; +} + +/* Compute a superset of the convex hull of set that is described + * by only (translates of) the constraints in the constituents of set. + * Translation is only allowed if "shift" is set. + */ +static __isl_give isl_basic_set *uset_simple_hull(__isl_take isl_set *set, + int shift) +{ + struct sh_data *data = NULL; + struct isl_basic_set *hull = NULL; + unsigned n_ineq; + int i; + + if (!set) + return NULL; + + n_ineq = 0; + for (i = 0; i < set->n; ++i) { + if (!set->p[i]) + goto error; + n_ineq += 2 * set->p[i]->n_eq + set->p[i]->n_ineq; + } + + hull = isl_basic_set_alloc_space(isl_space_copy(set->dim), 0, 0, n_ineq); + if (!hull) + goto error; + + data = sh_data_alloc(set, n_ineq); + if (!data) + goto error; + + for (i = 0; i < set->n; ++i) + hull = add_bounds(hull, data, set, i, shift); + + sh_data_free(data); + isl_set_free(set); + + return hull; +error: + sh_data_free(data); + isl_basic_set_free(hull); + isl_set_free(set); + return NULL; +} + +/* Compute a superset of the convex hull of map that is described + * by only (translates of) the constraints in the constituents of map. + * Handle trivial cases where map is NULL or contains at most one disjunct. + */ +static __isl_give isl_basic_map *map_simple_hull_trivial( + __isl_take isl_map *map) +{ + isl_basic_map *hull; + + if (!map) + return NULL; + if (map->n == 0) + return replace_map_by_empty_basic_map(map); + + hull = isl_basic_map_copy(map->p[0]); + isl_map_free(map); + return hull; +} + +/* Return a copy of the simple hull cached inside "map". + * "shift" determines whether to return the cached unshifted or shifted + * simple hull. + */ +static __isl_give isl_basic_map *cached_simple_hull(__isl_take isl_map *map, + int shift) +{ + isl_basic_map *hull; + + hull = isl_basic_map_copy(map->cached_simple_hull[shift]); + isl_map_free(map); + + return hull; +} + +/* Compute a superset of the convex hull of map that is described + * by only (translates of) the constraints in the constituents of map. + * Translation is only allowed if "shift" is set. + * + * The constraints are sorted while removing redundant constraints + * in order to indicate a preference of which constraints should + * be preserved. In particular, pairs of constraints that are + * sorted together are preferred to either both be preserved + * or both be removed. The sorting is performed inside + * isl_basic_map_remove_redundancies. + * + * The result of the computation is stored in map->cached_simple_hull[shift] + * such that it can be reused in subsequent calls. The cache is cleared + * whenever the map is modified (in isl_map_cow). + * Note that the results need to be stored in the input map for there + * to be any chance that they may get reused. In particular, they + * are stored in a copy of the input map that is saved before + * the integer division alignment. + */ +static __isl_give isl_basic_map *map_simple_hull(__isl_take isl_map *map, + int shift) +{ + struct isl_set *set = NULL; + struct isl_basic_map *model = NULL; + struct isl_basic_map *hull; + struct isl_basic_map *affine_hull; + struct isl_basic_set *bset = NULL; + isl_map *input; + + if (!map || map->n <= 1) + return map_simple_hull_trivial(map); + + if (map->cached_simple_hull[shift]) + return cached_simple_hull(map, shift); + + map = isl_map_detect_equalities(map); + if (!map || map->n <= 1) + return map_simple_hull_trivial(map); + affine_hull = isl_map_affine_hull(isl_map_copy(map)); + input = isl_map_copy(map); + map = isl_map_align_divs(map); + model = map ? isl_basic_map_copy(map->p[0]) : NULL; + + set = isl_map_underlying_set(map); + + bset = uset_simple_hull(set, shift); + + hull = isl_basic_map_overlying_set(bset, model); + + hull = isl_basic_map_intersect(hull, affine_hull); + hull = isl_basic_map_remove_redundancies(hull); + + if (hull) { + ISL_F_SET(hull, ISL_BASIC_MAP_NO_IMPLICIT); + ISL_F_SET(hull, ISL_BASIC_MAP_ALL_EQUALITIES); + } + + hull = isl_basic_map_finalize(hull); + if (input) + input->cached_simple_hull[shift] = isl_basic_map_copy(hull); + isl_map_free(input); + + return hull; +} + +/* Compute a superset of the convex hull of map that is described + * by only translates of the constraints in the constituents of map. + */ +__isl_give isl_basic_map *isl_map_simple_hull(__isl_take isl_map *map) +{ + return map_simple_hull(map, 1); +} + +struct isl_basic_set *isl_set_simple_hull(struct isl_set *set) +{ + return (struct isl_basic_set *) + isl_map_simple_hull((struct isl_map *)set); +} + +/* Compute a superset of the convex hull of map that is described + * by only the constraints in the constituents of map. + */ +__isl_give isl_basic_map *isl_map_unshifted_simple_hull( + __isl_take isl_map *map) +{ + return map_simple_hull(map, 0); +} + +__isl_give isl_basic_set *isl_set_unshifted_simple_hull( + __isl_take isl_set *set) +{ + return isl_map_unshifted_simple_hull(set); +} + +/* Drop all inequalities from "bmap1" that do not also appear in "bmap2". + * A constraint that appears with different constant terms + * in "bmap1" and "bmap2" is also kept, with the least restrictive + * (i.e., greatest) constant term. + * "bmap1" and "bmap2" are assumed to have the same (known) + * integer divisions. + * The constraints of both "bmap1" and "bmap2" are assumed + * to have been sorted using isl_basic_map_sort_constraints. + * + * Run through the inequality constraints of "bmap1" and "bmap2" + * in sorted order. + * Each constraint of "bmap1" without a matching constraint in "bmap2" + * is removed. + * If a match is found, the constraint is kept. If needed, the constant + * term of the constraint is adjusted. + */ +static __isl_give isl_basic_map *select_shared_inequalities( + __isl_take isl_basic_map *bmap1, __isl_keep isl_basic_map *bmap2) +{ + int i1, i2; + + bmap1 = isl_basic_map_cow(bmap1); + if (!bmap1 || !bmap2) + return isl_basic_map_free(bmap1); + + i1 = bmap1->n_ineq - 1; + i2 = bmap2->n_ineq - 1; + while (bmap1 && i1 >= 0 && i2 >= 0) { + int cmp; + + cmp = isl_basic_map_constraint_cmp(bmap1, bmap1->ineq[i1], + bmap2->ineq[i2]); + if (cmp < 0) { + --i2; + continue; + } + if (cmp > 0) { + if (isl_basic_map_drop_inequality(bmap1, i1) < 0) + bmap1 = isl_basic_map_free(bmap1); + --i1; + continue; + } + if (isl_int_lt(bmap1->ineq[i1][0], bmap2->ineq[i2][0])) + isl_int_set(bmap1->ineq[i1][0], bmap2->ineq[i2][0]); + --i1; + --i2; + } + for (; i1 >= 0; --i1) + if (isl_basic_map_drop_inequality(bmap1, i1) < 0) + bmap1 = isl_basic_map_free(bmap1); + + return bmap1; +} + +/* Drop all equalities from "bmap1" that do not also appear in "bmap2". + * "bmap1" and "bmap2" are assumed to have the same (known) + * integer divisions. + * + * Run through the equality constraints of "bmap1" and "bmap2". + * Each constraint of "bmap1" without a matching constraint in "bmap2" + * is removed. + */ +static __isl_give isl_basic_map *select_shared_equalities( + __isl_take isl_basic_map *bmap1, __isl_keep isl_basic_map *bmap2) +{ + int i1, i2; + unsigned total; + + bmap1 = isl_basic_map_cow(bmap1); + if (!bmap1 || !bmap2) + return isl_basic_map_free(bmap1); + + total = isl_basic_map_total_dim(bmap1); + + i1 = bmap1->n_eq - 1; + i2 = bmap2->n_eq - 1; + while (bmap1 && i1 >= 0 && i2 >= 0) { + int last1, last2; + + last1 = isl_seq_last_non_zero(bmap1->eq[i1] + 1, total); + last2 = isl_seq_last_non_zero(bmap2->eq[i2] + 1, total); + if (last1 > last2) { + --i2; + continue; + } + if (last1 < last2) { + if (isl_basic_map_drop_equality(bmap1, i1) < 0) + bmap1 = isl_basic_map_free(bmap1); + --i1; + continue; + } + if (!isl_seq_eq(bmap1->eq[i1], bmap2->eq[i2], 1 + total)) { + if (isl_basic_map_drop_equality(bmap1, i1) < 0) + bmap1 = isl_basic_map_free(bmap1); + } + --i1; + --i2; + } + for (; i1 >= 0; --i1) + if (isl_basic_map_drop_equality(bmap1, i1) < 0) + bmap1 = isl_basic_map_free(bmap1); + + return bmap1; +} + +/* Compute a superset of "bmap1" and "bmap2" that is described + * by only the constraints that appear in both "bmap1" and "bmap2". + * + * First drop constraints that involve unknown integer divisions + * since it is not trivial to check whether two such integer divisions + * in different basic maps are the same. + * Then align the remaining (known) divs and sort the constraints. + * Finally drop all inequalities and equalities from "bmap1" that + * do not also appear in "bmap2". + */ +__isl_give isl_basic_map *isl_basic_map_plain_unshifted_simple_hull( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2) +{ + bmap1 = isl_basic_map_drop_constraint_involving_unknown_divs(bmap1); + bmap2 = isl_basic_map_drop_constraint_involving_unknown_divs(bmap2); + bmap2 = isl_basic_map_align_divs(bmap2, bmap1); + bmap1 = isl_basic_map_align_divs(bmap1, bmap2); + bmap1 = isl_basic_map_gauss(bmap1, NULL); + bmap2 = isl_basic_map_gauss(bmap2, NULL); + bmap1 = isl_basic_map_sort_constraints(bmap1); + bmap2 = isl_basic_map_sort_constraints(bmap2); + + bmap1 = select_shared_inequalities(bmap1, bmap2); + bmap1 = select_shared_equalities(bmap1, bmap2); + + isl_basic_map_free(bmap2); + bmap1 = isl_basic_map_finalize(bmap1); + return bmap1; +} + +/* Compute a superset of the convex hull of "map" that is described + * by only the constraints in the constituents of "map". + * In particular, the result is composed of constraints that appear + * in each of the basic maps of "map" + * + * Constraints that involve unknown integer divisions are dropped + * since it is not trivial to check whether two such integer divisions + * in different basic maps are the same. + * + * The hull is initialized from the first basic map and then + * updated with respect to the other basic maps in turn. + */ +__isl_give isl_basic_map *isl_map_plain_unshifted_simple_hull( + __isl_take isl_map *map) +{ + int i; + isl_basic_map *hull; + + if (!map) + return NULL; + if (map->n <= 1) + return map_simple_hull_trivial(map); + map = isl_map_drop_constraint_involving_unknown_divs(map); + hull = isl_basic_map_copy(map->p[0]); + for (i = 1; i < map->n; ++i) { + isl_basic_map *bmap_i; + + bmap_i = isl_basic_map_copy(map->p[i]); + hull = isl_basic_map_plain_unshifted_simple_hull(hull, bmap_i); + } + + isl_map_free(map); + return hull; +} + +/* Compute a superset of the convex hull of "set" that is described + * by only the constraints in the constituents of "set". + * In particular, the result is composed of constraints that appear + * in each of the basic sets of "set" + */ +__isl_give isl_basic_set *isl_set_plain_unshifted_simple_hull( + __isl_take isl_set *set) +{ + return isl_map_plain_unshifted_simple_hull(set); +} + +/* Check if "ineq" is a bound on "set" and, if so, add it to "hull". + * + * For each basic set in "set", we first check if the basic set + * contains a translate of "ineq". If this translate is more relaxed, + * then we assume that "ineq" is not a bound on this basic set. + * Otherwise, we know that it is a bound. + * If the basic set does not contain a translate of "ineq", then + * we call is_bound to perform the test. + */ +static __isl_give isl_basic_set *add_bound_from_constraint( + __isl_take isl_basic_set *hull, struct sh_data *data, + __isl_keep isl_set *set, isl_int *ineq) +{ + int i, k; + isl_ctx *ctx; + uint32_t c_hash; + struct ineq_cmp_data v; + + if (!hull || !set) + return isl_basic_set_free(hull); + + v.len = isl_basic_set_total_dim(hull); + v.p = ineq; + c_hash = isl_seq_get_hash(ineq + 1, v.len); + + ctx = isl_basic_set_get_ctx(hull); + for (i = 0; i < set->n; ++i) { + int bound; + struct isl_hash_table_entry *entry; + + entry = isl_hash_table_find(ctx, data->p[i].table, + c_hash, &has_ineq, &v, 0); + if (entry) { + isl_int *ineq_i = entry->data; + int neg, more_relaxed; + + neg = isl_seq_is_neg(ineq_i + 1, ineq + 1, v.len); + if (neg) + isl_int_neg(ineq_i[0], ineq_i[0]); + more_relaxed = isl_int_gt(ineq_i[0], ineq[0]); + if (neg) + isl_int_neg(ineq_i[0], ineq_i[0]); + if (more_relaxed) + break; + else + continue; + } + bound = is_bound(data, set, i, ineq, 0); + if (bound < 0) + return isl_basic_set_free(hull); + if (!bound) + break; + } + if (i < set->n) + return hull; + + k = isl_basic_set_alloc_inequality(hull); + if (k < 0) + return isl_basic_set_free(hull); + isl_seq_cpy(hull->ineq[k], ineq, 1 + v.len); + + return hull; +} + +/* Compute a superset of the convex hull of "set" that is described + * by only some of the "n_ineq" constraints in the list "ineq", where "set" + * has no parameters or integer divisions. + * + * The inequalities in "ineq" are assumed to have been sorted such + * that constraints with the same linear part appear together and + * that among constraints with the same linear part, those with + * smaller constant term appear first. + * + * We reuse the same data structure that is used by uset_simple_hull, + * but we do not need the hull table since we will not consider the + * same constraint more than once. We therefore allocate it with zero size. + * + * We run through the constraints and try to add them one by one, + * skipping identical constraints. If we have added a constraint and + * the next constraint is a more relaxed translate, then we skip this + * next constraint as well. + */ +static __isl_give isl_basic_set *uset_unshifted_simple_hull_from_constraints( + __isl_take isl_set *set, int n_ineq, isl_int **ineq) +{ + int i; + int last_added = 0; + struct sh_data *data = NULL; + isl_basic_set *hull = NULL; + unsigned dim; + + hull = isl_basic_set_alloc_space(isl_set_get_space(set), 0, 0, n_ineq); + if (!hull) + goto error; + + data = sh_data_alloc(set, 0); + if (!data) + goto error; + + dim = isl_set_dim(set, isl_dim_set); + for (i = 0; i < n_ineq; ++i) { + int hull_n_ineq = hull->n_ineq; + int parallel; + + parallel = i > 0 && isl_seq_eq(ineq[i - 1] + 1, ineq[i] + 1, + dim); + if (parallel && + (last_added || isl_int_eq(ineq[i - 1][0], ineq[i][0]))) + continue; + hull = add_bound_from_constraint(hull, data, set, ineq[i]); + if (!hull) + goto error; + last_added = hull->n_ineq > hull_n_ineq; + } + + sh_data_free(data); + isl_set_free(set); + return hull; +error: + sh_data_free(data); + isl_set_free(set); + isl_basic_set_free(hull); + return NULL; +} + +/* Collect pointers to all the inequalities in the elements of "list" + * in "ineq". For equalities, store both a pointer to the equality and + * a pointer to its opposite, which is first copied to "mat". + * "ineq" and "mat" are assumed to have been preallocated to the right size + * (the number of inequalities + 2 times the number of equalites and + * the number of equalities, respectively). + */ +static __isl_give isl_mat *collect_inequalities(__isl_take isl_mat *mat, + __isl_keep isl_basic_set_list *list, isl_int **ineq) +{ + int i, j, n, n_eq, n_ineq; + + if (!mat) + return NULL; + + n_eq = 0; + n_ineq = 0; + n = isl_basic_set_list_n_basic_set(list); + for (i = 0; i < n; ++i) { + isl_basic_set *bset; + bset = isl_basic_set_list_get_basic_set(list, i); + if (!bset) + return isl_mat_free(mat); + for (j = 0; j < bset->n_eq; ++j) { + ineq[n_ineq++] = mat->row[n_eq]; + ineq[n_ineq++] = bset->eq[j]; + isl_seq_neg(mat->row[n_eq++], bset->eq[j], mat->n_col); + } + for (j = 0; j < bset->n_ineq; ++j) + ineq[n_ineq++] = bset->ineq[j]; + isl_basic_set_free(bset); + } + + return mat; +} + +/* Comparison routine for use as an isl_sort callback. + * + * Constraints with the same linear part are sorted together and + * among constraints with the same linear part, those with smaller + * constant term are sorted first. + */ +static int cmp_ineq(const void *a, const void *b, void *arg) +{ + unsigned dim = *(unsigned *) arg; + isl_int * const *ineq1 = a; + isl_int * const *ineq2 = b; + int cmp; + + cmp = isl_seq_cmp((*ineq1) + 1, (*ineq2) + 1, dim); + if (cmp != 0) + return cmp; + return isl_int_cmp((*ineq1)[0], (*ineq2)[0]); +} + +/* Compute a superset of the convex hull of "set" that is described + * by only constraints in the elements of "list", where "set" has + * no parameters or integer divisions. + * + * We collect all the constraints in those elements and then + * sort the constraints such that constraints with the same linear part + * are sorted together and that those with smaller constant term are + * sorted first. + */ +static __isl_give isl_basic_set *uset_unshifted_simple_hull_from_basic_set_list( + __isl_take isl_set *set, __isl_take isl_basic_set_list *list) +{ + int i, n, n_eq, n_ineq; + unsigned dim; + isl_ctx *ctx; + isl_mat *mat = NULL; + isl_int **ineq = NULL; + isl_basic_set *hull; + + if (!set) + goto error; + ctx = isl_set_get_ctx(set); + + n_eq = 0; + n_ineq = 0; + n = isl_basic_set_list_n_basic_set(list); + for (i = 0; i < n; ++i) { + isl_basic_set *bset; + bset = isl_basic_set_list_get_basic_set(list, i); + if (!bset) + goto error; + n_eq += bset->n_eq; + n_ineq += 2 * bset->n_eq + bset->n_ineq; + isl_basic_set_free(bset); + } + + ineq = isl_alloc_array(ctx, isl_int *, n_ineq); + if (n_ineq > 0 && !ineq) + goto error; + + dim = isl_set_dim(set, isl_dim_set); + mat = isl_mat_alloc(ctx, n_eq, 1 + dim); + mat = collect_inequalities(mat, list, ineq); + if (!mat) + goto error; + + if (isl_sort(ineq, n_ineq, sizeof(ineq[0]), &cmp_ineq, &dim) < 0) + goto error; + + hull = uset_unshifted_simple_hull_from_constraints(set, n_ineq, ineq); + + isl_mat_free(mat); + free(ineq); + isl_basic_set_list_free(list); + return hull; +error: + isl_mat_free(mat); + free(ineq); + isl_set_free(set); + isl_basic_set_list_free(list); + return NULL; +} + +/* Compute a superset of the convex hull of "map" that is described + * by only constraints in the elements of "list". + * + * If the list is empty, then we can only describe the universe set. + * If the input map is empty, then all constraints are valid, so + * we return the intersection of the elements in "list". + * + * Otherwise, we align all divs and temporarily treat them + * as regular variables, computing the unshifted simple hull in + * uset_unshifted_simple_hull_from_basic_set_list. + */ +static __isl_give isl_basic_map *map_unshifted_simple_hull_from_basic_map_list( + __isl_take isl_map *map, __isl_take isl_basic_map_list *list) +{ + isl_basic_map *model; + isl_basic_map *hull; + isl_set *set; + isl_basic_set_list *bset_list; + + if (!map || !list) + goto error; + + if (isl_basic_map_list_n_basic_map(list) == 0) { + isl_space *space; + + space = isl_map_get_space(map); + isl_map_free(map); + isl_basic_map_list_free(list); + return isl_basic_map_universe(space); + } + if (isl_map_plain_is_empty(map)) { + isl_map_free(map); + return isl_basic_map_list_intersect(list); + } + + map = isl_map_align_divs_to_basic_map_list(map, list); + if (!map) + goto error; + list = isl_basic_map_list_align_divs_to_basic_map(list, map->p[0]); + + model = isl_basic_map_list_get_basic_map(list, 0); + + set = isl_map_underlying_set(map); + bset_list = isl_basic_map_list_underlying_set(list); + + hull = uset_unshifted_simple_hull_from_basic_set_list(set, bset_list); + hull = isl_basic_map_overlying_set(hull, model); + + return hull; +error: + isl_map_free(map); + isl_basic_map_list_free(list); + return NULL; +} + +/* Return a sequence of the basic maps that make up the maps in "list". + */ +static __isl_give isl_basic_set_list *collect_basic_maps( + __isl_take isl_map_list *list) +{ + int i, n; + isl_ctx *ctx; + isl_basic_map_list *bmap_list; + + if (!list) + return NULL; + n = isl_map_list_n_map(list); + ctx = isl_map_list_get_ctx(list); + bmap_list = isl_basic_map_list_alloc(ctx, 0); + + for (i = 0; i < n; ++i) { + isl_map *map; + isl_basic_map_list *list_i; + + map = isl_map_list_get_map(list, i); + map = isl_map_compute_divs(map); + list_i = isl_map_get_basic_map_list(map); + isl_map_free(map); + bmap_list = isl_basic_map_list_concat(bmap_list, list_i); + } + + isl_map_list_free(list); + return bmap_list; +} + +/* Compute a superset of the convex hull of "map" that is described + * by only constraints in the elements of "list". + * + * If "map" is the universe, then the convex hull (and therefore + * any superset of the convexhull) is the universe as well. + * + * Otherwise, we collect all the basic maps in the map list and + * continue with map_unshifted_simple_hull_from_basic_map_list. + */ +__isl_give isl_basic_map *isl_map_unshifted_simple_hull_from_map_list( + __isl_take isl_map *map, __isl_take isl_map_list *list) +{ + isl_basic_map_list *bmap_list; + int is_universe; + + is_universe = isl_map_plain_is_universe(map); + if (is_universe < 0) + map = isl_map_free(map); + if (is_universe < 0 || is_universe) { + isl_map_list_free(list); + return isl_map_unshifted_simple_hull(map); + } + + bmap_list = collect_basic_maps(list); + return map_unshifted_simple_hull_from_basic_map_list(map, bmap_list); +} + +/* Compute a superset of the convex hull of "set" that is described + * by only constraints in the elements of "list". + */ +__isl_give isl_basic_set *isl_set_unshifted_simple_hull_from_set_list( + __isl_take isl_set *set, __isl_take isl_set_list *list) +{ + return isl_map_unshifted_simple_hull_from_map_list(set, list); +} + +/* Given a set "set", return parametric bounds on the dimension "dim". + */ +static struct isl_basic_set *set_bounds(struct isl_set *set, int dim) +{ + unsigned set_dim = isl_set_dim(set, isl_dim_set); + set = isl_set_copy(set); + set = isl_set_eliminate_dims(set, dim + 1, set_dim - (dim + 1)); + set = isl_set_eliminate_dims(set, 0, dim); + return isl_set_convex_hull(set); +} + +/* Computes a "simple hull" and then check if each dimension in the + * resulting hull is bounded by a symbolic constant. If not, the + * hull is intersected with the corresponding bounds on the whole set. + */ +struct isl_basic_set *isl_set_bounded_simple_hull(struct isl_set *set) +{ + int i, j; + struct isl_basic_set *hull; + unsigned nparam, left; + int removed_divs = 0; + + hull = isl_set_simple_hull(isl_set_copy(set)); + if (!hull) + goto error; + + nparam = isl_basic_set_dim(hull, isl_dim_param); + for (i = 0; i < isl_basic_set_dim(hull, isl_dim_set); ++i) { + int lower = 0, upper = 0; + struct isl_basic_set *bounds; + + left = isl_basic_set_total_dim(hull) - nparam - i - 1; + for (j = 0; j < hull->n_eq; ++j) { + if (isl_int_is_zero(hull->eq[j][1 + nparam + i])) + continue; + if (isl_seq_first_non_zero(hull->eq[j]+1+nparam+i+1, + left) == -1) + break; + } + if (j < hull->n_eq) + continue; + + for (j = 0; j < hull->n_ineq; ++j) { + if (isl_int_is_zero(hull->ineq[j][1 + nparam + i])) + continue; + if (isl_seq_first_non_zero(hull->ineq[j]+1+nparam+i+1, + left) != -1 || + isl_seq_first_non_zero(hull->ineq[j]+1+nparam, + i) != -1) + continue; + if (isl_int_is_pos(hull->ineq[j][1 + nparam + i])) + lower = 1; + else + upper = 1; + if (lower && upper) + break; + } + + if (lower && upper) + continue; + + if (!removed_divs) { + set = isl_set_remove_divs(set); + if (!set) + goto error; + removed_divs = 1; + } + bounds = set_bounds(set, i); + hull = isl_basic_set_intersect(hull, bounds); + if (!hull) + goto error; + } + + isl_set_free(set); + return hull; +error: + isl_set_free(set); + return NULL; +} Index: lib/Analysis/isl/isl_ctx.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ctx.c @@ -0,0 +1,342 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include + +#define __isl_calloc(type,size) ((type *)calloc(1, size)) +#define __isl_calloc_type(type) __isl_calloc(type,sizeof(type)) + +/* Return the negation of "b", where the negation of isl_bool_error + * is isl_bool_error again. + */ +isl_bool isl_bool_not(isl_bool b) +{ + return b < 0 ? isl_bool_error : !b; +} + +/* Check that the result of an allocation ("p") is not NULL and + * complain if it is. + * The only exception is when allocation size ("size") is equal to zero. + */ +static void *check_non_null(isl_ctx *ctx, void *p, size_t size) +{ + if (p || size == 0) + return p; + isl_die(ctx, isl_error_alloc, "allocation failure", return NULL); +} + +/* Prepare for performing the next "operation" in the context. + * Return 0 if we are allowed to perform this operation and + * return -1 if we should abort the computation. + * + * In particular, we should stop if the user has explicitly aborted + * the computation or if the maximal number of operations has been exceeded. + */ +int isl_ctx_next_operation(isl_ctx *ctx) +{ + if (!ctx) + return -1; + if (ctx->abort) { + isl_ctx_set_error(ctx, isl_error_abort); + return -1; + } + if (ctx->max_operations && ctx->operations >= ctx->max_operations) + isl_die(ctx, isl_error_quota, + "maximal number of operations exceeded", return -1); + ctx->operations++; + return 0; +} + +/* Call malloc and complain if it fails. + * If ctx is NULL, then return NULL. + */ +void *isl_malloc_or_die(isl_ctx *ctx, size_t size) +{ + if (isl_ctx_next_operation(ctx) < 0) + return NULL; + return ctx ? check_non_null(ctx, malloc(size), size) : NULL; +} + +/* Call calloc and complain if it fails. + * If ctx is NULL, then return NULL. + */ +void *isl_calloc_or_die(isl_ctx *ctx, size_t nmemb, size_t size) +{ + if (isl_ctx_next_operation(ctx) < 0) + return NULL; + return ctx ? check_non_null(ctx, calloc(nmemb, size), nmemb) : NULL; +} + +/* Call realloc and complain if it fails. + * If ctx is NULL, then return NULL. + */ +void *isl_realloc_or_die(isl_ctx *ctx, void *ptr, size_t size) +{ + if (isl_ctx_next_operation(ctx) < 0) + return NULL; + return ctx ? check_non_null(ctx, realloc(ptr, size), size) : NULL; +} + +void isl_handle_error(isl_ctx *ctx, enum isl_error error, const char *msg, + const char *file, int line) +{ + if (!ctx) + return; + + isl_ctx_set_error(ctx, error); + + switch (ctx->opt->on_error) { + case ISL_ON_ERROR_WARN: + fprintf(stderr, "%s:%d: %s\n", file, line, msg); + return; + case ISL_ON_ERROR_CONTINUE: + return; + case ISL_ON_ERROR_ABORT: + fprintf(stderr, "%s:%d: %s\n", file, line, msg); + abort(); + return; + } +} + +static struct isl_options *find_nested_options(struct isl_args *args, + void *opt, struct isl_args *wanted) +{ + int i; + struct isl_options *options; + + if (args == wanted) + return opt; + + for (i = 0; args->args[i].type != isl_arg_end; ++i) { + struct isl_arg *arg = &args->args[i]; + void *child; + + if (arg->type != isl_arg_child) + continue; + + if (arg->offset == (size_t) -1) + child = opt; + else + child = *(void **)(((char *)opt) + arg->offset); + + options = find_nested_options(arg->u.child.child, + child, wanted); + if (options) + return options; + } + + return NULL; +} + +static struct isl_options *find_nested_isl_options(struct isl_args *args, + void *opt) +{ + return find_nested_options(args, opt, &isl_options_args); +} + +void *isl_ctx_peek_options(isl_ctx *ctx, struct isl_args *args) +{ + if (!ctx) + return NULL; + if (args == &isl_options_args) + return ctx->opt; + return find_nested_options(ctx->user_args, ctx->user_opt, args); +} + +isl_ctx *isl_ctx_alloc_with_options(struct isl_args *args, void *user_opt) +{ + struct isl_ctx *ctx = NULL; + struct isl_options *opt = NULL; + int opt_allocated = 0; + + if (!user_opt) + return NULL; + + opt = find_nested_isl_options(args, user_opt); + if (!opt) { + opt = isl_options_new_with_defaults(); + if (!opt) + goto error; + opt_allocated = 1; + } + + ctx = __isl_calloc_type(struct isl_ctx); + if (!ctx) + goto error; + + if (isl_hash_table_init(ctx, &ctx->id_table, 0)) + goto error; + + ctx->stats = isl_calloc_type(ctx, struct isl_stats); + if (!ctx->stats) + goto error; + + ctx->user_args = args; + ctx->user_opt = user_opt; + ctx->opt_allocated = opt_allocated; + ctx->opt = opt; + ctx->ref = 0; + + isl_int_init(ctx->zero); + isl_int_set_si(ctx->zero, 0); + + isl_int_init(ctx->one); + isl_int_set_si(ctx->one, 1); + + isl_int_init(ctx->two); + isl_int_set_si(ctx->two, 2); + + isl_int_init(ctx->negone); + isl_int_set_si(ctx->negone, -1); + + isl_int_init(ctx->normalize_gcd); + + ctx->n_cached = 0; + ctx->n_miss = 0; + + ctx->error = isl_error_none; + + ctx->operations = 0; + isl_ctx_set_max_operations(ctx, ctx->opt->max_operations); + + return ctx; +error: + isl_args_free(args, user_opt); + if (opt_allocated) + isl_options_free(opt); + free(ctx); + return NULL; +} + +struct isl_ctx *isl_ctx_alloc() +{ + struct isl_options *opt; + + opt = isl_options_new_with_defaults(); + + return isl_ctx_alloc_with_options(&isl_options_args, opt); +} + +void isl_ctx_ref(struct isl_ctx *ctx) +{ + ctx->ref++; +} + +void isl_ctx_deref(struct isl_ctx *ctx) +{ + isl_assert(ctx, ctx->ref > 0, return); + ctx->ref--; +} + +/* Print statistics on usage. + */ +static void print_stats(isl_ctx *ctx) +{ + fprintf(stderr, "operations: %lu\n", ctx->operations); +} + +void isl_ctx_free(struct isl_ctx *ctx) +{ + if (!ctx) + return; + if (ctx->ref != 0) + isl_die(ctx, isl_error_invalid, + "isl_ctx freed, but some objects still reference it", + return); + + if (ctx->opt->print_stats) + print_stats(ctx); + + isl_hash_table_clear(&ctx->id_table); + isl_blk_clear_cache(ctx); + isl_int_clear(ctx->zero); + isl_int_clear(ctx->one); + isl_int_clear(ctx->two); + isl_int_clear(ctx->negone); + isl_int_clear(ctx->normalize_gcd); + isl_args_free(ctx->user_args, ctx->user_opt); + if (ctx->opt_allocated) + isl_options_free(ctx->opt); + free(ctx->stats); + free(ctx); +} + +struct isl_options *isl_ctx_options(isl_ctx *ctx) +{ + if (!ctx) + return NULL; + return ctx->opt; +} + +enum isl_error isl_ctx_last_error(isl_ctx *ctx) +{ + return ctx->error; +} + +void isl_ctx_reset_error(isl_ctx *ctx) +{ + ctx->error = isl_error_none; +} + +void isl_ctx_set_error(isl_ctx *ctx, enum isl_error error) +{ + if (ctx) + ctx->error = error; +} + +void isl_ctx_abort(isl_ctx *ctx) +{ + if (ctx) + ctx->abort = 1; +} + +void isl_ctx_resume(isl_ctx *ctx) +{ + if (ctx) + ctx->abort = 0; +} + +int isl_ctx_aborted(isl_ctx *ctx) +{ + return ctx ? ctx->abort : -1; +} + +int isl_ctx_parse_options(isl_ctx *ctx, int argc, char **argv, unsigned flags) +{ + if (!ctx) + return -1; + return isl_args_parse(ctx->user_args, argc, argv, ctx->user_opt, flags); +} + +/* Set the maximal number of iterations of "ctx" to "max_operations". + */ +void isl_ctx_set_max_operations(isl_ctx *ctx, unsigned long max_operations) +{ + if (!ctx) + return; + ctx->max_operations = max_operations; +} + +/* Return the maximal number of iterations of "ctx". + */ +unsigned long isl_ctx_get_max_operations(isl_ctx *ctx) +{ + return ctx ? ctx->max_operations : 0; +} + +/* Reset the number of operations performed by "ctx". + */ +void isl_ctx_reset_operations(isl_ctx *ctx) +{ + if (!ctx) + return; + ctx->operations = 0; +} Index: lib/Analysis/isl/isl_ctx_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ctx_private.h @@ -0,0 +1,34 @@ +#include +#include + +struct isl_ctx { + int ref; + + struct isl_stats *stats; + + int opt_allocated; + struct isl_options *opt; + void *user_opt; + struct isl_args *user_args; + + isl_int zero; + isl_int one; + isl_int two; + isl_int negone; + + isl_int normalize_gcd; + + int n_cached; + int n_miss; + struct isl_blk cache[ISL_BLK_CACHE_SIZE]; + struct isl_hash_table id_table; + + enum isl_error error; + + int abort; + + unsigned long operations; + unsigned long max_operations; +}; + +int isl_ctx_next_operation(isl_ctx *ctx); Index: lib/Analysis/isl/isl_deprecated.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_deprecated.c @@ -0,0 +1,25 @@ +#include +#include + +/* This function was never documented and has been replaced by + * isl_basic_set_add_dims. + */ +__isl_give isl_basic_set *isl_basic_set_add(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned n) +{ + return isl_basic_set_add_dims(bset, type, n); +} + +/* This function was replaced by isl_constraint_alloc_equality. + */ +__isl_give isl_constraint *isl_equality_alloc(__isl_take isl_local_space *ls) +{ + return isl_constraint_alloc_equality(ls); +} + +/* This function was replaced by isl_constraint_alloc_inequality. + */ +__isl_give isl_constraint *isl_inequality_alloc(__isl_take isl_local_space *ls) +{ + return isl_constraint_alloc_inequality(ls); +} Index: lib/Analysis/isl/isl_dim_map.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_dim_map.h @@ -0,0 +1,36 @@ +#ifndef ISL_DIM_MAP_H +#define ISL_DIM_MAP_H + +#include +#include +#include + +struct isl_dim_map; +typedef struct isl_dim_map isl_dim_map; + +__isl_give isl_dim_map *isl_dim_map_alloc(isl_ctx *ctx, unsigned len); +void isl_dim_map_range(__isl_keep isl_dim_map *dim_map, + unsigned dst_pos, unsigned dst_stride, + unsigned src_pos, unsigned src_stride, + unsigned n, int sign); +void isl_dim_map_dim_range(__isl_keep isl_dim_map *dim_map, + isl_space *dim, enum isl_dim_type type, + unsigned first, unsigned n, unsigned dst_pos); +void isl_dim_map_dim(__isl_keep isl_dim_map *dim_map, __isl_keep isl_space *dim, + enum isl_dim_type type, unsigned dst_pos); +void isl_dim_map_div(__isl_keep isl_dim_map *dim_map, + __isl_keep isl_basic_map *bmap, unsigned dst_pos); +__isl_give isl_basic_set *isl_basic_set_add_constraints_dim_map( + __isl_take isl_basic_set *dst, __isl_take isl_basic_set *src, + __isl_take isl_dim_map *dim_map); +__isl_give isl_basic_map *isl_basic_map_add_constraints_dim_map( + __isl_take isl_basic_map *dst, __isl_take isl_basic_map *src, + __isl_take isl_dim_map *dim_map); + +__isl_give isl_dim_map *isl_dim_map_extend(__isl_keep isl_dim_map *dim_map, + __isl_keep isl_basic_map *bmap); + +__isl_give isl_dim_map *isl_dim_map_from_reordering( + __isl_keep isl_reordering *exp); + +#endif Index: lib/Analysis/isl/isl_dim_map.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_dim_map.c @@ -0,0 +1,233 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010-2011 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + */ + +#include +#include +#include +#include + +struct isl_dim_map_entry { + int pos; + int sgn; +}; + +/* Maps dst positions to src positions */ +struct isl_dim_map { + unsigned len; + struct isl_dim_map_entry m[1]; +}; + +__isl_give isl_dim_map *isl_dim_map_alloc(isl_ctx *ctx, unsigned len) +{ + int i; + struct isl_dim_map *dim_map; + dim_map = isl_alloc(ctx, struct isl_dim_map, + sizeof(struct isl_dim_map) + len * sizeof(struct isl_dim_map_entry)); + if (!dim_map) + return NULL; + dim_map->len = 1 + len; + dim_map->m[0].pos = 0; + dim_map->m[0].sgn = 1; + for (i = 0; i < len; ++i) + dim_map->m[1 + i].sgn = 0; + return dim_map; +} + +void isl_dim_map_range(__isl_keep isl_dim_map *dim_map, + unsigned dst_pos, unsigned dst_stride, + unsigned src_pos, unsigned src_stride, + unsigned n, int sign) +{ + int i; + + if (!dim_map) + return; + + for (i = 0; i < n; ++i) { + unsigned d = 1 + dst_pos + dst_stride * i; + unsigned s = 1 + src_pos + src_stride * i; + dim_map->m[d].pos = s; + dim_map->m[d].sgn = sign; + } +} + +void isl_dim_map_dim_range(__isl_keep isl_dim_map *dim_map, + __isl_keep isl_space *dim, enum isl_dim_type type, + unsigned first, unsigned n, unsigned dst_pos) +{ + int i; + unsigned src_pos; + + if (!dim_map || !dim) + return; + + src_pos = 1 + isl_space_offset(dim, type); + for (i = 0; i < n; ++i) { + dim_map->m[1 + dst_pos + i].pos = src_pos + first + i; + dim_map->m[1 + dst_pos + i].sgn = 1; + } +} + +void isl_dim_map_dim(__isl_keep isl_dim_map *dim_map, __isl_keep isl_space *dim, + enum isl_dim_type type, unsigned dst_pos) +{ + isl_dim_map_dim_range(dim_map, dim, type, + 0, isl_space_dim(dim, type), dst_pos); +} + +void isl_dim_map_div(__isl_keep isl_dim_map *dim_map, + __isl_keep isl_basic_map *bmap, unsigned dst_pos) +{ + int i; + unsigned src_pos; + + if (!dim_map || !bmap) + return; + + src_pos = 1 + isl_space_dim(bmap->dim, isl_dim_all); + for (i = 0; i < bmap->n_div; ++i) { + dim_map->m[1 + dst_pos + i].pos = src_pos + i; + dim_map->m[1 + dst_pos + i].sgn = 1; + } +} + +void isl_dim_map_dump(struct isl_dim_map *dim_map) +{ + int i; + + for (i = 0; i < dim_map->len; ++i) + fprintf(stderr, "%d -> %d * %d; ", i, + dim_map->m[i].sgn, dim_map->m[i].pos); + fprintf(stderr, "\n"); +} + +static void copy_constraint_dim_map(isl_int *dst, isl_int *src, + struct isl_dim_map *dim_map) +{ + int i; + + for (i = 0; i < dim_map->len; ++i) { + if (dim_map->m[i].sgn == 0) + isl_int_set_si(dst[i], 0); + else if (dim_map->m[i].sgn > 0) + isl_int_set(dst[i], src[dim_map->m[i].pos]); + else + isl_int_neg(dst[i], src[dim_map->m[i].pos]); + } +} + +static void copy_div_dim_map(isl_int *dst, isl_int *src, + struct isl_dim_map *dim_map) +{ + isl_int_set(dst[0], src[0]); + copy_constraint_dim_map(dst+1, src+1, dim_map); +} + +__isl_give isl_basic_map *isl_basic_map_add_constraints_dim_map( + __isl_take isl_basic_map *dst, __isl_take isl_basic_map *src, + __isl_take isl_dim_map *dim_map) +{ + int i; + + if (!src || !dst || !dim_map) + goto error; + + for (i = 0; i < src->n_eq; ++i) { + int i1 = isl_basic_map_alloc_equality(dst); + if (i1 < 0) + goto error; + copy_constraint_dim_map(dst->eq[i1], src->eq[i], dim_map); + } + + for (i = 0; i < src->n_ineq; ++i) { + int i1 = isl_basic_map_alloc_inequality(dst); + if (i1 < 0) + goto error; + copy_constraint_dim_map(dst->ineq[i1], src->ineq[i], dim_map); + } + + for (i = 0; i < src->n_div; ++i) { + int i1 = isl_basic_map_alloc_div(dst); + if (i1 < 0) + goto error; + copy_div_dim_map(dst->div[i1], src->div[i], dim_map); + } + + free(dim_map); + isl_basic_map_free(src); + + return dst; +error: + free(dim_map); + isl_basic_map_free(src); + isl_basic_map_free(dst); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_add_constraints_dim_map( + __isl_take isl_basic_set *dst, __isl_take isl_basic_set *src, + __isl_take isl_dim_map *dim_map) +{ + return isl_basic_map_add_constraints_dim_map(dst, src, dim_map); +} + +/* Extend the given dim_map with mappings for the divs in bmap. + */ +__isl_give isl_dim_map *isl_dim_map_extend(__isl_keep isl_dim_map *dim_map, + __isl_keep isl_basic_map *bmap) +{ + int i; + struct isl_dim_map *res; + int offset; + + offset = isl_basic_map_offset(bmap, isl_dim_div); + + res = isl_dim_map_alloc(bmap->ctx, dim_map->len - 1 + bmap->n_div); + if (!res) + return NULL; + + for (i = 0; i < dim_map->len; ++i) + res->m[i] = dim_map->m[i]; + for (i = 0; i < bmap->n_div; ++i) { + res->m[dim_map->len + i].pos = offset + i; + res->m[dim_map->len + i].sgn = 1; + } + + return res; +} + +/* Extract a dim_map from a reordering. + * We essentially need to reverse the mapping, and add an offset + * of 1 for the constant term. + */ +__isl_give isl_dim_map *isl_dim_map_from_reordering( + __isl_keep isl_reordering *exp) +{ + int i; + isl_ctx *ctx; + struct isl_dim_map *dim_map; + + if (!exp) + return NULL; + + ctx = isl_space_get_ctx(exp->dim); + dim_map = isl_dim_map_alloc(ctx, isl_space_dim(exp->dim, isl_dim_all)); + if (!dim_map) + return NULL; + + for (i = 0; i < exp->len; ++i) { + dim_map->m[1 + exp->pos[i]].pos = 1 + i; + dim_map->m[1 + exp->pos[i]].sgn = 1; + } + + return dim_map; +} Index: lib/Analysis/isl/isl_equalities.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_equalities.h @@ -0,0 +1,35 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_EQUALITIES_H +#define ISL_EQUALITIES_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_mat *isl_mat_final_variable_compression(__isl_take isl_mat *B, + int first, __isl_give isl_mat **T2); +__isl_give isl_mat *isl_mat_variable_compression(__isl_take isl_mat *B, + __isl_give isl_mat **T2); +struct isl_mat *isl_mat_parameter_compression( + struct isl_mat *B, struct isl_vec *d); +__isl_give isl_mat *isl_mat_parameter_compression_ext(__isl_take isl_mat *B, + __isl_take isl_mat *A); +struct isl_basic_set *isl_basic_set_remove_equalities( + struct isl_basic_set *bset, struct isl_mat **T, struct isl_mat **T2); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/isl_equalities.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_equalities.c @@ -0,0 +1,891 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + */ + +#include +#include +#include +#include "isl_map_private.h" +#include "isl_equalities.h" +#include + +/* Given a set of modulo constraints + * + * c + A y = 0 mod d + * + * this function computes a particular solution y_0 + * + * The input is given as a matrix B = [ c A ] and a vector d. + * + * The output is matrix containing the solution y_0 or + * a zero-column matrix if the constraints admit no integer solution. + * + * The given set of constrains is equivalent to + * + * c + A y = -D x + * + * with D = diag d and x a fresh set of variables. + * Reducing both c and A modulo d does not change the + * value of y in the solution and may lead to smaller coefficients. + * Let M = [ D A ] and [ H 0 ] = M U, the Hermite normal form of M. + * Then + * [ x ] + * M [ y ] = - c + * and so + * [ x ] + * [ H 0 ] U^{-1} [ y ] = - c + * Let + * [ A ] [ x ] + * [ B ] = U^{-1} [ y ] + * then + * H A + 0 B = -c + * + * so B may be chosen arbitrarily, e.g., B = 0, and then + * + * [ x ] = [ -c ] + * U^{-1} [ y ] = [ 0 ] + * or + * [ x ] [ -c ] + * [ y ] = U [ 0 ] + * specifically, + * + * y = U_{2,1} (-c) + * + * If any of the coordinates of this y are non-integer + * then the constraints admit no integer solution and + * a zero-column matrix is returned. + */ +static struct isl_mat *particular_solution(struct isl_mat *B, struct isl_vec *d) +{ + int i, j; + struct isl_mat *M = NULL; + struct isl_mat *C = NULL; + struct isl_mat *U = NULL; + struct isl_mat *H = NULL; + struct isl_mat *cst = NULL; + struct isl_mat *T = NULL; + + M = isl_mat_alloc(B->ctx, B->n_row, B->n_row + B->n_col - 1); + C = isl_mat_alloc(B->ctx, 1 + B->n_row, 1); + if (!M || !C) + goto error; + isl_int_set_si(C->row[0][0], 1); + for (i = 0; i < B->n_row; ++i) { + isl_seq_clr(M->row[i], B->n_row); + isl_int_set(M->row[i][i], d->block.data[i]); + isl_int_neg(C->row[1 + i][0], B->row[i][0]); + isl_int_fdiv_r(C->row[1+i][0], C->row[1+i][0], M->row[i][i]); + for (j = 0; j < B->n_col - 1; ++j) + isl_int_fdiv_r(M->row[i][B->n_row + j], + B->row[i][1 + j], M->row[i][i]); + } + M = isl_mat_left_hermite(M, 0, &U, NULL); + if (!M || !U) + goto error; + H = isl_mat_sub_alloc(M, 0, B->n_row, 0, B->n_row); + H = isl_mat_lin_to_aff(H); + C = isl_mat_inverse_product(H, C); + if (!C) + goto error; + for (i = 0; i < B->n_row; ++i) { + if (!isl_int_is_divisible_by(C->row[1+i][0], C->row[0][0])) + break; + isl_int_divexact(C->row[1+i][0], C->row[1+i][0], C->row[0][0]); + } + if (i < B->n_row) + cst = isl_mat_alloc(B->ctx, B->n_row, 0); + else + cst = isl_mat_sub_alloc(C, 1, B->n_row, 0, 1); + T = isl_mat_sub_alloc(U, B->n_row, B->n_col - 1, 0, B->n_row); + cst = isl_mat_product(T, cst); + isl_mat_free(M); + isl_mat_free(C); + isl_mat_free(U); + return cst; +error: + isl_mat_free(M); + isl_mat_free(C); + isl_mat_free(U); + return NULL; +} + +/* Compute and return the matrix + * + * U_1^{-1} diag(d_1, 1, ..., 1) + * + * with U_1 the unimodular completion of the first (and only) row of B. + * The columns of this matrix generate the lattice that satisfies + * the single (linear) modulo constraint. + */ +static struct isl_mat *parameter_compression_1( + struct isl_mat *B, struct isl_vec *d) +{ + struct isl_mat *U; + + U = isl_mat_alloc(B->ctx, B->n_col - 1, B->n_col - 1); + if (!U) + return NULL; + isl_seq_cpy(U->row[0], B->row[0] + 1, B->n_col - 1); + U = isl_mat_unimodular_complete(U, 1); + U = isl_mat_right_inverse(U); + if (!U) + return NULL; + isl_mat_col_mul(U, 0, d->block.data[0], 0); + U = isl_mat_lin_to_aff(U); + return U; +} + +/* Compute a common lattice of solutions to the linear modulo + * constraints specified by B and d. + * See also the documentation of isl_mat_parameter_compression. + * We put the matrix + * + * A = [ L_1^{-T} L_2^{-T} ... L_k^{-T} ] + * + * on a common denominator. This denominator D is the lcm of modulos d. + * Since L_i = U_i^{-1} diag(d_i, 1, ... 1), we have + * L_i^{-T} = U_i^T diag(d_i, 1, ... 1)^{-T} = U_i^T diag(1/d_i, 1, ..., 1). + * Putting this on the common denominator, we have + * D * L_i^{-T} = U_i^T diag(D/d_i, D, ..., D). + */ +static struct isl_mat *parameter_compression_multi( + struct isl_mat *B, struct isl_vec *d) +{ + int i, j, k; + isl_int D; + struct isl_mat *A = NULL, *U = NULL; + struct isl_mat *T; + unsigned size; + + isl_int_init(D); + + isl_vec_lcm(d, &D); + + size = B->n_col - 1; + A = isl_mat_alloc(B->ctx, size, B->n_row * size); + U = isl_mat_alloc(B->ctx, size, size); + if (!U || !A) + goto error; + for (i = 0; i < B->n_row; ++i) { + isl_seq_cpy(U->row[0], B->row[i] + 1, size); + U = isl_mat_unimodular_complete(U, 1); + if (!U) + goto error; + isl_int_divexact(D, D, d->block.data[i]); + for (k = 0; k < U->n_col; ++k) + isl_int_mul(A->row[k][i*size+0], D, U->row[0][k]); + isl_int_mul(D, D, d->block.data[i]); + for (j = 1; j < U->n_row; ++j) + for (k = 0; k < U->n_col; ++k) + isl_int_mul(A->row[k][i*size+j], + D, U->row[j][k]); + } + A = isl_mat_left_hermite(A, 0, NULL, NULL); + T = isl_mat_sub_alloc(A, 0, A->n_row, 0, A->n_row); + T = isl_mat_lin_to_aff(T); + if (!T) + goto error; + isl_int_set(T->row[0][0], D); + T = isl_mat_right_inverse(T); + if (!T) + goto error; + isl_assert(T->ctx, isl_int_is_one(T->row[0][0]), goto error); + T = isl_mat_transpose(T); + isl_mat_free(A); + isl_mat_free(U); + + isl_int_clear(D); + return T; +error: + isl_mat_free(A); + isl_mat_free(U); + isl_int_clear(D); + return NULL; +} + +/* Given a set of modulo constraints + * + * c + A y = 0 mod d + * + * this function returns an affine transformation T, + * + * y = T y' + * + * that bijectively maps the integer vectors y' to integer + * vectors y that satisfy the modulo constraints. + * + * This function is inspired by Section 2.5.3 + * of B. Meister, "Stating and Manipulating Periodicity in the Polytope + * Model. Applications to Program Analysis and Optimization". + * However, the implementation only follows the algorithm of that + * section for computing a particular solution and not for computing + * a general homogeneous solution. The latter is incomplete and + * may remove some valid solutions. + * Instead, we use an adaptation of the algorithm in Section 7 of + * B. Meister, S. Verdoolaege, "Polynomial Approximations in the Polytope + * Model: Bringing the Power of Quasi-Polynomials to the Masses". + * + * The input is given as a matrix B = [ c A ] and a vector d. + * Each element of the vector d corresponds to a row in B. + * The output is a lower triangular matrix. + * If no integer vector y satisfies the given constraints then + * a matrix with zero columns is returned. + * + * We first compute a particular solution y_0 to the given set of + * modulo constraints in particular_solution. If no such solution + * exists, then we return a zero-columned transformation matrix. + * Otherwise, we compute the generic solution to + * + * A y = 0 mod d + * + * That is we want to compute G such that + * + * y = G y'' + * + * with y'' integer, describes the set of solutions. + * + * We first remove the common factors of each row. + * In particular if gcd(A_i,d_i) != 1, then we divide the whole + * row i (including d_i) by this common factor. If afterwards gcd(A_i) != 1, + * then we divide this row of A by the common factor, unless gcd(A_i) = 0. + * In the later case, we simply drop the row (in both A and d). + * + * If there are no rows left in A, then G is the identity matrix. Otherwise, + * for each row i, we now determine the lattice of integer vectors + * that satisfies this row. Let U_i be the unimodular extension of the + * row A_i. This unimodular extension exists because gcd(A_i) = 1. + * The first component of + * + * y' = U_i y + * + * needs to be a multiple of d_i. Let y' = diag(d_i, 1, ..., 1) y''. + * Then, + * + * y = U_i^{-1} diag(d_i, 1, ..., 1) y'' + * + * for arbitrary integer vectors y''. That is, y belongs to the lattice + * generated by the columns of L_i = U_i^{-1} diag(d_i, 1, ..., 1). + * If there is only one row, then G = L_1. + * + * If there is more than one row left, we need to compute the intersection + * of the lattices. That is, we need to compute an L such that + * + * L = L_i L_i' for all i + * + * with L_i' some integer matrices. Let A be constructed as follows + * + * A = [ L_1^{-T} L_2^{-T} ... L_k^{-T} ] + * + * and computed the Hermite Normal Form of A = [ H 0 ] U + * Then, + * + * L_i^{-T} = H U_{1,i} + * + * or + * + * H^{-T} = L_i U_{1,i}^T + * + * In other words G = L = H^{-T}. + * To ensure that G is lower triangular, we compute and use its Hermite + * normal form. + * + * The affine transformation matrix returned is then + * + * [ 1 0 ] + * [ y_0 G ] + * + * as any y = y_0 + G y' with y' integer is a solution to the original + * modulo constraints. + */ +struct isl_mat *isl_mat_parameter_compression( + struct isl_mat *B, struct isl_vec *d) +{ + int i; + struct isl_mat *cst = NULL; + struct isl_mat *T = NULL; + isl_int D; + + if (!B || !d) + goto error; + isl_assert(B->ctx, B->n_row == d->size, goto error); + cst = particular_solution(B, d); + if (!cst) + goto error; + if (cst->n_col == 0) { + T = isl_mat_alloc(B->ctx, B->n_col, 0); + isl_mat_free(cst); + isl_mat_free(B); + isl_vec_free(d); + return T; + } + isl_int_init(D); + /* Replace a*g*row = 0 mod g*m by row = 0 mod m */ + for (i = 0; i < B->n_row; ++i) { + isl_seq_gcd(B->row[i] + 1, B->n_col - 1, &D); + if (isl_int_is_one(D)) + continue; + if (isl_int_is_zero(D)) { + B = isl_mat_drop_rows(B, i, 1); + d = isl_vec_cow(d); + if (!B || !d) + goto error2; + isl_seq_cpy(d->block.data+i, d->block.data+i+1, + d->size - (i+1)); + d->size--; + i--; + continue; + } + B = isl_mat_cow(B); + if (!B) + goto error2; + isl_seq_scale_down(B->row[i] + 1, B->row[i] + 1, D, B->n_col-1); + isl_int_gcd(D, D, d->block.data[i]); + d = isl_vec_cow(d); + if (!d) + goto error2; + isl_int_divexact(d->block.data[i], d->block.data[i], D); + } + isl_int_clear(D); + if (B->n_row == 0) + T = isl_mat_identity(B->ctx, B->n_col); + else if (B->n_row == 1) + T = parameter_compression_1(B, d); + else + T = parameter_compression_multi(B, d); + T = isl_mat_left_hermite(T, 0, NULL, NULL); + if (!T) + goto error; + isl_mat_sub_copy(T->ctx, T->row + 1, cst->row, cst->n_row, 0, 0, 1); + isl_mat_free(cst); + isl_mat_free(B); + isl_vec_free(d); + return T; +error2: + isl_int_clear(D); +error: + isl_mat_free(cst); + isl_mat_free(B); + isl_vec_free(d); + return NULL; +} + +/* Given a set of equalities + * + * B(y) + A x = 0 (*) + * + * compute and return an affine transformation T, + * + * y = T y' + * + * that bijectively maps the integer vectors y' to integer + * vectors y that satisfy the modulo constraints for some value of x. + * + * Let [H 0] be the Hermite Normal Form of A, i.e., + * + * A = [H 0] Q + * + * Then y is a solution of (*) iff + * + * H^-1 B(y) (= - [I 0] Q x) + * + * is an integer vector. Let d be the common denominator of H^-1. + * We impose + * + * d H^-1 B(y) = 0 mod d + * + * and compute the solution using isl_mat_parameter_compression. + */ +__isl_give isl_mat *isl_mat_parameter_compression_ext(__isl_take isl_mat *B, + __isl_take isl_mat *A) +{ + isl_ctx *ctx; + isl_vec *d; + int n_row, n_col; + + if (!A) + return isl_mat_free(B); + + ctx = isl_mat_get_ctx(A); + n_row = A->n_row; + n_col = A->n_col; + A = isl_mat_left_hermite(A, 0, NULL, NULL); + A = isl_mat_drop_cols(A, n_row, n_col - n_row); + A = isl_mat_lin_to_aff(A); + A = isl_mat_right_inverse(A); + d = isl_vec_alloc(ctx, n_row); + if (A) + d = isl_vec_set(d, A->row[0][0]); + A = isl_mat_drop_rows(A, 0, 1); + A = isl_mat_drop_cols(A, 0, 1); + B = isl_mat_product(A, B); + + return isl_mat_parameter_compression(B, d); +} + +/* Return a compression matrix that indicates that there are no solutions + * to the original constraints. In particular, return a zero-column + * matrix with 1 + dim rows. If "T2" is not NULL, then assign *T2 + * the inverse of this matrix. *T2 may already have been assigned + * matrix, so free it first. + * "free1", "free2" and "free3" are temporary matrices that are + * not useful when an empty compression is returned. They are + * simply freed. + */ +static __isl_give isl_mat *empty_compression(isl_ctx *ctx, unsigned dim, + __isl_give isl_mat **T2, __isl_take isl_mat *free1, + __isl_take isl_mat *free2, __isl_take isl_mat *free3) +{ + isl_mat_free(free1); + isl_mat_free(free2); + isl_mat_free(free3); + if (T2) { + isl_mat_free(*T2); + *T2 = isl_mat_alloc(ctx, 0, 1 + dim); + } + return isl_mat_alloc(ctx, 1 + dim, 0); +} + +/* Given a matrix that maps a (possibly) parametric domain to + * a parametric domain, add in rows that map the "nparam" parameters onto + * themselves. + */ +static __isl_give isl_mat *insert_parameter_rows(__isl_take isl_mat *mat, + unsigned nparam) +{ + int i; + + if (nparam == 0) + return mat; + if (!mat) + return NULL; + + mat = isl_mat_insert_rows(mat, 1, nparam); + if (!mat) + return NULL; + + for (i = 0; i < nparam; ++i) { + isl_seq_clr(mat->row[1 + i], mat->n_col); + isl_int_set(mat->row[1 + i][1 + i], mat->row[0][0]); + } + + return mat; +} + +/* Given a set of equalities + * + * -C(y) + M x = 0 + * + * this function computes a unimodular transformation from a lower-dimensional + * space to the original space that bijectively maps the integer points x' + * in the lower-dimensional space to the integer points x in the original + * space that satisfy the equalities. + * + * The input is given as a matrix B = [ -C M ] and the output is a + * matrix that maps [1 x'] to [1 x]. + * The number of equality constraints in B is assumed to be smaller than + * or equal to the number of variables x. + * "first" is the position of the first x variable. + * The preceding variables are considered to by y-variables. + * If T2 is not NULL, then *T2 is set to a matrix mapping [1 x] to [1 x']. + * + * First compute the (left) Hermite normal form of M, + * + * M [U1 U2] = M U = H = [H1 0] + * or + * M = H Q = [H1 0] [Q1] + * [Q2] + * + * with U, Q unimodular, Q = U^{-1} (and H lower triangular). + * Define the transformed variables as + * + * x = [U1 U2] [ x1' ] = [U1 U2] [Q1] x + * [ x2' ] [Q2] + * + * The equalities then become + * + * -C(y) + H1 x1' = 0 or x1' = H1^{-1} C(y) = C'(y) + * + * If the denominator of the constant term does not divide the + * the common denominator of the coefficients of y, then every + * integer point is mapped to a non-integer point and then the original set + * has no integer solutions (since the x' are a unimodular transformation + * of the x). In this case, a zero-column matrix is returned. + * Otherwise, the transformation is given by + * + * x = U1 H1^{-1} C(y) + U2 x2' + * + * The inverse transformation is simply + * + * x2' = Q2 x + */ +__isl_give isl_mat *isl_mat_final_variable_compression(__isl_take isl_mat *B, + int first, __isl_give isl_mat **T2) +{ + int i, n; + isl_ctx *ctx; + isl_mat *H = NULL, *C, *H1, *U = NULL, *U1, *U2; + unsigned dim; + + if (T2) + *T2 = NULL; + if (!B) + goto error; + + ctx = isl_mat_get_ctx(B); + dim = B->n_col - 1; + n = dim - first; + if (n < B->n_row) + isl_die(ctx, isl_error_invalid, "too many equality constraints", + goto error); + H = isl_mat_sub_alloc(B, 0, B->n_row, 1 + first, n); + H = isl_mat_left_hermite(H, 0, &U, T2); + if (!H || !U || (T2 && !*T2)) + goto error; + if (T2) { + *T2 = isl_mat_drop_rows(*T2, 0, B->n_row); + *T2 = isl_mat_diagonal(isl_mat_identity(ctx, 1 + first), *T2); + if (!*T2) + goto error; + } + C = isl_mat_alloc(ctx, 1 + B->n_row, 1 + first); + if (!C) + goto error; + isl_int_set_si(C->row[0][0], 1); + isl_seq_clr(C->row[0] + 1, first); + isl_mat_sub_neg(ctx, C->row + 1, B->row, B->n_row, 0, 0, 1 + first); + H1 = isl_mat_sub_alloc(H, 0, H->n_row, 0, H->n_row); + H1 = isl_mat_lin_to_aff(H1); + C = isl_mat_inverse_product(H1, C); + if (!C) + goto error; + isl_mat_free(H); + if (!isl_int_is_one(C->row[0][0])) { + isl_int g; + + isl_int_init(g); + for (i = 0; i < B->n_row; ++i) { + isl_seq_gcd(C->row[1 + i] + 1, first, &g); + isl_int_gcd(g, g, C->row[0][0]); + if (!isl_int_is_divisible_by(C->row[1 + i][0], g)) + break; + } + isl_int_clear(g); + + if (i < B->n_row) + return empty_compression(ctx, dim, T2, B, C, U); + C = isl_mat_normalize(C); + } + U1 = isl_mat_sub_alloc(U, 0, U->n_row, 0, B->n_row); + U1 = isl_mat_lin_to_aff(U1); + U2 = isl_mat_sub_alloc(U, 0, U->n_row, B->n_row, U->n_row - B->n_row); + U2 = isl_mat_lin_to_aff(U2); + isl_mat_free(U); + C = isl_mat_product(U1, C); + C = isl_mat_aff_direct_sum(C, U2); + C = insert_parameter_rows(C, first); + + isl_mat_free(B); + + return C; +error: + isl_mat_free(B); + isl_mat_free(H); + isl_mat_free(U); + if (T2) { + isl_mat_free(*T2); + *T2 = NULL; + } + return NULL; +} + +/* Given a set of equalities + * + * M x - c = 0 + * + * this function computes a unimodular transformation from a lower-dimensional + * space to the original space that bijectively maps the integer points x' + * in the lower-dimensional space to the integer points x in the original + * space that satisfy the equalities. + * + * The input is given as a matrix B = [ -c M ] and the output is a + * matrix that maps [1 x'] to [1 x]. + * The number of equality constraints in B is assumed to be smaller than + * or equal to the number of variables x. + * If T2 is not NULL, then *T2 is set to a matrix mapping [1 x] to [1 x']. + */ +__isl_give isl_mat *isl_mat_variable_compression(__isl_take isl_mat *B, + __isl_give isl_mat **T2) +{ + return isl_mat_final_variable_compression(B, 0, T2); +} + +/* Return "bset" and set *T and *T2 to the identity transformation + * on "bset" (provided T and T2 are not NULL). + */ +static __isl_give isl_basic_set *return_with_identity( + __isl_take isl_basic_set *bset, __isl_give isl_mat **T, + __isl_give isl_mat **T2) +{ + unsigned dim; + isl_mat *id; + + if (!bset) + return NULL; + if (!T && !T2) + return bset; + + dim = isl_basic_set_dim(bset, isl_dim_set); + id = isl_mat_identity(isl_basic_map_get_ctx(bset), 1 + dim); + if (T) + *T = isl_mat_copy(id); + if (T2) + *T2 = isl_mat_copy(id); + isl_mat_free(id); + + return bset; +} + +/* Use the n equalities of bset to unimodularly transform the + * variables x such that n transformed variables x1' have a constant value + * and rewrite the constraints of bset in terms of the remaining + * transformed variables x2'. The matrix pointed to by T maps + * the new variables x2' back to the original variables x, while T2 + * maps the original variables to the new variables. + */ +static struct isl_basic_set *compress_variables( + struct isl_basic_set *bset, struct isl_mat **T, struct isl_mat **T2) +{ + struct isl_mat *B, *TC; + unsigned dim; + + if (T) + *T = NULL; + if (T2) + *T2 = NULL; + if (!bset) + goto error; + isl_assert(bset->ctx, isl_basic_set_n_param(bset) == 0, goto error); + isl_assert(bset->ctx, bset->n_div == 0, goto error); + dim = isl_basic_set_n_dim(bset); + isl_assert(bset->ctx, bset->n_eq <= dim, goto error); + if (bset->n_eq == 0) + return return_with_identity(bset, T, T2); + + B = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, bset->n_eq, 0, 1 + dim); + TC = isl_mat_variable_compression(B, T2); + if (!TC) + goto error; + if (TC->n_col == 0) { + isl_mat_free(TC); + if (T2) { + isl_mat_free(*T2); + *T2 = NULL; + } + return isl_basic_set_set_to_empty(bset); + } + + bset = isl_basic_set_preimage(bset, T ? isl_mat_copy(TC) : TC); + if (T) + *T = TC; + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +struct isl_basic_set *isl_basic_set_remove_equalities( + struct isl_basic_set *bset, struct isl_mat **T, struct isl_mat **T2) +{ + if (T) + *T = NULL; + if (T2) + *T2 = NULL; + if (!bset) + return NULL; + isl_assert(bset->ctx, isl_basic_set_n_param(bset) == 0, goto error); + bset = isl_basic_set_gauss(bset, NULL); + if (ISL_F_ISSET(bset, ISL_BASIC_SET_EMPTY)) + return return_with_identity(bset, T, T2); + bset = compress_variables(bset, T, T2); + return bset; +error: + isl_basic_set_free(bset); + *T = NULL; + return NULL; +} + +/* Check if dimension dim belongs to a residue class + * i_dim \equiv r mod m + * with m != 1 and if so return m in *modulo and r in *residue. + * As a special case, when i_dim has a fixed value v, then + * *modulo is set to 0 and *residue to v. + * + * If i_dim does not belong to such a residue class, then *modulo + * is set to 1 and *residue is set to 0. + */ +int isl_basic_set_dim_residue_class(struct isl_basic_set *bset, + int pos, isl_int *modulo, isl_int *residue) +{ + struct isl_ctx *ctx; + struct isl_mat *H = NULL, *U = NULL, *C, *H1, *U1; + unsigned total; + unsigned nparam; + + if (!bset || !modulo || !residue) + return -1; + + if (isl_basic_set_plain_dim_is_fixed(bset, pos, residue)) { + isl_int_set_si(*modulo, 0); + return 0; + } + + ctx = isl_basic_set_get_ctx(bset); + total = isl_basic_set_total_dim(bset); + nparam = isl_basic_set_n_param(bset); + H = isl_mat_sub_alloc6(ctx, bset->eq, 0, bset->n_eq, 1, total); + H = isl_mat_left_hermite(H, 0, &U, NULL); + if (!H) + return -1; + + isl_seq_gcd(U->row[nparam + pos]+bset->n_eq, + total-bset->n_eq, modulo); + if (isl_int_is_zero(*modulo)) + isl_int_set_si(*modulo, 1); + if (isl_int_is_one(*modulo)) { + isl_int_set_si(*residue, 0); + isl_mat_free(H); + isl_mat_free(U); + return 0; + } + + C = isl_mat_alloc(ctx, 1 + bset->n_eq, 1); + if (!C) + goto error; + isl_int_set_si(C->row[0][0], 1); + isl_mat_sub_neg(ctx, C->row + 1, bset->eq, bset->n_eq, 0, 0, 1); + H1 = isl_mat_sub_alloc(H, 0, H->n_row, 0, H->n_row); + H1 = isl_mat_lin_to_aff(H1); + C = isl_mat_inverse_product(H1, C); + isl_mat_free(H); + U1 = isl_mat_sub_alloc(U, nparam+pos, 1, 0, bset->n_eq); + U1 = isl_mat_lin_to_aff(U1); + isl_mat_free(U); + C = isl_mat_product(U1, C); + if (!C) + return -1; + if (!isl_int_is_divisible_by(C->row[1][0], C->row[0][0])) { + bset = isl_basic_set_copy(bset); + bset = isl_basic_set_set_to_empty(bset); + isl_basic_set_free(bset); + isl_int_set_si(*modulo, 1); + isl_int_set_si(*residue, 0); + return 0; + } + isl_int_divexact(*residue, C->row[1][0], C->row[0][0]); + isl_int_fdiv_r(*residue, *residue, *modulo); + isl_mat_free(C); + return 0; +error: + isl_mat_free(H); + isl_mat_free(U); + return -1; +} + +/* Check if dimension dim belongs to a residue class + * i_dim \equiv r mod m + * with m != 1 and if so return m in *modulo and r in *residue. + * As a special case, when i_dim has a fixed value v, then + * *modulo is set to 0 and *residue to v. + * + * If i_dim does not belong to such a residue class, then *modulo + * is set to 1 and *residue is set to 0. + */ +int isl_set_dim_residue_class(struct isl_set *set, + int pos, isl_int *modulo, isl_int *residue) +{ + isl_int m; + isl_int r; + int i; + + if (!set || !modulo || !residue) + return -1; + + if (set->n == 0) { + isl_int_set_si(*modulo, 0); + isl_int_set_si(*residue, 0); + return 0; + } + + if (isl_basic_set_dim_residue_class(set->p[0], pos, modulo, residue)<0) + return -1; + + if (set->n == 1) + return 0; + + if (isl_int_is_one(*modulo)) + return 0; + + isl_int_init(m); + isl_int_init(r); + + for (i = 1; i < set->n; ++i) { + if (isl_basic_set_dim_residue_class(set->p[i], pos, &m, &r) < 0) + goto error; + isl_int_gcd(*modulo, *modulo, m); + isl_int_sub(m, *residue, r); + isl_int_gcd(*modulo, *modulo, m); + if (!isl_int_is_zero(*modulo)) + isl_int_fdiv_r(*residue, *residue, *modulo); + if (isl_int_is_one(*modulo)) + break; + } + + isl_int_clear(m); + isl_int_clear(r); + + return 0; +error: + isl_int_clear(m); + isl_int_clear(r); + return -1; +} + +/* Check if dimension "dim" belongs to a residue class + * i_dim \equiv r mod m + * with m != 1 and if so return m in *modulo and r in *residue. + * As a special case, when i_dim has a fixed value v, then + * *modulo is set to 0 and *residue to v. + * + * If i_dim does not belong to such a residue class, then *modulo + * is set to 1 and *residue is set to 0. + */ +isl_stat isl_set_dim_residue_class_val(__isl_keep isl_set *set, + int pos, __isl_give isl_val **modulo, __isl_give isl_val **residue) +{ + *modulo = NULL; + *residue = NULL; + if (!set) + return isl_stat_error; + *modulo = isl_val_alloc(isl_set_get_ctx(set)); + *residue = isl_val_alloc(isl_set_get_ctx(set)); + if (!*modulo || !*residue) + goto error; + if (isl_set_dim_residue_class(set, pos, + &(*modulo)->n, &(*residue)->n) < 0) + goto error; + isl_int_set_si((*modulo)->d, 1); + isl_int_set_si((*residue)->d, 1); + return isl_stat_ok; +error: + isl_val_free(*modulo); + isl_val_free(*residue); + return isl_stat_error; +} Index: lib/Analysis/isl/isl_factorization.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_factorization.h @@ -0,0 +1,29 @@ +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Data for factorizing a particular basic set. + * After applying "morph" to the basic set, there are "n_group" + * groups of consecutive set variables, each of length "len[i]", + * with 0 <= i < n_group. + * If no factorization is possible, then "n_group" is set to 0. + */ +struct isl_factorizer { + isl_morph *morph; + int n_group; + int *len; +}; +typedef struct isl_factorizer isl_factorizer; + +__isl_give isl_factorizer *isl_basic_set_factorizer( + __isl_keep isl_basic_set *bset); + +void isl_factorizer_free(__isl_take isl_factorizer *f); +void isl_factorizer_dump(__isl_take isl_factorizer *f); + +#if defined(__cplusplus) +} +#endif Index: lib/Analysis/isl/isl_factorization.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_factorization.c @@ -0,0 +1,331 @@ +/* + * Copyright 2005-2007 Universiteit Leiden + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science, + * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands + * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A, + * B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + */ + +#include +#include +#include +#include + +static __isl_give isl_factorizer *isl_factorizer_alloc( + __isl_take isl_morph *morph, int n_group) +{ + isl_factorizer *f = NULL; + int *len = NULL; + + if (!morph) + return NULL; + + if (n_group > 0) { + len = isl_alloc_array(morph->dom->ctx, int, n_group); + if (!len) + goto error; + } + + f = isl_alloc_type(morph->dom->ctx, struct isl_factorizer); + if (!f) + goto error; + + f->morph = morph; + f->n_group = n_group; + f->len = len; + + return f; +error: + free(len); + isl_morph_free(morph); + return NULL; +} + +void isl_factorizer_free(__isl_take isl_factorizer *f) +{ + if (!f) + return; + + isl_morph_free(f->morph); + free(f->len); + free(f); +} + +void isl_factorizer_dump(__isl_take isl_factorizer *f) +{ + int i; + + if (!f) + return; + + isl_morph_print_internal(f->morph, stderr); + fprintf(stderr, "["); + for (i = 0; i < f->n_group; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "%d", f->len[i]); + } + fprintf(stderr, "]\n"); +} + +__isl_give isl_factorizer *isl_factorizer_identity(__isl_keep isl_basic_set *bset) +{ + return isl_factorizer_alloc(isl_morph_identity(bset), 0); +} + +__isl_give isl_factorizer *isl_factorizer_groups(__isl_keep isl_basic_set *bset, + __isl_take isl_mat *Q, __isl_take isl_mat *U, int n, int *len) +{ + int i; + unsigned nvar; + unsigned ovar; + isl_space *dim; + isl_basic_set *dom; + isl_basic_set *ran; + isl_morph *morph; + isl_factorizer *f; + isl_mat *id; + + if (!bset || !Q || !U) + goto error; + + ovar = 1 + isl_space_offset(bset->dim, isl_dim_set); + id = isl_mat_identity(bset->ctx, ovar); + Q = isl_mat_diagonal(isl_mat_copy(id), Q); + U = isl_mat_diagonal(id, U); + + nvar = isl_basic_set_dim(bset, isl_dim_set); + dim = isl_basic_set_get_space(bset); + dom = isl_basic_set_universe(isl_space_copy(dim)); + dim = isl_space_drop_dims(dim, isl_dim_set, 0, nvar); + dim = isl_space_add_dims(dim, isl_dim_set, nvar); + ran = isl_basic_set_universe(dim); + morph = isl_morph_alloc(dom, ran, Q, U); + f = isl_factorizer_alloc(morph, n); + if (!f) + return NULL; + for (i = 0; i < n; ++i) + f->len[i] = len[i]; + return f; +error: + isl_mat_free(Q); + isl_mat_free(U); + return NULL; +} + +struct isl_factor_groups { + int *pos; /* for each column: row position of pivot */ + int *group; /* group to which a column belongs */ + int *cnt; /* number of columns in the group */ + int *rowgroup; /* group to which a constraint belongs */ +}; + +/* Initialize isl_factor_groups structure: find pivot row positions, + * each column initially belongs to its own group and the groups + * of the constraints are still unknown. + */ +static int init_groups(struct isl_factor_groups *g, __isl_keep isl_mat *H) +{ + int i, j; + + if (!H) + return -1; + + g->pos = isl_alloc_array(H->ctx, int, H->n_col); + g->group = isl_alloc_array(H->ctx, int, H->n_col); + g->cnt = isl_alloc_array(H->ctx, int, H->n_col); + g->rowgroup = isl_alloc_array(H->ctx, int, H->n_row); + + if (!g->pos || !g->group || !g->cnt || !g->rowgroup) + return -1; + + for (i = 0; i < H->n_row; ++i) + g->rowgroup[i] = -1; + for (i = 0, j = 0; i < H->n_col; ++i) { + for ( ; j < H->n_row; ++j) + if (!isl_int_is_zero(H->row[j][i])) + break; + g->pos[i] = j; + } + for (i = 0; i < H->n_col; ++i) { + g->group[i] = i; + g->cnt[i] = 1; + } + + return 0; +} + +/* Update group[k] to the group column k belongs to. + * When merging two groups, only the group of the current + * group leader is changed. Here we change the group of + * the other members to also point to the group that the + * old group leader now points to. + */ +static void update_group(struct isl_factor_groups *g, int k) +{ + int p = g->group[k]; + while (g->cnt[p] == 0) + p = g->group[p]; + g->group[k] = p; +} + +/* Merge group i with all groups of the subsequent columns + * with non-zero coefficients in row j of H. + * (The previous columns are all zero; otherwise we would have handled + * the row before.) + */ +static int update_group_i_with_row_j(struct isl_factor_groups *g, int i, int j, + __isl_keep isl_mat *H) +{ + int k; + + g->rowgroup[j] = g->group[i]; + for (k = i + 1; k < H->n_col && j >= g->pos[k]; ++k) { + update_group(g, k); + update_group(g, i); + if (g->group[k] != g->group[i] && + !isl_int_is_zero(H->row[j][k])) { + isl_assert(H->ctx, g->cnt[g->group[k]] != 0, return -1); + isl_assert(H->ctx, g->cnt[g->group[i]] != 0, return -1); + if (g->group[i] < g->group[k]) { + g->cnt[g->group[i]] += g->cnt[g->group[k]]; + g->cnt[g->group[k]] = 0; + g->group[g->group[k]] = g->group[i]; + } else { + g->cnt[g->group[k]] += g->cnt[g->group[i]]; + g->cnt[g->group[i]] = 0; + g->group[g->group[i]] = g->group[k]; + } + } + } + + return 0; +} + +/* Update the group information based on the constraint matrix. + */ +static int update_groups(struct isl_factor_groups *g, __isl_keep isl_mat *H) +{ + int i, j; + + for (i = 0; i < H->n_col && g->cnt[0] < H->n_col; ++i) { + if (g->pos[i] == H->n_row) + continue; /* A line direction */ + if (g->rowgroup[g->pos[i]] == -1) + g->rowgroup[g->pos[i]] = i; + for (j = g->pos[i] + 1; j < H->n_row; ++j) { + if (isl_int_is_zero(H->row[j][i])) + continue; + if (g->rowgroup[j] != -1) + continue; + if (update_group_i_with_row_j(g, i, j, H) < 0) + return -1; + } + } + for (i = 1; i < H->n_col; ++i) + update_group(g, i); + + return 0; +} + +static void clear_groups(struct isl_factor_groups *g) +{ + if (!g) + return; + free(g->pos); + free(g->group); + free(g->cnt); + free(g->rowgroup); +} + +/* Determine if the set variables of the basic set can be factorized and + * return the results in an isl_factorizer. + * + * The algorithm works by first computing the Hermite normal form + * and then grouping columns linked by one or more constraints together, + * where a constraints "links" two or more columns if the constraint + * has nonzero coefficients in the columns. + */ +__isl_give isl_factorizer *isl_basic_set_factorizer( + __isl_keep isl_basic_set *bset) +{ + int i, j, n, done; + isl_mat *H, *U, *Q; + unsigned nvar; + struct isl_factor_groups g = { 0 }; + isl_factorizer *f; + + if (!bset) + return NULL; + + isl_assert(bset->ctx, isl_basic_set_dim(bset, isl_dim_div) == 0, + return NULL); + + nvar = isl_basic_set_dim(bset, isl_dim_set); + if (nvar <= 1) + return isl_factorizer_identity(bset); + + H = isl_mat_alloc(bset->ctx, bset->n_eq + bset->n_ineq, nvar); + if (!H) + return NULL; + isl_mat_sub_copy(bset->ctx, H->row, bset->eq, bset->n_eq, + 0, 1 + isl_space_offset(bset->dim, isl_dim_set), nvar); + isl_mat_sub_copy(bset->ctx, H->row + bset->n_eq, bset->ineq, bset->n_ineq, + 0, 1 + isl_space_offset(bset->dim, isl_dim_set), nvar); + H = isl_mat_left_hermite(H, 0, &U, &Q); + + if (init_groups(&g, H) < 0) + goto error; + if (update_groups(&g, H) < 0) + goto error; + + if (g.cnt[0] == nvar) { + isl_mat_free(H); + isl_mat_free(U); + isl_mat_free(Q); + clear_groups(&g); + + return isl_factorizer_identity(bset); + } + + done = 0; + n = 0; + while (done != nvar) { + int group = g.group[done]; + for (i = 1; i < g.cnt[group]; ++i) { + if (g.group[done + i] == group) + continue; + for (j = done + g.cnt[group]; j < nvar; ++j) + if (g.group[j] == group) + break; + if (j == nvar) + isl_die(bset->ctx, isl_error_internal, + "internal error", goto error); + g.group[j] = g.group[done + i]; + Q = isl_mat_swap_rows(Q, done + i, j); + U = isl_mat_swap_cols(U, done + i, j); + } + done += g.cnt[group]; + g.pos[n++] = g.cnt[group]; + } + + f = isl_factorizer_groups(bset, Q, U, n, g.pos); + + isl_mat_free(H); + clear_groups(&g); + + return f; +error: + isl_mat_free(H); + isl_mat_free(U); + isl_mat_free(Q); + clear_groups(&g); + return NULL; +} Index: lib/Analysis/isl/isl_farkas.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_farkas.c @@ -0,0 +1,403 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include +#include +#include +#include + +/* + * Let C be a cone and define + * + * C' := { y | forall x in C : y x >= 0 } + * + * C' contains the coefficients of all linear constraints + * that are valid for C. + * Furthermore, C'' = C. + * + * If C is defined as { x | A x >= 0 } + * then any element in C' must be a non-negative combination + * of the rows of A, i.e., y = t A with t >= 0. That is, + * + * C' = { y | exists t >= 0 : y = t A } + * + * If any of the rows in A actually represents an equality, then + * also negative combinations of this row are allowed and so the + * non-negativity constraint on the corresponding element of t + * can be dropped. + * + * A polyhedron P = { x | b + A x >= 0 } can be represented + * in homogeneous coordinates by the cone + * C = { [z,x] | b z + A x >= and z >= 0 } + * The valid linear constraints on C correspond to the valid affine + * constraints on P. + * This is essentially Farkas' lemma. + * + * Since + * [ 1 0 ] + * [ w y ] = [t_0 t] [ b A ] + * + * we have + * + * C' = { w, y | exists t_0, t >= 0 : y = t A and w = t_0 + t b } + * or + * + * C' = { w, y | exists t >= 0 : y = t A and w - t b >= 0 } + * + * In practice, we introduce an extra variable (w), shifting all + * other variables to the right, and an extra inequality + * (w - t b >= 0) corresponding to the positivity constraint on + * the homogeneous coordinate. + * + * When going back from coefficients to solutions, we immediately + * plug in 1 for z, which corresponds to shifting all variables + * to the left, with the leftmost ending up in the constant position. + */ + +/* Add the given prefix to all named isl_dim_set dimensions in "dim". + */ +static __isl_give isl_space *isl_space_prefix(__isl_take isl_space *dim, + const char *prefix) +{ + int i; + isl_ctx *ctx; + unsigned nvar; + size_t prefix_len = strlen(prefix); + + if (!dim) + return NULL; + + ctx = isl_space_get_ctx(dim); + nvar = isl_space_dim(dim, isl_dim_set); + + for (i = 0; i < nvar; ++i) { + const char *name; + char *prefix_name; + + name = isl_space_get_dim_name(dim, isl_dim_set, i); + if (!name) + continue; + + prefix_name = isl_alloc_array(ctx, char, + prefix_len + strlen(name) + 1); + if (!prefix_name) + goto error; + memcpy(prefix_name, prefix, prefix_len); + strcpy(prefix_name + prefix_len, name); + + dim = isl_space_set_dim_name(dim, isl_dim_set, i, prefix_name); + free(prefix_name); + } + + return dim; +error: + isl_space_free(dim); + return NULL; +} + +/* Given a dimension specification of the solutions space, construct + * a dimension specification for the space of coefficients. + * + * In particular transform + * + * [params] -> { S } + * + * to + * + * { coefficients[[cst, params] -> S] } + * + * and prefix each dimension name with "c_". + */ +static __isl_give isl_space *isl_space_coefficients(__isl_take isl_space *dim) +{ + isl_space *dim_param; + unsigned nvar; + unsigned nparam; + + nvar = isl_space_dim(dim, isl_dim_set); + nparam = isl_space_dim(dim, isl_dim_param); + dim_param = isl_space_copy(dim); + dim_param = isl_space_drop_dims(dim_param, isl_dim_set, 0, nvar); + dim_param = isl_space_move_dims(dim_param, isl_dim_set, 0, + isl_dim_param, 0, nparam); + dim_param = isl_space_prefix(dim_param, "c_"); + dim_param = isl_space_insert_dims(dim_param, isl_dim_set, 0, 1); + dim_param = isl_space_set_dim_name(dim_param, isl_dim_set, 0, "c_cst"); + dim = isl_space_drop_dims(dim, isl_dim_param, 0, nparam); + dim = isl_space_prefix(dim, "c_"); + dim = isl_space_join(isl_space_from_domain(dim_param), + isl_space_from_range(dim)); + dim = isl_space_wrap(dim); + dim = isl_space_set_tuple_name(dim, isl_dim_set, "coefficients"); + + return dim; +} + +/* Drop the given prefix from all named dimensions of type "type" in "dim". + */ +static __isl_give isl_space *isl_space_unprefix(__isl_take isl_space *dim, + enum isl_dim_type type, const char *prefix) +{ + int i; + unsigned n; + size_t prefix_len = strlen(prefix); + + n = isl_space_dim(dim, type); + + for (i = 0; i < n; ++i) { + const char *name; + + name = isl_space_get_dim_name(dim, type, i); + if (!name) + continue; + if (strncmp(name, prefix, prefix_len)) + continue; + + dim = isl_space_set_dim_name(dim, type, i, name + prefix_len); + } + + return dim; +} + +/* Given a dimension specification of the space of coefficients, construct + * a dimension specification for the space of solutions. + * + * In particular transform + * + * { coefficients[[cst, params] -> S] } + * + * to + * + * [params] -> { S } + * + * and drop the "c_" prefix from the dimension names. + */ +static __isl_give isl_space *isl_space_solutions(__isl_take isl_space *dim) +{ + unsigned nparam; + + dim = isl_space_unwrap(dim); + dim = isl_space_drop_dims(dim, isl_dim_in, 0, 1); + dim = isl_space_unprefix(dim, isl_dim_in, "c_"); + dim = isl_space_unprefix(dim, isl_dim_out, "c_"); + nparam = isl_space_dim(dim, isl_dim_in); + dim = isl_space_move_dims(dim, isl_dim_param, 0, isl_dim_in, 0, nparam); + dim = isl_space_range(dim); + + return dim; +} + +/* Return the rational universe basic set in the given space. + */ +static __isl_give isl_basic_set *rational_universe(__isl_take isl_space *space) +{ + isl_basic_set *bset; + + bset = isl_basic_set_universe(space); + bset = isl_basic_set_set_rational(bset); + + return bset; +} + +/* Compute the dual of "bset" by applying Farkas' lemma. + * As explained above, we add an extra dimension to represent + * the coefficient of the constant term when going from solutions + * to coefficients (shift == 1) and we drop the extra dimension when going + * in the opposite direction (shift == -1). "dim" is the space in which + * the dual should be created. + * + * If "bset" is (obviously) empty, then the way this emptiness + * is represented by the constraints does not allow for the application + * of the standard farkas algorithm. We therefore handle this case + * specifically and return the universe basic set. + */ +static __isl_give isl_basic_set *farkas(__isl_take isl_space *space, + __isl_take isl_basic_set *bset, int shift) +{ + int i, j, k; + isl_basic_set *dual = NULL; + unsigned total; + + if (isl_basic_set_plain_is_empty(bset)) { + isl_basic_set_free(bset); + return rational_universe(space); + } + + total = isl_basic_set_total_dim(bset); + + dual = isl_basic_set_alloc_space(space, bset->n_eq + bset->n_ineq, + total, bset->n_ineq + (shift > 0)); + dual = isl_basic_set_set_rational(dual); + + for (i = 0; i < bset->n_eq + bset->n_ineq; ++i) { + k = isl_basic_set_alloc_div(dual); + if (k < 0) + goto error; + isl_int_set_si(dual->div[k][0], 0); + } + + for (i = 0; i < total; ++i) { + k = isl_basic_set_alloc_equality(dual); + if (k < 0) + goto error; + isl_seq_clr(dual->eq[k], 1 + shift + total); + isl_int_set_si(dual->eq[k][1 + shift + i], -1); + for (j = 0; j < bset->n_eq; ++j) + isl_int_set(dual->eq[k][1 + shift + total + j], + bset->eq[j][1 + i]); + for (j = 0; j < bset->n_ineq; ++j) + isl_int_set(dual->eq[k][1 + shift + total + bset->n_eq + j], + bset->ineq[j][1 + i]); + } + + for (i = 0; i < bset->n_ineq; ++i) { + k = isl_basic_set_alloc_inequality(dual); + if (k < 0) + goto error; + isl_seq_clr(dual->ineq[k], + 1 + shift + total + bset->n_eq + bset->n_ineq); + isl_int_set_si(dual->ineq[k][1 + shift + total + bset->n_eq + i], 1); + } + + if (shift > 0) { + k = isl_basic_set_alloc_inequality(dual); + if (k < 0) + goto error; + isl_seq_clr(dual->ineq[k], 2 + total); + isl_int_set_si(dual->ineq[k][1], 1); + for (j = 0; j < bset->n_eq; ++j) + isl_int_neg(dual->ineq[k][2 + total + j], + bset->eq[j][0]); + for (j = 0; j < bset->n_ineq; ++j) + isl_int_neg(dual->ineq[k][2 + total + bset->n_eq + j], + bset->ineq[j][0]); + } + + dual = isl_basic_set_remove_divs(dual); + isl_basic_set_simplify(dual); + isl_basic_set_finalize(dual); + + isl_basic_set_free(bset); + return dual; +error: + isl_basic_set_free(bset); + isl_basic_set_free(dual); + return NULL; +} + +/* Construct a basic set containing the tuples of coefficients of all + * valid affine constraints on the given basic set. + */ +__isl_give isl_basic_set *isl_basic_set_coefficients( + __isl_take isl_basic_set *bset) +{ + isl_space *dim; + + if (!bset) + return NULL; + if (bset->n_div) + isl_die(bset->ctx, isl_error_invalid, + "input set not allowed to have local variables", + goto error); + + dim = isl_basic_set_get_space(bset); + dim = isl_space_coefficients(dim); + + return farkas(dim, bset, 1); +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Construct a basic set containing the elements that satisfy all + * affine constraints whose coefficient tuples are + * contained in the given basic set. + */ +__isl_give isl_basic_set *isl_basic_set_solutions( + __isl_take isl_basic_set *bset) +{ + isl_space *dim; + + if (!bset) + return NULL; + if (bset->n_div) + isl_die(bset->ctx, isl_error_invalid, + "input set not allowed to have local variables", + goto error); + + dim = isl_basic_set_get_space(bset); + dim = isl_space_solutions(dim); + + return farkas(dim, bset, -1); +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Construct a basic set containing the tuples of coefficients of all + * valid affine constraints on the given set. + */ +__isl_give isl_basic_set *isl_set_coefficients(__isl_take isl_set *set) +{ + int i; + isl_basic_set *coeff; + + if (!set) + return NULL; + if (set->n == 0) { + isl_space *space = isl_set_get_space(set); + space = isl_space_coefficients(space); + isl_set_free(set); + return rational_universe(space); + } + + coeff = isl_basic_set_coefficients(isl_basic_set_copy(set->p[0])); + + for (i = 1; i < set->n; ++i) { + isl_basic_set *bset, *coeff_i; + bset = isl_basic_set_copy(set->p[i]); + coeff_i = isl_basic_set_coefficients(bset); + coeff = isl_basic_set_intersect(coeff, coeff_i); + } + + isl_set_free(set); + return coeff; +} + +/* Construct a basic set containing the elements that satisfy all + * affine constraints whose coefficient tuples are + * contained in the given set. + */ +__isl_give isl_basic_set *isl_set_solutions(__isl_take isl_set *set) +{ + int i; + isl_basic_set *sol; + + if (!set) + return NULL; + if (set->n == 0) { + isl_space *space = isl_set_get_space(set); + space = isl_space_solutions(space); + isl_set_free(set); + return rational_universe(space); + } + + sol = isl_basic_set_solutions(isl_basic_set_copy(set->p[0])); + + for (i = 1; i < set->n; ++i) { + isl_basic_set *bset, *sol_i; + bset = isl_basic_set_copy(set->p[i]); + sol_i = isl_basic_set_solutions(bset); + sol = isl_basic_set_intersect(sol, sol_i); + } + + isl_set_free(set); + return sol; +} Index: lib/Analysis/isl/isl_ffs.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ffs.c @@ -0,0 +1,24 @@ +#include + +#if !HAVE_DECL_FFS && !HAVE_DECL___BUILTIN_FFS && HAVE_DECL__BITSCANFORWARD +#include + +/* Implementation of ffs in terms of _BitScanForward. + * + * ffs returns the position of the least significant bit set in i, + * with the least significant bit is position 1, or 0 if not bits are set. + * + * _BitScanForward returns 1 if mask is non-zero and sets index + * to the position of the least significant bit set in i, + * with the least significant bit is position 0. + */ +int isl_ffs(int i) +{ + unsigned char non_zero; + unsigned long index, mask = i; + + non_zero = _BitScanForward(&index, mask); + + return non_zero ? 1 + index : 0; +} +#endif Index: lib/Analysis/isl/isl_flow.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_flow.c @@ -0,0 +1,2641 @@ +/* + * Copyright 2005-2007 Universiteit Leiden + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012 Universiteit Leiden + * Copyright 2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science, + * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands + * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A, + * B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include + +enum isl_restriction_type { + isl_restriction_type_empty, + isl_restriction_type_none, + isl_restriction_type_input, + isl_restriction_type_output +}; + +struct isl_restriction { + enum isl_restriction_type type; + + isl_set *source; + isl_set *sink; +}; + +/* Create a restriction of the given type. + */ +static __isl_give isl_restriction *isl_restriction_alloc( + __isl_take isl_map *source_map, enum isl_restriction_type type) +{ + isl_ctx *ctx; + isl_restriction *restr; + + if (!source_map) + return NULL; + + ctx = isl_map_get_ctx(source_map); + restr = isl_calloc_type(ctx, struct isl_restriction); + if (!restr) + goto error; + + restr->type = type; + + isl_map_free(source_map); + return restr; +error: + isl_map_free(source_map); + return NULL; +} + +/* Create a restriction that doesn't restrict anything. + */ +__isl_give isl_restriction *isl_restriction_none(__isl_take isl_map *source_map) +{ + return isl_restriction_alloc(source_map, isl_restriction_type_none); +} + +/* Create a restriction that removes everything. + */ +__isl_give isl_restriction *isl_restriction_empty( + __isl_take isl_map *source_map) +{ + return isl_restriction_alloc(source_map, isl_restriction_type_empty); +} + +/* Create a restriction on the input of the maximization problem + * based on the given source and sink restrictions. + */ +__isl_give isl_restriction *isl_restriction_input( + __isl_take isl_set *source_restr, __isl_take isl_set *sink_restr) +{ + isl_ctx *ctx; + isl_restriction *restr; + + if (!source_restr || !sink_restr) + goto error; + + ctx = isl_set_get_ctx(source_restr); + restr = isl_calloc_type(ctx, struct isl_restriction); + if (!restr) + goto error; + + restr->type = isl_restriction_type_input; + restr->source = source_restr; + restr->sink = sink_restr; + + return restr; +error: + isl_set_free(source_restr); + isl_set_free(sink_restr); + return NULL; +} + +/* Create a restriction on the output of the maximization problem + * based on the given source restriction. + */ +__isl_give isl_restriction *isl_restriction_output( + __isl_take isl_set *source_restr) +{ + isl_ctx *ctx; + isl_restriction *restr; + + if (!source_restr) + return NULL; + + ctx = isl_set_get_ctx(source_restr); + restr = isl_calloc_type(ctx, struct isl_restriction); + if (!restr) + goto error; + + restr->type = isl_restriction_type_output; + restr->source = source_restr; + + return restr; +error: + isl_set_free(source_restr); + return NULL; +} + +__isl_null isl_restriction *isl_restriction_free( + __isl_take isl_restriction *restr) +{ + if (!restr) + return NULL; + + isl_set_free(restr->source); + isl_set_free(restr->sink); + free(restr); + return NULL; +} + +isl_ctx *isl_restriction_get_ctx(__isl_keep isl_restriction *restr) +{ + return restr ? isl_set_get_ctx(restr->source) : NULL; +} + +/* A private structure to keep track of a mapping together with + * a user-specified identifier and a boolean indicating whether + * the map represents a must or may access/dependence. + */ +struct isl_labeled_map { + struct isl_map *map; + void *data; + int must; +}; + +/* A structure containing the input for dependence analysis: + * - a sink + * - n_must + n_may (<= max_source) sources + * - a function for determining the relative order of sources and sink + * The must sources are placed before the may sources. + * + * domain_map is an auxiliary map that maps the sink access relation + * to the domain of this access relation. + * This field is only needed when restrict_fn is set and + * the field itself is set by isl_access_info_compute_flow. + * + * restrict_fn is a callback that (if not NULL) will be called + * right before any lexicographical maximization. + */ +struct isl_access_info { + isl_map *domain_map; + struct isl_labeled_map sink; + isl_access_level_before level_before; + + isl_access_restrict restrict_fn; + void *restrict_user; + + int max_source; + int n_must; + int n_may; + struct isl_labeled_map source[1]; +}; + +/* A structure containing the output of dependence analysis: + * - n_source dependences + * - a wrapped subset of the sink for which definitely no source could be found + * - a wrapped subset of the sink for which possibly no source could be found + */ +struct isl_flow { + isl_set *must_no_source; + isl_set *may_no_source; + int n_source; + struct isl_labeled_map *dep; +}; + +/* Construct an isl_access_info structure and fill it up with + * the given data. The number of sources is set to 0. + */ +__isl_give isl_access_info *isl_access_info_alloc(__isl_take isl_map *sink, + void *sink_user, isl_access_level_before fn, int max_source) +{ + isl_ctx *ctx; + struct isl_access_info *acc; + + if (!sink) + return NULL; + + ctx = isl_map_get_ctx(sink); + isl_assert(ctx, max_source >= 0, goto error); + + acc = isl_calloc(ctx, struct isl_access_info, + sizeof(struct isl_access_info) + + (max_source - 1) * sizeof(struct isl_labeled_map)); + if (!acc) + goto error; + + acc->sink.map = sink; + acc->sink.data = sink_user; + acc->level_before = fn; + acc->max_source = max_source; + acc->n_must = 0; + acc->n_may = 0; + + return acc; +error: + isl_map_free(sink); + return NULL; +} + +/* Free the given isl_access_info structure. + */ +__isl_null isl_access_info *isl_access_info_free( + __isl_take isl_access_info *acc) +{ + int i; + + if (!acc) + return NULL; + isl_map_free(acc->domain_map); + isl_map_free(acc->sink.map); + for (i = 0; i < acc->n_must + acc->n_may; ++i) + isl_map_free(acc->source[i].map); + free(acc); + return NULL; +} + +isl_ctx *isl_access_info_get_ctx(__isl_keep isl_access_info *acc) +{ + return acc ? isl_map_get_ctx(acc->sink.map) : NULL; +} + +__isl_give isl_access_info *isl_access_info_set_restrict( + __isl_take isl_access_info *acc, isl_access_restrict fn, void *user) +{ + if (!acc) + return NULL; + acc->restrict_fn = fn; + acc->restrict_user = user; + return acc; +} + +/* Add another source to an isl_access_info structure, making + * sure the "must" sources are placed before the "may" sources. + * This function may be called at most max_source times on a + * given isl_access_info structure, with max_source as specified + * in the call to isl_access_info_alloc that constructed the structure. + */ +__isl_give isl_access_info *isl_access_info_add_source( + __isl_take isl_access_info *acc, __isl_take isl_map *source, + int must, void *source_user) +{ + isl_ctx *ctx; + + if (!acc) + goto error; + ctx = isl_map_get_ctx(acc->sink.map); + isl_assert(ctx, acc->n_must + acc->n_may < acc->max_source, goto error); + + if (must) { + if (acc->n_may) + acc->source[acc->n_must + acc->n_may] = + acc->source[acc->n_must]; + acc->source[acc->n_must].map = source; + acc->source[acc->n_must].data = source_user; + acc->source[acc->n_must].must = 1; + acc->n_must++; + } else { + acc->source[acc->n_must + acc->n_may].map = source; + acc->source[acc->n_must + acc->n_may].data = source_user; + acc->source[acc->n_must + acc->n_may].must = 0; + acc->n_may++; + } + + return acc; +error: + isl_map_free(source); + isl_access_info_free(acc); + return NULL; +} + +/* Return -n, 0 or n (with n a positive value), depending on whether + * the source access identified by p1 should be sorted before, together + * or after that identified by p2. + * + * If p1 appears before p2, then it should be sorted first. + * For more generic initial schedules, it is possible that neither + * p1 nor p2 appears before the other, or at least not in any obvious way. + * We therefore also check if p2 appears before p1, in which case p2 + * should be sorted first. + * If not, we try to order the two statements based on the description + * of the iteration domains. This results in an arbitrary, but fairly + * stable ordering. + */ +static int access_sort_cmp(const void *p1, const void *p2, void *user) +{ + isl_access_info *acc = user; + const struct isl_labeled_map *i1, *i2; + int level1, level2; + uint32_t h1, h2; + i1 = (const struct isl_labeled_map *) p1; + i2 = (const struct isl_labeled_map *) p2; + + level1 = acc->level_before(i1->data, i2->data); + if (level1 % 2) + return -1; + + level2 = acc->level_before(i2->data, i1->data); + if (level2 % 2) + return 1; + + h1 = isl_map_get_hash(i1->map); + h2 = isl_map_get_hash(i2->map); + return h1 > h2 ? 1 : h1 < h2 ? -1 : 0; +} + +/* Sort the must source accesses in their textual order. + */ +static __isl_give isl_access_info *isl_access_info_sort_sources( + __isl_take isl_access_info *acc) +{ + if (!acc) + return NULL; + if (acc->n_must <= 1) + return acc; + + if (isl_sort(acc->source, acc->n_must, sizeof(struct isl_labeled_map), + access_sort_cmp, acc) < 0) + return isl_access_info_free(acc); + + return acc; +} + +/* Align the parameters of the two spaces if needed and then call + * isl_space_join. + */ +static __isl_give isl_space *space_align_and_join(__isl_take isl_space *left, + __isl_take isl_space *right) +{ + if (isl_space_match(left, isl_dim_param, right, isl_dim_param)) + return isl_space_join(left, right); + + left = isl_space_align_params(left, isl_space_copy(right)); + right = isl_space_align_params(right, isl_space_copy(left)); + return isl_space_join(left, right); +} + +/* Initialize an empty isl_flow structure corresponding to a given + * isl_access_info structure. + * For each must access, two dependences are created (initialized + * to the empty relation), one for the resulting must dependences + * and one for the resulting may dependences. May accesses can + * only lead to may dependences, so only one dependence is created + * for each of them. + * This function is private as isl_flow structures are only supposed + * to be created by isl_access_info_compute_flow. + */ +static __isl_give isl_flow *isl_flow_alloc(__isl_keep isl_access_info *acc) +{ + int i, n; + struct isl_ctx *ctx; + struct isl_flow *dep; + + if (!acc) + return NULL; + + ctx = isl_map_get_ctx(acc->sink.map); + dep = isl_calloc_type(ctx, struct isl_flow); + if (!dep) + return NULL; + + n = 2 * acc->n_must + acc->n_may; + dep->dep = isl_calloc_array(ctx, struct isl_labeled_map, n); + if (n && !dep->dep) + goto error; + + dep->n_source = n; + for (i = 0; i < acc->n_must; ++i) { + isl_space *dim; + dim = space_align_and_join( + isl_map_get_space(acc->source[i].map), + isl_space_reverse(isl_map_get_space(acc->sink.map))); + dep->dep[2 * i].map = isl_map_empty(dim); + dep->dep[2 * i + 1].map = isl_map_copy(dep->dep[2 * i].map); + dep->dep[2 * i].data = acc->source[i].data; + dep->dep[2 * i + 1].data = acc->source[i].data; + dep->dep[2 * i].must = 1; + dep->dep[2 * i + 1].must = 0; + if (!dep->dep[2 * i].map || !dep->dep[2 * i + 1].map) + goto error; + } + for (i = acc->n_must; i < acc->n_must + acc->n_may; ++i) { + isl_space *dim; + dim = space_align_and_join( + isl_map_get_space(acc->source[i].map), + isl_space_reverse(isl_map_get_space(acc->sink.map))); + dep->dep[acc->n_must + i].map = isl_map_empty(dim); + dep->dep[acc->n_must + i].data = acc->source[i].data; + dep->dep[acc->n_must + i].must = 0; + if (!dep->dep[acc->n_must + i].map) + goto error; + } + + return dep; +error: + isl_flow_free(dep); + return NULL; +} + +/* Iterate over all sources and for each resulting flow dependence + * that is not empty, call the user specfied function. + * The second argument in this function call identifies the source, + * while the third argument correspond to the final argument of + * the isl_flow_foreach call. + */ +isl_stat isl_flow_foreach(__isl_keep isl_flow *deps, + isl_stat (*fn)(__isl_take isl_map *dep, int must, void *dep_user, + void *user), + void *user) +{ + int i; + + if (!deps) + return isl_stat_error; + + for (i = 0; i < deps->n_source; ++i) { + if (isl_map_plain_is_empty(deps->dep[i].map)) + continue; + if (fn(isl_map_copy(deps->dep[i].map), deps->dep[i].must, + deps->dep[i].data, user) < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Return a copy of the subset of the sink for which no source could be found. + */ +__isl_give isl_map *isl_flow_get_no_source(__isl_keep isl_flow *deps, int must) +{ + if (!deps) + return NULL; + + if (must) + return isl_set_unwrap(isl_set_copy(deps->must_no_source)); + else + return isl_set_unwrap(isl_set_copy(deps->may_no_source)); +} + +void isl_flow_free(__isl_take isl_flow *deps) +{ + int i; + + if (!deps) + return; + isl_set_free(deps->must_no_source); + isl_set_free(deps->may_no_source); + if (deps->dep) { + for (i = 0; i < deps->n_source; ++i) + isl_map_free(deps->dep[i].map); + free(deps->dep); + } + free(deps); +} + +isl_ctx *isl_flow_get_ctx(__isl_keep isl_flow *deps) +{ + return deps ? isl_set_get_ctx(deps->must_no_source) : NULL; +} + +/* Return a map that enforces that the domain iteration occurs after + * the range iteration at the given level. + * If level is odd, then the domain iteration should occur after + * the target iteration in their shared level/2 outermost loops. + * In this case we simply need to enforce that these outermost + * loop iterations are the same. + * If level is even, then the loop iterator of the domain should + * be greater than the loop iterator of the range at the last + * of the level/2 shared loops, i.e., loop level/2 - 1. + */ +static __isl_give isl_map *after_at_level(__isl_take isl_space *dim, int level) +{ + struct isl_basic_map *bmap; + + if (level % 2) + bmap = isl_basic_map_equal(dim, level/2); + else + bmap = isl_basic_map_more_at(dim, level/2 - 1); + + return isl_map_from_basic_map(bmap); +} + +/* Compute the partial lexicographic maximum of "dep" on domain "sink", + * but first check if the user has set acc->restrict_fn and if so + * update either the input or the output of the maximization problem + * with respect to the resulting restriction. + * + * Since the user expects a mapping from sink iterations to source iterations, + * whereas the domain of "dep" is a wrapped map, mapping sink iterations + * to accessed array elements, we first need to project out the accessed + * sink array elements by applying acc->domain_map. + * Similarly, the sink restriction specified by the user needs to be + * converted back to the wrapped map. + */ +static __isl_give isl_map *restricted_partial_lexmax( + __isl_keep isl_access_info *acc, __isl_take isl_map *dep, + int source, __isl_take isl_set *sink, __isl_give isl_set **empty) +{ + isl_map *source_map; + isl_restriction *restr; + isl_set *sink_domain; + isl_set *sink_restr; + isl_map *res; + + if (!acc->restrict_fn) + return isl_map_partial_lexmax(dep, sink, empty); + + source_map = isl_map_copy(dep); + source_map = isl_map_apply_domain(source_map, + isl_map_copy(acc->domain_map)); + sink_domain = isl_set_copy(sink); + sink_domain = isl_set_apply(sink_domain, isl_map_copy(acc->domain_map)); + restr = acc->restrict_fn(source_map, sink_domain, + acc->source[source].data, acc->restrict_user); + isl_set_free(sink_domain); + isl_map_free(source_map); + + if (!restr) + goto error; + if (restr->type == isl_restriction_type_input) { + dep = isl_map_intersect_range(dep, isl_set_copy(restr->source)); + sink_restr = isl_set_copy(restr->sink); + sink_restr = isl_set_apply(sink_restr, + isl_map_reverse(isl_map_copy(acc->domain_map))); + sink = isl_set_intersect(sink, sink_restr); + } else if (restr->type == isl_restriction_type_empty) { + isl_space *space = isl_map_get_space(dep); + isl_map_free(dep); + dep = isl_map_empty(space); + } + + res = isl_map_partial_lexmax(dep, sink, empty); + + if (restr->type == isl_restriction_type_output) + res = isl_map_intersect_range(res, isl_set_copy(restr->source)); + + isl_restriction_free(restr); + return res; +error: + isl_map_free(dep); + isl_set_free(sink); + *empty = NULL; + return NULL; +} + +/* Compute the last iteration of must source j that precedes the sink + * at the given level for sink iterations in set_C. + * The subset of set_C for which no such iteration can be found is returned + * in *empty. + */ +static struct isl_map *last_source(struct isl_access_info *acc, + struct isl_set *set_C, + int j, int level, struct isl_set **empty) +{ + struct isl_map *read_map; + struct isl_map *write_map; + struct isl_map *dep_map; + struct isl_map *after; + struct isl_map *result; + + read_map = isl_map_copy(acc->sink.map); + write_map = isl_map_copy(acc->source[j].map); + write_map = isl_map_reverse(write_map); + dep_map = isl_map_apply_range(read_map, write_map); + after = after_at_level(isl_map_get_space(dep_map), level); + dep_map = isl_map_intersect(dep_map, after); + result = restricted_partial_lexmax(acc, dep_map, j, set_C, empty); + result = isl_map_reverse(result); + + return result; +} + +/* For a given mapping between iterations of must source j and iterations + * of the sink, compute the last iteration of must source k preceding + * the sink at level before_level for any of the sink iterations, + * but following the corresponding iteration of must source j at level + * after_level. + */ +static struct isl_map *last_later_source(struct isl_access_info *acc, + struct isl_map *old_map, + int j, int before_level, + int k, int after_level, + struct isl_set **empty) +{ + isl_space *dim; + struct isl_set *set_C; + struct isl_map *read_map; + struct isl_map *write_map; + struct isl_map *dep_map; + struct isl_map *after_write; + struct isl_map *before_read; + struct isl_map *result; + + set_C = isl_map_range(isl_map_copy(old_map)); + read_map = isl_map_copy(acc->sink.map); + write_map = isl_map_copy(acc->source[k].map); + + write_map = isl_map_reverse(write_map); + dep_map = isl_map_apply_range(read_map, write_map); + dim = space_align_and_join(isl_map_get_space(acc->source[k].map), + isl_space_reverse(isl_map_get_space(acc->source[j].map))); + after_write = after_at_level(dim, after_level); + after_write = isl_map_apply_range(after_write, old_map); + after_write = isl_map_reverse(after_write); + dep_map = isl_map_intersect(dep_map, after_write); + before_read = after_at_level(isl_map_get_space(dep_map), before_level); + dep_map = isl_map_intersect(dep_map, before_read); + result = restricted_partial_lexmax(acc, dep_map, k, set_C, empty); + result = isl_map_reverse(result); + + return result; +} + +/* Given a shared_level between two accesses, return 1 if the + * the first can precede the second at the requested target_level. + * If the target level is odd, i.e., refers to a statement level + * dimension, then first needs to precede second at the requested + * level, i.e., shared_level must be equal to target_level. + * If the target level is odd, then the two loops should share + * at least the requested number of outer loops. + */ +static int can_precede_at_level(int shared_level, int target_level) +{ + if (shared_level < target_level) + return 0; + if ((target_level % 2) && shared_level > target_level) + return 0; + return 1; +} + +/* Given a possible flow dependence temp_rel[j] between source j and the sink + * at level sink_level, remove those elements for which + * there is an iteration of another source k < j that is closer to the sink. + * The flow dependences temp_rel[k] are updated with the improved sources. + * Any improved source needs to precede the sink at the same level + * and needs to follow source j at the same or a deeper level. + * The lower this level, the later the execution date of source k. + * We therefore consider lower levels first. + * + * If temp_rel[j] is empty, then there can be no improvement and + * we return immediately. + */ +static int intermediate_sources(__isl_keep isl_access_info *acc, + struct isl_map **temp_rel, int j, int sink_level) +{ + int k, level; + int depth = 2 * isl_map_dim(acc->source[j].map, isl_dim_in) + 1; + + if (isl_map_plain_is_empty(temp_rel[j])) + return 0; + + for (k = j - 1; k >= 0; --k) { + int plevel, plevel2; + plevel = acc->level_before(acc->source[k].data, acc->sink.data); + if (!can_precede_at_level(plevel, sink_level)) + continue; + + plevel2 = acc->level_before(acc->source[j].data, + acc->source[k].data); + + for (level = sink_level; level <= depth; ++level) { + struct isl_map *T; + struct isl_set *trest; + struct isl_map *copy; + + if (!can_precede_at_level(plevel2, level)) + continue; + + copy = isl_map_copy(temp_rel[j]); + T = last_later_source(acc, copy, j, sink_level, k, + level, &trest); + if (isl_map_plain_is_empty(T)) { + isl_set_free(trest); + isl_map_free(T); + continue; + } + temp_rel[j] = isl_map_intersect_range(temp_rel[j], trest); + temp_rel[k] = isl_map_union_disjoint(temp_rel[k], T); + } + } + + return 0; +} + +/* Compute all iterations of may source j that precedes the sink at the given + * level for sink iterations in set_C. + */ +static __isl_give isl_map *all_sources(__isl_keep isl_access_info *acc, + __isl_take isl_set *set_C, int j, int level) +{ + isl_map *read_map; + isl_map *write_map; + isl_map *dep_map; + isl_map *after; + + read_map = isl_map_copy(acc->sink.map); + read_map = isl_map_intersect_domain(read_map, set_C); + write_map = isl_map_copy(acc->source[acc->n_must + j].map); + write_map = isl_map_reverse(write_map); + dep_map = isl_map_apply_range(read_map, write_map); + after = after_at_level(isl_map_get_space(dep_map), level); + dep_map = isl_map_intersect(dep_map, after); + + return isl_map_reverse(dep_map); +} + +/* For a given mapping between iterations of must source k and iterations + * of the sink, compute the all iteration of may source j preceding + * the sink at level before_level for any of the sink iterations, + * but following the corresponding iteration of must source k at level + * after_level. + */ +static __isl_give isl_map *all_later_sources(__isl_keep isl_access_info *acc, + __isl_take isl_map *old_map, + int j, int before_level, int k, int after_level) +{ + isl_space *dim; + isl_set *set_C; + isl_map *read_map; + isl_map *write_map; + isl_map *dep_map; + isl_map *after_write; + isl_map *before_read; + + set_C = isl_map_range(isl_map_copy(old_map)); + read_map = isl_map_copy(acc->sink.map); + read_map = isl_map_intersect_domain(read_map, set_C); + write_map = isl_map_copy(acc->source[acc->n_must + j].map); + + write_map = isl_map_reverse(write_map); + dep_map = isl_map_apply_range(read_map, write_map); + dim = isl_space_join(isl_map_get_space(acc->source[acc->n_must + j].map), + isl_space_reverse(isl_map_get_space(acc->source[k].map))); + after_write = after_at_level(dim, after_level); + after_write = isl_map_apply_range(after_write, old_map); + after_write = isl_map_reverse(after_write); + dep_map = isl_map_intersect(dep_map, after_write); + before_read = after_at_level(isl_map_get_space(dep_map), before_level); + dep_map = isl_map_intersect(dep_map, before_read); + return isl_map_reverse(dep_map); +} + +/* Given the must and may dependence relations for the must accesses + * for level sink_level, check if there are any accesses of may access j + * that occur in between and return their union. + * If some of these accesses are intermediate with respect to + * (previously thought to be) must dependences, then these + * must dependences are turned into may dependences. + */ +static __isl_give isl_map *all_intermediate_sources( + __isl_keep isl_access_info *acc, __isl_take isl_map *map, + struct isl_map **must_rel, struct isl_map **may_rel, + int j, int sink_level) +{ + int k, level; + int depth = 2 * isl_map_dim(acc->source[acc->n_must + j].map, + isl_dim_in) + 1; + + for (k = 0; k < acc->n_must; ++k) { + int plevel; + + if (isl_map_plain_is_empty(may_rel[k]) && + isl_map_plain_is_empty(must_rel[k])) + continue; + + plevel = acc->level_before(acc->source[k].data, + acc->source[acc->n_must + j].data); + + for (level = sink_level; level <= depth; ++level) { + isl_map *T; + isl_map *copy; + isl_set *ran; + + if (!can_precede_at_level(plevel, level)) + continue; + + copy = isl_map_copy(may_rel[k]); + T = all_later_sources(acc, copy, j, sink_level, k, level); + map = isl_map_union(map, T); + + copy = isl_map_copy(must_rel[k]); + T = all_later_sources(acc, copy, j, sink_level, k, level); + ran = isl_map_range(isl_map_copy(T)); + map = isl_map_union(map, T); + may_rel[k] = isl_map_union_disjoint(may_rel[k], + isl_map_intersect_range(isl_map_copy(must_rel[k]), + isl_set_copy(ran))); + T = isl_map_from_domain_and_range( + isl_set_universe( + isl_space_domain(isl_map_get_space(must_rel[k]))), + ran); + must_rel[k] = isl_map_subtract(must_rel[k], T); + } + } + + return map; +} + +/* Compute dependences for the case where all accesses are "may" + * accesses, which boils down to computing memory based dependences. + * The generic algorithm would also work in this case, but it would + * be overkill to use it. + */ +static __isl_give isl_flow *compute_mem_based_dependences( + __isl_keep isl_access_info *acc) +{ + int i; + isl_set *mustdo; + isl_set *maydo; + isl_flow *res; + + res = isl_flow_alloc(acc); + if (!res) + return NULL; + + mustdo = isl_map_domain(isl_map_copy(acc->sink.map)); + maydo = isl_set_copy(mustdo); + + for (i = 0; i < acc->n_may; ++i) { + int plevel; + int is_before; + isl_space *dim; + isl_map *before; + isl_map *dep; + + plevel = acc->level_before(acc->source[i].data, acc->sink.data); + is_before = plevel & 1; + plevel >>= 1; + + dim = isl_map_get_space(res->dep[i].map); + if (is_before) + before = isl_map_lex_le_first(dim, plevel); + else + before = isl_map_lex_lt_first(dim, plevel); + dep = isl_map_apply_range(isl_map_copy(acc->source[i].map), + isl_map_reverse(isl_map_copy(acc->sink.map))); + dep = isl_map_intersect(dep, before); + mustdo = isl_set_subtract(mustdo, + isl_map_range(isl_map_copy(dep))); + res->dep[i].map = isl_map_union(res->dep[i].map, dep); + } + + res->may_no_source = isl_set_subtract(maydo, isl_set_copy(mustdo)); + res->must_no_source = mustdo; + + return res; +} + +/* Compute dependences for the case where there is at least one + * "must" access. + * + * The core algorithm considers all levels in which a source may precede + * the sink, where a level may either be a statement level or a loop level. + * The outermost statement level is 1, the first loop level is 2, etc... + * The algorithm basically does the following: + * for all levels l of the read access from innermost to outermost + * for all sources w that may precede the sink access at that level + * compute the last iteration of the source that precedes the sink access + * at that level + * add result to possible last accesses at level l of source w + * for all sources w2 that we haven't considered yet at this level that may + * also precede the sink access + * for all levels l2 of w from l to innermost + * for all possible last accesses dep of w at l + * compute last iteration of w2 between the source and sink + * of dep + * add result to possible last accesses at level l of write w2 + * and replace possible last accesses dep by the remainder + * + * + * The above algorithm is applied to the must access. During the course + * of the algorithm, we keep track of sink iterations that still + * need to be considered. These iterations are split into those that + * haven't been matched to any source access (mustdo) and those that have only + * been matched to may accesses (maydo). + * At the end of each level, we also consider the may accesses. + * In particular, we consider may accesses that precede the remaining + * sink iterations, moving elements from mustdo to maydo when appropriate, + * and may accesses that occur between a must source and a sink of any + * dependences found at the current level, turning must dependences into + * may dependences when appropriate. + * + */ +static __isl_give isl_flow *compute_val_based_dependences( + __isl_keep isl_access_info *acc) +{ + isl_ctx *ctx; + isl_flow *res; + isl_set *mustdo = NULL; + isl_set *maydo = NULL; + int level, j; + int depth; + isl_map **must_rel = NULL; + isl_map **may_rel = NULL; + + if (!acc) + return NULL; + + res = isl_flow_alloc(acc); + if (!res) + goto error; + ctx = isl_map_get_ctx(acc->sink.map); + + depth = 2 * isl_map_dim(acc->sink.map, isl_dim_in) + 1; + mustdo = isl_map_domain(isl_map_copy(acc->sink.map)); + maydo = isl_set_empty(isl_set_get_space(mustdo)); + if (!mustdo || !maydo) + goto error; + if (isl_set_plain_is_empty(mustdo)) + goto done; + + must_rel = isl_alloc_array(ctx, struct isl_map *, acc->n_must); + may_rel = isl_alloc_array(ctx, struct isl_map *, acc->n_must); + if (!must_rel || !may_rel) + goto error; + + for (level = depth; level >= 1; --level) { + for (j = acc->n_must-1; j >=0; --j) { + isl_space *space; + space = isl_map_get_space(res->dep[2 * j].map); + must_rel[j] = isl_map_empty(space); + may_rel[j] = isl_map_copy(must_rel[j]); + } + + for (j = acc->n_must - 1; j >= 0; --j) { + struct isl_map *T; + struct isl_set *rest; + int plevel; + + plevel = acc->level_before(acc->source[j].data, + acc->sink.data); + if (!can_precede_at_level(plevel, level)) + continue; + + T = last_source(acc, mustdo, j, level, &rest); + must_rel[j] = isl_map_union_disjoint(must_rel[j], T); + mustdo = rest; + + intermediate_sources(acc, must_rel, j, level); + + T = last_source(acc, maydo, j, level, &rest); + may_rel[j] = isl_map_union_disjoint(may_rel[j], T); + maydo = rest; + + intermediate_sources(acc, may_rel, j, level); + + if (isl_set_plain_is_empty(mustdo) && + isl_set_plain_is_empty(maydo)) + break; + } + for (j = j - 1; j >= 0; --j) { + int plevel; + + plevel = acc->level_before(acc->source[j].data, + acc->sink.data); + if (!can_precede_at_level(plevel, level)) + continue; + + intermediate_sources(acc, must_rel, j, level); + intermediate_sources(acc, may_rel, j, level); + } + + for (j = 0; j < acc->n_may; ++j) { + int plevel; + isl_map *T; + isl_set *ran; + + plevel = acc->level_before(acc->source[acc->n_must + j].data, + acc->sink.data); + if (!can_precede_at_level(plevel, level)) + continue; + + T = all_sources(acc, isl_set_copy(maydo), j, level); + res->dep[2 * acc->n_must + j].map = + isl_map_union(res->dep[2 * acc->n_must + j].map, T); + T = all_sources(acc, isl_set_copy(mustdo), j, level); + ran = isl_map_range(isl_map_copy(T)); + res->dep[2 * acc->n_must + j].map = + isl_map_union(res->dep[2 * acc->n_must + j].map, T); + mustdo = isl_set_subtract(mustdo, isl_set_copy(ran)); + maydo = isl_set_union_disjoint(maydo, ran); + + T = res->dep[2 * acc->n_must + j].map; + T = all_intermediate_sources(acc, T, must_rel, may_rel, + j, level); + res->dep[2 * acc->n_must + j].map = T; + } + + for (j = acc->n_must - 1; j >= 0; --j) { + res->dep[2 * j].map = + isl_map_union_disjoint(res->dep[2 * j].map, + must_rel[j]); + res->dep[2 * j + 1].map = + isl_map_union_disjoint(res->dep[2 * j + 1].map, + may_rel[j]); + } + + if (isl_set_plain_is_empty(mustdo) && + isl_set_plain_is_empty(maydo)) + break; + } + + free(must_rel); + free(may_rel); +done: + res->must_no_source = mustdo; + res->may_no_source = maydo; + return res; +error: + isl_flow_free(res); + isl_set_free(mustdo); + isl_set_free(maydo); + free(must_rel); + free(may_rel); + return NULL; +} + +/* Given a "sink" access, a list of n "source" accesses, + * compute for each iteration of the sink access + * and for each element accessed by that iteration, + * the source access in the list that last accessed the + * element accessed by the sink access before this sink access. + * Each access is given as a map from the loop iterators + * to the array indices. + * The result is a list of n relations between source and sink + * iterations and a subset of the domain of the sink access, + * corresponding to those iterations that access an element + * not previously accessed. + * + * To deal with multi-valued sink access relations, the sink iteration + * domain is first extended with dimensions that correspond to the data + * space. However, these extra dimensions are not projected out again. + * It is up to the caller to decide whether these dimensions should be kept. + */ +static __isl_give isl_flow *access_info_compute_flow_core( + __isl_take isl_access_info *acc) +{ + struct isl_flow *res = NULL; + + if (!acc) + return NULL; + + acc->sink.map = isl_map_range_map(acc->sink.map); + if (!acc->sink.map) + goto error; + + if (acc->n_must == 0) + res = compute_mem_based_dependences(acc); + else { + acc = isl_access_info_sort_sources(acc); + res = compute_val_based_dependences(acc); + } + acc = isl_access_info_free(acc); + if (!res) + return NULL; + if (!res->must_no_source || !res->may_no_source) + goto error; + return res; +error: + isl_access_info_free(acc); + isl_flow_free(res); + return NULL; +} + +/* Given a "sink" access, a list of n "source" accesses, + * compute for each iteration of the sink access + * and for each element accessed by that iteration, + * the source access in the list that last accessed the + * element accessed by the sink access before this sink access. + * Each access is given as a map from the loop iterators + * to the array indices. + * The result is a list of n relations between source and sink + * iterations and a subset of the domain of the sink access, + * corresponding to those iterations that access an element + * not previously accessed. + * + * To deal with multi-valued sink access relations, + * access_info_compute_flow_core extends the sink iteration domain + * with dimensions that correspond to the data space. These extra dimensions + * are projected out from the result of access_info_compute_flow_core. + */ +__isl_give isl_flow *isl_access_info_compute_flow(__isl_take isl_access_info *acc) +{ + int j; + struct isl_flow *res; + + if (!acc) + return NULL; + + acc->domain_map = isl_map_domain_map(isl_map_copy(acc->sink.map)); + res = access_info_compute_flow_core(acc); + if (!res) + return NULL; + + for (j = 0; j < res->n_source; ++j) { + res->dep[j].map = isl_map_range_factor_domain(res->dep[j].map); + if (!res->dep[j].map) + goto error; + } + + return res; +error: + isl_flow_free(res); + return NULL; +} + + +/* Keep track of some information about a schedule for a given + * access. In particular, keep track of which dimensions + * have a constant value and of the actual constant values. + */ +struct isl_sched_info { + int *is_cst; + isl_vec *cst; +}; + +static void sched_info_free(__isl_take struct isl_sched_info *info) +{ + if (!info) + return; + isl_vec_free(info->cst); + free(info->is_cst); + free(info); +} + +/* Extract information on the constant dimensions of the schedule + * for a given access. The "map" is of the form + * + * [S -> D] -> A + * + * with S the schedule domain, D the iteration domain and A the data domain. + */ +static __isl_give struct isl_sched_info *sched_info_alloc( + __isl_keep isl_map *map) +{ + isl_ctx *ctx; + isl_space *dim; + struct isl_sched_info *info; + int i, n; + + if (!map) + return NULL; + + dim = isl_space_unwrap(isl_space_domain(isl_map_get_space(map))); + if (!dim) + return NULL; + n = isl_space_dim(dim, isl_dim_in); + isl_space_free(dim); + + ctx = isl_map_get_ctx(map); + info = isl_alloc_type(ctx, struct isl_sched_info); + if (!info) + return NULL; + info->is_cst = isl_alloc_array(ctx, int, n); + info->cst = isl_vec_alloc(ctx, n); + if (n && (!info->is_cst || !info->cst)) + goto error; + + for (i = 0; i < n; ++i) { + isl_val *v; + + v = isl_map_plain_get_val_if_fixed(map, isl_dim_in, i); + if (!v) + goto error; + info->is_cst[i] = !isl_val_is_nan(v); + if (info->is_cst[i]) + info->cst = isl_vec_set_element_val(info->cst, i, v); + else + isl_val_free(v); + } + + return info; +error: + sched_info_free(info); + return NULL; +} + +/* This structure represents the input for a dependence analysis computation. + * + * "sink" represents the sink accesses. + * "must_source" represents the definite source accesses. + * "may_source" represents the possible source accesses. + * + * "schedule" or "schedule_map" represents the execution order. + * Exactly one of these fields should be NULL. The other field + * determines the execution order. + * + * The domains of these four maps refer to the same iteration spaces(s). + * The ranges of the first three maps also refer to the same data space(s). + * + * After a call to isl_union_access_info_introduce_schedule, + * the "schedule_map" field no longer contains useful information. + */ +struct isl_union_access_info { + isl_union_map *sink; + isl_union_map *must_source; + isl_union_map *may_source; + + isl_schedule *schedule; + isl_union_map *schedule_map; +}; + +/* Free "access" and return NULL. + */ +__isl_null isl_union_access_info *isl_union_access_info_free( + __isl_take isl_union_access_info *access) +{ + if (!access) + return NULL; + + isl_union_map_free(access->sink); + isl_union_map_free(access->must_source); + isl_union_map_free(access->may_source); + isl_schedule_free(access->schedule); + isl_union_map_free(access->schedule_map); + free(access); + + return NULL; +} + +/* Return the isl_ctx to which "access" belongs. + */ +isl_ctx *isl_union_access_info_get_ctx(__isl_keep isl_union_access_info *access) +{ + return access ? isl_union_map_get_ctx(access->sink) : NULL; +} + +/* Create a new isl_union_access_info with the given sink accesses and + * and no source accesses or schedule information. + * + * By default, we use the schedule field of the isl_union_access_info, + * but this may be overridden by a call + * to isl_union_access_info_set_schedule_map. + */ +__isl_give isl_union_access_info *isl_union_access_info_from_sink( + __isl_take isl_union_map *sink) +{ + isl_ctx *ctx; + isl_space *space; + isl_union_map *empty; + isl_union_access_info *access; + + if (!sink) + return NULL; + ctx = isl_union_map_get_ctx(sink); + access = isl_alloc_type(ctx, isl_union_access_info); + if (!access) + goto error; + + space = isl_union_map_get_space(sink); + empty = isl_union_map_empty(isl_space_copy(space)); + access->sink = sink; + access->must_source = isl_union_map_copy(empty); + access->may_source = empty; + access->schedule = isl_schedule_empty(space); + access->schedule_map = NULL; + + if (!access->sink || !access->must_source || + !access->may_source || !access->schedule) + return isl_union_access_info_free(access); + + return access; +error: + isl_union_map_free(sink); + return NULL; +} + +/* Replace the definite source accesses of "access" by "must_source". + */ +__isl_give isl_union_access_info *isl_union_access_info_set_must_source( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *must_source) +{ + if (!access || !must_source) + goto error; + + isl_union_map_free(access->must_source); + access->must_source = must_source; + + return access; +error: + isl_union_access_info_free(access); + isl_union_map_free(must_source); + return NULL; +} + +/* Replace the possible source accesses of "access" by "may_source". + */ +__isl_give isl_union_access_info *isl_union_access_info_set_may_source( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *may_source) +{ + if (!access || !may_source) + goto error; + + isl_union_map_free(access->may_source); + access->may_source = may_source; + + return access; +error: + isl_union_access_info_free(access); + isl_union_map_free(may_source); + return NULL; +} + +/* Replace the schedule of "access" by "schedule". + * Also free the schedule_map in case it was set last. + */ +__isl_give isl_union_access_info *isl_union_access_info_set_schedule( + __isl_take isl_union_access_info *access, + __isl_take isl_schedule *schedule) +{ + if (!access || !schedule) + goto error; + + access->schedule_map = isl_union_map_free(access->schedule_map); + isl_schedule_free(access->schedule); + access->schedule = schedule; + + return access; +error: + isl_union_access_info_free(access); + isl_schedule_free(schedule); + return NULL; +} + +/* Replace the schedule map of "access" by "schedule_map". + * Also free the schedule in case it was set last. + */ +__isl_give isl_union_access_info *isl_union_access_info_set_schedule_map( + __isl_take isl_union_access_info *access, + __isl_take isl_union_map *schedule_map) +{ + if (!access || !schedule_map) + goto error; + + isl_union_map_free(access->schedule_map); + access->schedule = isl_schedule_free(access->schedule); + access->schedule_map = schedule_map; + + return access; +error: + isl_union_access_info_free(access); + isl_union_map_free(schedule_map); + return NULL; +} + +__isl_give isl_union_access_info *isl_union_access_info_copy( + __isl_keep isl_union_access_info *access) +{ + isl_union_access_info *copy; + + if (!access) + return NULL; + copy = isl_union_access_info_from_sink( + isl_union_map_copy(access->sink)); + copy = isl_union_access_info_set_must_source(copy, + isl_union_map_copy(access->must_source)); + copy = isl_union_access_info_set_may_source(copy, + isl_union_map_copy(access->may_source)); + if (access->schedule) + copy = isl_union_access_info_set_schedule(copy, + isl_schedule_copy(access->schedule)); + else + copy = isl_union_access_info_set_schedule_map(copy, + isl_union_map_copy(access->schedule_map)); + + return copy; +} + +/* Print a key-value pair of a YAML mapping to "p", + * with key "name" and value "umap". + */ +static __isl_give isl_printer *print_union_map_field(__isl_take isl_printer *p, + const char *name, __isl_keep isl_union_map *umap) +{ + p = isl_printer_print_str(p, name); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_map(p, umap); + p = isl_printer_print_str(p, "\""); + p = isl_printer_yaml_next(p); + + return p; +} + +/* Print the information contained in "access" to "p". + * The information is printed as a YAML document. + */ +__isl_give isl_printer *isl_printer_print_union_access_info( + __isl_take isl_printer *p, __isl_keep isl_union_access_info *access) +{ + if (!access) + return isl_printer_free(p); + + p = isl_printer_yaml_start_mapping(p); + p = print_union_map_field(p, "sink", access->sink); + p = print_union_map_field(p, "must_source", access->must_source); + p = print_union_map_field(p, "may_source", access->may_source); + if (access->schedule) { + p = isl_printer_print_str(p, "schedule"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_schedule(p, access->schedule); + p = isl_printer_yaml_next(p); + } else { + p = print_union_map_field(p, "schedule_map", + access->schedule_map); + } + p = isl_printer_yaml_end_mapping(p); + + return p; +} + +/* Return a string representation of the information in "access". + * The information is printed in flow format. + */ +__isl_give char *isl_union_access_info_to_str( + __isl_keep isl_union_access_info *access) +{ + isl_printer *p; + char *s; + + if (!access) + return NULL; + + p = isl_printer_to_str(isl_union_access_info_get_ctx(access)); + p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW); + p = isl_printer_print_union_access_info(p, access); + s = isl_printer_get_str(p); + isl_printer_free(p); + + return s; +} + +/* Update the fields of "access" such that they all have the same parameters, + * keeping in mind that the schedule_map field may be NULL and ignoring + * the schedule field. + */ +static __isl_give isl_union_access_info *isl_union_access_info_align_params( + __isl_take isl_union_access_info *access) +{ + isl_space *space; + + if (!access) + return NULL; + + space = isl_union_map_get_space(access->sink); + space = isl_space_align_params(space, + isl_union_map_get_space(access->must_source)); + space = isl_space_align_params(space, + isl_union_map_get_space(access->may_source)); + if (access->schedule_map) + space = isl_space_align_params(space, + isl_union_map_get_space(access->schedule_map)); + access->sink = isl_union_map_align_params(access->sink, + isl_space_copy(space)); + access->must_source = isl_union_map_align_params(access->must_source, + isl_space_copy(space)); + access->may_source = isl_union_map_align_params(access->may_source, + isl_space_copy(space)); + if (!access->schedule_map) { + isl_space_free(space); + } else { + access->schedule_map = + isl_union_map_align_params(access->schedule_map, space); + if (!access->schedule_map) + return isl_union_access_info_free(access); + } + + if (!access->sink || !access->must_source || !access->may_source) + return isl_union_access_info_free(access); + + return access; +} + +/* Prepend the schedule dimensions to the iteration domains. + * + * That is, if the schedule is of the form + * + * D -> S + * + * while the access relations are of the form + * + * D -> A + * + * then the updated access relations are of the form + * + * [S -> D] -> A + * + * The schedule map is also replaced by the map + * + * [S -> D] -> D + * + * that is used during the internal computation. + * Neither the original schedule map nor this updated schedule map + * are used after the call to this function. + */ +static __isl_give isl_union_access_info * +isl_union_access_info_introduce_schedule( + __isl_take isl_union_access_info *access) +{ + isl_union_map *sm; + + if (!access) + return NULL; + + sm = isl_union_map_reverse(access->schedule_map); + sm = isl_union_map_range_map(sm); + access->sink = isl_union_map_apply_range(isl_union_map_copy(sm), + access->sink); + access->may_source = isl_union_map_apply_range(isl_union_map_copy(sm), + access->may_source); + access->must_source = isl_union_map_apply_range(isl_union_map_copy(sm), + access->must_source); + access->schedule_map = sm; + + if (!access->sink || !access->must_source || + !access->may_source || !access->schedule_map) + return isl_union_access_info_free(access); + + return access; +} + +/* This structure represents the result of a dependence analysis computation. + * + * "must_dep" represents the full definite dependences + * "may_dep" represents the full non-definite dependences. + * Both are of the form + * + * [Source] -> [[Sink -> Data]] + * + * (after the schedule dimensions have been projected out). + * "must_no_source" represents the subset of the sink accesses for which + * definitely no source was found. + * "may_no_source" represents the subset of the sink accesses for which + * possibly, but not definitely, no source was found. + */ +struct isl_union_flow { + isl_union_map *must_dep; + isl_union_map *may_dep; + isl_union_map *must_no_source; + isl_union_map *may_no_source; +}; + +/* Return the isl_ctx to which "flow" belongs. + */ +isl_ctx *isl_union_flow_get_ctx(__isl_keep isl_union_flow *flow) +{ + return flow ? isl_union_map_get_ctx(flow->must_dep) : NULL; +} + +/* Free "flow" and return NULL. + */ +__isl_null isl_union_flow *isl_union_flow_free(__isl_take isl_union_flow *flow) +{ + if (!flow) + return NULL; + isl_union_map_free(flow->must_dep); + isl_union_map_free(flow->may_dep); + isl_union_map_free(flow->must_no_source); + isl_union_map_free(flow->may_no_source); + free(flow); + return NULL; +} + +void isl_union_flow_dump(__isl_keep isl_union_flow *flow) +{ + if (!flow) + return; + + fprintf(stderr, "must dependences: "); + isl_union_map_dump(flow->must_dep); + fprintf(stderr, "may dependences: "); + isl_union_map_dump(flow->may_dep); + fprintf(stderr, "must no source: "); + isl_union_map_dump(flow->must_no_source); + fprintf(stderr, "may no source: "); + isl_union_map_dump(flow->may_no_source); +} + +/* Return the full definite dependences in "flow", with accessed elements. + */ +__isl_give isl_union_map *isl_union_flow_get_full_must_dependence( + __isl_keep isl_union_flow *flow) +{ + if (!flow) + return NULL; + return isl_union_map_copy(flow->must_dep); +} + +/* Return the full possible dependences in "flow", including the definite + * dependences, with accessed elements. + */ +__isl_give isl_union_map *isl_union_flow_get_full_may_dependence( + __isl_keep isl_union_flow *flow) +{ + if (!flow) + return NULL; + return isl_union_map_union(isl_union_map_copy(flow->must_dep), + isl_union_map_copy(flow->may_dep)); +} + +/* Return the definite dependences in "flow", without the accessed elements. + */ +__isl_give isl_union_map *isl_union_flow_get_must_dependence( + __isl_keep isl_union_flow *flow) +{ + isl_union_map *dep; + + if (!flow) + return NULL; + dep = isl_union_map_copy(flow->must_dep); + return isl_union_map_range_factor_domain(dep); +} + +/* Return the possible dependences in "flow", including the definite + * dependences, without the accessed elements. + */ +__isl_give isl_union_map *isl_union_flow_get_may_dependence( + __isl_keep isl_union_flow *flow) +{ + isl_union_map *dep; + + if (!flow) + return NULL; + dep = isl_union_map_union(isl_union_map_copy(flow->must_dep), + isl_union_map_copy(flow->may_dep)); + return isl_union_map_range_factor_domain(dep); +} + +/* Return the non-definite dependences in "flow". + */ +static __isl_give isl_union_map *isl_union_flow_get_non_must_dependence( + __isl_keep isl_union_flow *flow) +{ + if (!flow) + return NULL; + return isl_union_map_copy(flow->may_dep); +} + +/* Return the subset of the sink accesses for which definitely + * no source was found. + */ +__isl_give isl_union_map *isl_union_flow_get_must_no_source( + __isl_keep isl_union_flow *flow) +{ + if (!flow) + return NULL; + return isl_union_map_copy(flow->must_no_source); +} + +/* Return the subset of the sink accesses for which possibly + * no source was found, including those for which definitely + * no source was found. + */ +__isl_give isl_union_map *isl_union_flow_get_may_no_source( + __isl_keep isl_union_flow *flow) +{ + if (!flow) + return NULL; + return isl_union_map_union(isl_union_map_copy(flow->must_no_source), + isl_union_map_copy(flow->may_no_source)); +} + +/* Return the subset of the sink accesses for which possibly, but not + * definitely, no source was found. + */ +static __isl_give isl_union_map *isl_union_flow_get_non_must_no_source( + __isl_keep isl_union_flow *flow) +{ + if (!flow) + return NULL; + return isl_union_map_copy(flow->may_no_source); +} + +/* Create a new isl_union_flow object, initialized with empty + * dependence relations and sink subsets. + */ +static __isl_give isl_union_flow *isl_union_flow_alloc( + __isl_take isl_space *space) +{ + isl_ctx *ctx; + isl_union_map *empty; + isl_union_flow *flow; + + if (!space) + return NULL; + ctx = isl_space_get_ctx(space); + flow = isl_alloc_type(ctx, isl_union_flow); + if (!flow) + goto error; + + empty = isl_union_map_empty(space); + flow->must_dep = isl_union_map_copy(empty); + flow->may_dep = isl_union_map_copy(empty); + flow->must_no_source = isl_union_map_copy(empty); + flow->may_no_source = empty; + + if (!flow->must_dep || !flow->may_dep || + !flow->must_no_source || !flow->may_no_source) + return isl_union_flow_free(flow); + + return flow; +error: + isl_space_free(space); + return NULL; +} + +/* Drop the schedule dimensions from the iteration domains in "flow". + * In particular, the schedule dimensions have been prepended + * to the iteration domains prior to the dependence analysis by + * replacing the iteration domain D, by the wrapped map [S -> D]. + * Replace these wrapped maps by the original D. + * + * In particular, the dependences computed by access_info_compute_flow_core + * are of the form + * + * [S -> D] -> [[S' -> D'] -> A] + * + * The schedule dimensions are projected out by first currying the range, + * resulting in + * + * [S -> D] -> [S' -> [D' -> A]] + * + * and then computing the factor range + * + * D -> [D' -> A] + */ +static __isl_give isl_union_flow *isl_union_flow_drop_schedule( + __isl_take isl_union_flow *flow) +{ + if (!flow) + return NULL; + + flow->must_dep = isl_union_map_range_curry(flow->must_dep); + flow->must_dep = isl_union_map_factor_range(flow->must_dep); + flow->may_dep = isl_union_map_range_curry(flow->may_dep); + flow->may_dep = isl_union_map_factor_range(flow->may_dep); + flow->must_no_source = + isl_union_map_domain_factor_range(flow->must_no_source); + flow->may_no_source = + isl_union_map_domain_factor_range(flow->may_no_source); + + if (!flow->must_dep || !flow->may_dep || + !flow->must_no_source || !flow->may_no_source) + return isl_union_flow_free(flow); + + return flow; +} + +struct isl_compute_flow_data { + isl_union_map *must_source; + isl_union_map *may_source; + isl_union_flow *flow; + + int count; + int must; + isl_space *dim; + struct isl_sched_info *sink_info; + struct isl_sched_info **source_info; + isl_access_info *accesses; +}; + +static isl_stat count_matching_array(__isl_take isl_map *map, void *user) +{ + int eq; + isl_space *dim; + struct isl_compute_flow_data *data; + + data = (struct isl_compute_flow_data *)user; + + dim = isl_space_range(isl_map_get_space(map)); + + eq = isl_space_is_equal(dim, data->dim); + + isl_space_free(dim); + isl_map_free(map); + + if (eq < 0) + return isl_stat_error; + if (eq) + data->count++; + + return isl_stat_ok; +} + +static isl_stat collect_matching_array(__isl_take isl_map *map, void *user) +{ + int eq; + isl_space *dim; + struct isl_sched_info *info; + struct isl_compute_flow_data *data; + + data = (struct isl_compute_flow_data *)user; + + dim = isl_space_range(isl_map_get_space(map)); + + eq = isl_space_is_equal(dim, data->dim); + + isl_space_free(dim); + + if (eq < 0) + goto error; + if (!eq) { + isl_map_free(map); + return isl_stat_ok; + } + + info = sched_info_alloc(map); + data->source_info[data->count] = info; + + data->accesses = isl_access_info_add_source(data->accesses, + map, data->must, info); + + data->count++; + + return isl_stat_ok; +error: + isl_map_free(map); + return isl_stat_error; +} + +/* Determine the shared nesting level and the "textual order" of + * the given accesses. + * + * We first determine the minimal schedule dimension for both accesses. + * + * If among those dimensions, we can find one where both have a fixed + * value and if moreover those values are different, then the previous + * dimension is the last shared nesting level and the textual order + * is determined based on the order of the fixed values. + * If no such fixed values can be found, then we set the shared + * nesting level to the minimal schedule dimension, with no textual ordering. + */ +static int before(void *first, void *second) +{ + struct isl_sched_info *info1 = first; + struct isl_sched_info *info2 = second; + int n1, n2; + int i; + + n1 = isl_vec_size(info1->cst); + n2 = isl_vec_size(info2->cst); + + if (n2 < n1) + n1 = n2; + + for (i = 0; i < n1; ++i) { + int r; + int cmp; + + if (!info1->is_cst[i]) + continue; + if (!info2->is_cst[i]) + continue; + cmp = isl_vec_cmp_element(info1->cst, info2->cst, i); + if (cmp == 0) + continue; + + r = 2 * i + (cmp < 0); + + return r; + } + + return 2 * n1; +} + +/* Given a sink access, look for all the source accesses that access + * the same array and perform dataflow analysis on them using + * isl_access_info_compute_flow_core. + */ +static isl_stat compute_flow(__isl_take isl_map *map, void *user) +{ + int i; + isl_ctx *ctx; + struct isl_compute_flow_data *data; + isl_flow *flow; + isl_union_flow *df; + + data = (struct isl_compute_flow_data *)user; + df = data->flow; + + ctx = isl_map_get_ctx(map); + + data->accesses = NULL; + data->sink_info = NULL; + data->source_info = NULL; + data->count = 0; + data->dim = isl_space_range(isl_map_get_space(map)); + + if (isl_union_map_foreach_map(data->must_source, + &count_matching_array, data) < 0) + goto error; + if (isl_union_map_foreach_map(data->may_source, + &count_matching_array, data) < 0) + goto error; + + data->sink_info = sched_info_alloc(map); + data->source_info = isl_calloc_array(ctx, struct isl_sched_info *, + data->count); + + data->accesses = isl_access_info_alloc(isl_map_copy(map), + data->sink_info, &before, data->count); + if (!data->sink_info || (data->count && !data->source_info) || + !data->accesses) + goto error; + data->count = 0; + data->must = 1; + if (isl_union_map_foreach_map(data->must_source, + &collect_matching_array, data) < 0) + goto error; + data->must = 0; + if (isl_union_map_foreach_map(data->may_source, + &collect_matching_array, data) < 0) + goto error; + + flow = access_info_compute_flow_core(data->accesses); + data->accesses = NULL; + + if (!flow) + goto error; + + df->must_no_source = isl_union_map_union(df->must_no_source, + isl_union_map_from_map(isl_flow_get_no_source(flow, 1))); + df->may_no_source = isl_union_map_union(df->may_no_source, + isl_union_map_from_map(isl_flow_get_no_source(flow, 0))); + + for (i = 0; i < flow->n_source; ++i) { + isl_union_map *dep; + dep = isl_union_map_from_map(isl_map_copy(flow->dep[i].map)); + if (flow->dep[i].must) + df->must_dep = isl_union_map_union(df->must_dep, dep); + else + df->may_dep = isl_union_map_union(df->may_dep, dep); + } + + isl_flow_free(flow); + + sched_info_free(data->sink_info); + if (data->source_info) { + for (i = 0; i < data->count; ++i) + sched_info_free(data->source_info[i]); + free(data->source_info); + } + isl_space_free(data->dim); + isl_map_free(map); + + return isl_stat_ok; +error: + isl_access_info_free(data->accesses); + sched_info_free(data->sink_info); + if (data->source_info) { + for (i = 0; i < data->count; ++i) + sched_info_free(data->source_info[i]); + free(data->source_info); + } + isl_space_free(data->dim); + isl_map_free(map); + + return isl_stat_error; +} + +/* Remove the must accesses from the may accesses. + * + * A must access always trumps a may access, so there is no need + * for a must access to also be considered as a may access. Doing so + * would only cost extra computations only to find out that + * the duplicated may access does not make any difference. + */ +static __isl_give isl_union_access_info *isl_union_access_info_normalize( + __isl_take isl_union_access_info *access) +{ + if (!access) + return NULL; + access->may_source = isl_union_map_subtract(access->may_source, + isl_union_map_copy(access->must_source)); + if (!access->may_source) + return isl_union_access_info_free(access); + + return access; +} + +/* Given a description of the "sink" accesses, the "source" accesses and + * a schedule, compute for each instance of a sink access + * and for each element accessed by that instance, + * the possible or definite source accesses that last accessed the + * element accessed by the sink access before this sink access + * in the sense that there is no intermediate definite source access. + * + * The must_no_source and may_no_source elements of the result + * are subsets of access->sink. The elements must_dep and may_dep + * map domain elements of access->{may,must)_source to + * domain elements of access->sink. + * + * This function is used when only the schedule map representation + * is available. + * + * We first prepend the schedule dimensions to the domain + * of the accesses so that we can easily compare their relative order. + * Then we consider each sink access individually in compute_flow. + */ +static __isl_give isl_union_flow *compute_flow_union_map( + __isl_take isl_union_access_info *access) +{ + struct isl_compute_flow_data data; + + access = isl_union_access_info_align_params(access); + access = isl_union_access_info_introduce_schedule(access); + if (!access) + return NULL; + + data.must_source = access->must_source; + data.may_source = access->may_source; + + data.flow = isl_union_flow_alloc(isl_union_map_get_space(access->sink)); + + if (isl_union_map_foreach_map(access->sink, &compute_flow, &data) < 0) + goto error; + + data.flow = isl_union_flow_drop_schedule(data.flow); + + isl_union_access_info_free(access); + return data.flow; +error: + isl_union_access_info_free(access); + isl_union_flow_free(data.flow); + return NULL; +} + +/* A schedule access relation. + * + * The access relation "access" is of the form [S -> D] -> A, + * where S corresponds to the prefix schedule at "node". + * "must" is only relevant for source accesses and indicates + * whether the access is a must source or a may source. + */ +struct isl_scheduled_access { + isl_map *access; + int must; + isl_schedule_node *node; +}; + +/* Data structure for keeping track of individual scheduled sink and source + * accesses when computing dependence analysis based on a schedule tree. + * + * "n_sink" is the number of used entries in "sink" + * "n_source" is the number of used entries in "source" + * + * "set_sink", "must" and "node" are only used inside collect_sink_source, + * to keep track of the current node and + * of what extract_sink_source needs to do. + */ +struct isl_compute_flow_schedule_data { + isl_union_access_info *access; + + int n_sink; + int n_source; + + struct isl_scheduled_access *sink; + struct isl_scheduled_access *source; + + int set_sink; + int must; + isl_schedule_node *node; +}; + +/* Align the parameters of all sinks with all sources. + * + * If there are no sinks or no sources, then no alignment is needed. + */ +static void isl_compute_flow_schedule_data_align_params( + struct isl_compute_flow_schedule_data *data) +{ + int i; + isl_space *space; + + if (data->n_sink == 0 || data->n_source == 0) + return; + + space = isl_map_get_space(data->sink[0].access); + + for (i = 1; i < data->n_sink; ++i) + space = isl_space_align_params(space, + isl_map_get_space(data->sink[i].access)); + for (i = 0; i < data->n_source; ++i) + space = isl_space_align_params(space, + isl_map_get_space(data->source[i].access)); + + for (i = 0; i < data->n_sink; ++i) + data->sink[i].access = + isl_map_align_params(data->sink[i].access, + isl_space_copy(space)); + for (i = 0; i < data->n_source; ++i) + data->source[i].access = + isl_map_align_params(data->source[i].access, + isl_space_copy(space)); + + isl_space_free(space); +} + +/* Free all the memory referenced from "data". + * Do not free "data" itself as it may be allocated on the stack. + */ +static void isl_compute_flow_schedule_data_clear( + struct isl_compute_flow_schedule_data *data) +{ + int i; + + if (!data->sink) + return; + + for (i = 0; i < data->n_sink; ++i) { + isl_map_free(data->sink[i].access); + isl_schedule_node_free(data->sink[i].node); + } + + for (i = 0; i < data->n_source; ++i) { + isl_map_free(data->source[i].access); + isl_schedule_node_free(data->source[i].node); + } + + free(data->sink); +} + +/* isl_schedule_foreach_schedule_node_top_down callback for counting + * (an upper bound on) the number of sinks and sources. + * + * Sinks and sources are only extracted at leaves of the tree, + * so we skip the node if it is not a leaf. + * Otherwise we increment data->n_sink and data->n_source with + * the number of spaces in the sink and source access domains + * that reach this node. + */ +static isl_bool count_sink_source(__isl_keep isl_schedule_node *node, + void *user) +{ + struct isl_compute_flow_schedule_data *data = user; + isl_union_set *domain; + isl_union_map *umap; + isl_bool r = isl_bool_false; + + if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf) + return isl_bool_true; + + domain = isl_schedule_node_get_universe_domain(node); + + umap = isl_union_map_copy(data->access->sink); + umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(domain)); + data->n_sink += isl_union_map_n_map(umap); + isl_union_map_free(umap); + if (!umap) + r = isl_bool_error; + + umap = isl_union_map_copy(data->access->must_source); + umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(domain)); + data->n_source += isl_union_map_n_map(umap); + isl_union_map_free(umap); + if (!umap) + r = isl_bool_error; + + umap = isl_union_map_copy(data->access->may_source); + umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(domain)); + data->n_source += isl_union_map_n_map(umap); + isl_union_map_free(umap); + if (!umap) + r = isl_bool_error; + + isl_union_set_free(domain); + + return r; +} + +/* Add a single scheduled sink or source (depending on data->set_sink) + * with scheduled access relation "map", must property data->must and + * schedule node data->node to the list of sinks or sources. + */ +static isl_stat extract_sink_source(__isl_take isl_map *map, void *user) +{ + struct isl_compute_flow_schedule_data *data = user; + struct isl_scheduled_access *access; + + if (data->set_sink) + access = data->sink + data->n_sink++; + else + access = data->source + data->n_source++; + + access->access = map; + access->must = data->must; + access->node = isl_schedule_node_copy(data->node); + + return isl_stat_ok; +} + +/* isl_schedule_foreach_schedule_node_top_down callback for collecting + * individual scheduled source and sink accesses (taking into account + * the domain of the schedule). + * + * We only collect accesses at the leaves of the schedule tree. + * We prepend the schedule dimensions at the leaf to the iteration + * domains of the source and sink accesses and then extract + * the individual accesses (per space). + * + * In particular, if the prefix schedule at the node is of the form + * + * D -> S + * + * while the access relations are of the form + * + * D -> A + * + * then the updated access relations are of the form + * + * [S -> D] -> A + * + * Note that S consists of a single space such that introducing S + * in the access relations does not increase the number of spaces. + */ +static isl_bool collect_sink_source(__isl_keep isl_schedule_node *node, + void *user) +{ + struct isl_compute_flow_schedule_data *data = user; + isl_union_map *prefix; + isl_union_map *umap; + isl_bool r = isl_bool_false; + + if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf) + return isl_bool_true; + + data->node = node; + + prefix = isl_schedule_node_get_prefix_schedule_relation(node); + prefix = isl_union_map_reverse(prefix); + prefix = isl_union_map_range_map(prefix); + + data->set_sink = 1; + umap = isl_union_map_copy(data->access->sink); + umap = isl_union_map_apply_range(isl_union_map_copy(prefix), umap); + if (isl_union_map_foreach_map(umap, &extract_sink_source, data) < 0) + r = isl_bool_error; + isl_union_map_free(umap); + + data->set_sink = 0; + data->must = 1; + umap = isl_union_map_copy(data->access->must_source); + umap = isl_union_map_apply_range(isl_union_map_copy(prefix), umap); + if (isl_union_map_foreach_map(umap, &extract_sink_source, data) < 0) + r = isl_bool_error; + isl_union_map_free(umap); + + data->set_sink = 0; + data->must = 0; + umap = isl_union_map_copy(data->access->may_source); + umap = isl_union_map_apply_range(isl_union_map_copy(prefix), umap); + if (isl_union_map_foreach_map(umap, &extract_sink_source, data) < 0) + r = isl_bool_error; + isl_union_map_free(umap); + + isl_union_map_free(prefix); + + return r; +} + +/* isl_access_info_compute_flow callback for determining whether + * the shared nesting level and the ordering within that level + * for two scheduled accesses for use in compute_single_flow. + * + * The tokens passed to this function refer to the leaves + * in the schedule tree where the accesses take place. + * + * If n is the shared number of loops, then we need to return + * "2 * n + 1" if "first" precedes "second" inside the innermost + * shared loop and "2 * n" otherwise. + * + * The innermost shared ancestor may be the leaves themselves + * if the accesses take place in the same leaf. Otherwise, + * it is either a set node or a sequence node. Only in the case + * of a sequence node do we consider one access to precede the other. + */ +static int before_node(void *first, void *second) +{ + isl_schedule_node *node1 = first; + isl_schedule_node *node2 = second; + isl_schedule_node *shared; + int depth; + int before = 0; + + shared = isl_schedule_node_get_shared_ancestor(node1, node2); + if (!shared) + return -1; + + depth = isl_schedule_node_get_schedule_depth(shared); + if (isl_schedule_node_get_type(shared) == isl_schedule_node_sequence) { + int pos1, pos2; + + pos1 = isl_schedule_node_get_ancestor_child_position(node1, + shared); + pos2 = isl_schedule_node_get_ancestor_child_position(node2, + shared); + before = pos1 < pos2; + } + + isl_schedule_node_free(shared); + + return 2 * depth + before; +} + +/* Add the scheduled sources from "data" that access + * the same data space as "sink" to "access". + */ +static __isl_give isl_access_info *add_matching_sources( + __isl_take isl_access_info *access, struct isl_scheduled_access *sink, + struct isl_compute_flow_schedule_data *data) +{ + int i; + isl_space *space; + + space = isl_space_range(isl_map_get_space(sink->access)); + for (i = 0; i < data->n_source; ++i) { + struct isl_scheduled_access *source; + isl_space *source_space; + int eq; + + source = &data->source[i]; + source_space = isl_map_get_space(source->access); + source_space = isl_space_range(source_space); + eq = isl_space_is_equal(space, source_space); + isl_space_free(source_space); + + if (!eq) + continue; + if (eq < 0) + goto error; + + access = isl_access_info_add_source(access, + isl_map_copy(source->access), source->must, source->node); + } + + isl_space_free(space); + return access; +error: + isl_space_free(space); + isl_access_info_free(access); + return NULL; +} + +/* Given a scheduled sink access relation "sink", compute the corresponding + * dependences on the sources in "data" and add the computed dependences + * to "uf". + * + * The dependences computed by access_info_compute_flow_core are of the form + * + * [S -> I] -> [[S' -> I'] -> A] + * + * The schedule dimensions are projected out by first currying the range, + * resulting in + * + * [S -> I] -> [S' -> [I' -> A]] + * + * and then computing the factor range + * + * I -> [I' -> A] + */ +static __isl_give isl_union_flow *compute_single_flow( + __isl_take isl_union_flow *uf, struct isl_scheduled_access *sink, + struct isl_compute_flow_schedule_data *data) +{ + int i; + isl_access_info *access; + isl_flow *flow; + isl_map *map; + + if (!uf) + return NULL; + + access = isl_access_info_alloc(isl_map_copy(sink->access), sink->node, + &before_node, data->n_source); + access = add_matching_sources(access, sink, data); + + flow = access_info_compute_flow_core(access); + if (!flow) + return isl_union_flow_free(uf); + + map = isl_map_domain_factor_range(isl_flow_get_no_source(flow, 1)); + uf->must_no_source = isl_union_map_union(uf->must_no_source, + isl_union_map_from_map(map)); + map = isl_map_domain_factor_range(isl_flow_get_no_source(flow, 0)); + uf->may_no_source = isl_union_map_union(uf->may_no_source, + isl_union_map_from_map(map)); + + for (i = 0; i < flow->n_source; ++i) { + isl_union_map *dep; + + map = isl_map_range_curry(isl_map_copy(flow->dep[i].map)); + map = isl_map_factor_range(map); + dep = isl_union_map_from_map(map); + if (flow->dep[i].must) + uf->must_dep = isl_union_map_union(uf->must_dep, dep); + else + uf->may_dep = isl_union_map_union(uf->may_dep, dep); + } + + isl_flow_free(flow); + + return uf; +} + +/* Given a description of the "sink" accesses, the "source" accesses and + * a schedule, compute for each instance of a sink access + * and for each element accessed by that instance, + * the possible or definite source accesses that last accessed the + * element accessed by the sink access before this sink access + * in the sense that there is no intermediate definite source access. + * Only consider dependences between statement instances that belong + * to the domain of the schedule. + * + * The must_no_source and may_no_source elements of the result + * are subsets of access->sink. The elements must_dep and may_dep + * map domain elements of access->{may,must)_source to + * domain elements of access->sink. + * + * This function is used when a schedule tree representation + * is available. + * + * We extract the individual scheduled source and sink access relations + * (taking into account the domain of the schedule) and + * then compute dependences for each scheduled sink individually. + */ +static __isl_give isl_union_flow *compute_flow_schedule( + __isl_take isl_union_access_info *access) +{ + struct isl_compute_flow_schedule_data data = { access }; + int i, n; + isl_ctx *ctx; + isl_union_flow *flow; + + ctx = isl_union_access_info_get_ctx(access); + + data.n_sink = 0; + data.n_source = 0; + if (isl_schedule_foreach_schedule_node_top_down(access->schedule, + &count_sink_source, &data) < 0) + goto error; + + n = data.n_sink + data.n_source; + data.sink = isl_calloc_array(ctx, struct isl_scheduled_access, n); + if (n && !data.sink) + goto error; + data.source = data.sink + data.n_sink; + + data.n_sink = 0; + data.n_source = 0; + if (isl_schedule_foreach_schedule_node_top_down(access->schedule, + &collect_sink_source, &data) < 0) + goto error; + + flow = isl_union_flow_alloc(isl_union_map_get_space(access->sink)); + + isl_compute_flow_schedule_data_align_params(&data); + + for (i = 0; i < data.n_sink; ++i) + flow = compute_single_flow(flow, &data.sink[i], &data); + + isl_compute_flow_schedule_data_clear(&data); + + isl_union_access_info_free(access); + return flow; +error: + isl_union_access_info_free(access); + isl_compute_flow_schedule_data_clear(&data); + return NULL; +} + +/* Given a description of the "sink" accesses, the "source" accesses and + * a schedule, compute for each instance of a sink access + * and for each element accessed by that instance, + * the possible or definite source accesses that last accessed the + * element accessed by the sink access before this sink access + * in the sense that there is no intermediate definite source access. + * + * The must_no_source and may_no_source elements of the result + * are subsets of access->sink. The elements must_dep and may_dep + * map domain elements of access->{may,must)_source to + * domain elements of access->sink. + * + * We check whether the schedule is available as a schedule tree + * or a schedule map and call the correpsonding function to perform + * the analysis. + */ +__isl_give isl_union_flow *isl_union_access_info_compute_flow( + __isl_take isl_union_access_info *access) +{ + access = isl_union_access_info_normalize(access); + if (!access) + return NULL; + if (access->schedule) + return compute_flow_schedule(access); + else + return compute_flow_union_map(access); +} + +/* Print the information contained in "flow" to "p". + * The information is printed as a YAML document. + */ +__isl_give isl_printer *isl_printer_print_union_flow( + __isl_take isl_printer *p, __isl_keep isl_union_flow *flow) +{ + isl_union_map *umap; + + if (!flow) + return isl_printer_free(p); + + p = isl_printer_yaml_start_mapping(p); + p = print_union_map_field(p, "must_dependence", flow->must_dep); + umap = isl_union_flow_get_may_dependence(flow); + p = print_union_map_field(p, "may_dependence", umap); + isl_union_map_free(umap); + p = print_union_map_field(p, "must_no_source", flow->must_no_source); + umap = isl_union_flow_get_may_no_source(flow); + p = print_union_map_field(p, "may_no_source", umap); + isl_union_map_free(umap); + p = isl_printer_yaml_end_mapping(p); + + return p; +} + +/* Return a string representation of the information in "flow". + * The information is printed in flow format. + */ +__isl_give char *isl_union_flow_to_str(__isl_keep isl_union_flow *flow) +{ + isl_printer *p; + char *s; + + if (!flow) + return NULL; + + p = isl_printer_to_str(isl_union_flow_get_ctx(flow)); + p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW); + p = isl_printer_print_union_flow(p, flow); + s = isl_printer_get_str(p); + isl_printer_free(p); + + return s; +} + +/* Given a collection of "sink" and "source" accesses, + * compute for each iteration of a sink access + * and for each element accessed by that iteration, + * the source access in the list that last accessed the + * element accessed by the sink access before this sink access. + * Each access is given as a map from the loop iterators + * to the array indices. + * The result is a relations between source and sink + * iterations and a subset of the domain of the sink accesses, + * corresponding to those iterations that access an element + * not previously accessed. + * + * We collect the inputs in an isl_union_access_info object, + * call isl_union_access_info_compute_flow and extract + * the outputs from the result. + */ +int isl_union_map_compute_flow(__isl_take isl_union_map *sink, + __isl_take isl_union_map *must_source, + __isl_take isl_union_map *may_source, + __isl_take isl_union_map *schedule, + __isl_give isl_union_map **must_dep, __isl_give isl_union_map **may_dep, + __isl_give isl_union_map **must_no_source, + __isl_give isl_union_map **may_no_source) +{ + isl_union_access_info *access; + isl_union_flow *flow; + + access = isl_union_access_info_from_sink(sink); + access = isl_union_access_info_set_must_source(access, must_source); + access = isl_union_access_info_set_may_source(access, may_source); + access = isl_union_access_info_set_schedule_map(access, schedule); + flow = isl_union_access_info_compute_flow(access); + + if (must_dep) + *must_dep = isl_union_flow_get_must_dependence(flow); + if (may_dep) + *may_dep = isl_union_flow_get_non_must_dependence(flow); + if (must_no_source) + *must_no_source = isl_union_flow_get_must_no_source(flow); + if (may_no_source) + *may_no_source = isl_union_flow_get_non_must_no_source(flow); + + isl_union_flow_free(flow); + + if ((must_dep && !*must_dep) || (may_dep && !*may_dep) || + (must_no_source && !*must_no_source) || + (may_no_source && !*may_no_source)) + goto error; + + return 0; +error: + if (must_dep) + *must_dep = isl_union_map_free(*must_dep); + if (may_dep) + *may_dep = isl_union_map_free(*may_dep); + if (must_no_source) + *must_no_source = isl_union_map_free(*must_no_source); + if (may_no_source) + *may_no_source = isl_union_map_free(*may_no_source); + return -1; +} Index: lib/Analysis/isl/isl_fold.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_fold.c @@ -0,0 +1,1777 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#define ISL_DIM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum isl_fold isl_fold_type_negate(enum isl_fold type) +{ + switch (type) { + case isl_fold_min: + return isl_fold_max; + case isl_fold_max: + return isl_fold_min; + case isl_fold_list: + return isl_fold_list; + } + + isl_die(NULL, isl_error_internal, "unhandled isl_fold type", abort()); +} + +static __isl_give isl_qpolynomial_fold *qpolynomial_fold_alloc( + enum isl_fold type, __isl_take isl_space *dim, int n) +{ + isl_qpolynomial_fold *fold; + + if (!dim) + goto error; + + isl_assert(dim->ctx, n >= 0, goto error); + fold = isl_calloc(dim->ctx, struct isl_qpolynomial_fold, + sizeof(struct isl_qpolynomial_fold) + + (n - 1) * sizeof(struct isl_qpolynomial *)); + if (!fold) + goto error; + + fold->ref = 1; + fold->size = n; + fold->n = 0; + fold->type = type; + fold->dim = dim; + + return fold; +error: + isl_space_free(dim); + return NULL; +} + +isl_ctx *isl_qpolynomial_fold_get_ctx(__isl_keep isl_qpolynomial_fold *fold) +{ + return fold ? fold->dim->ctx : NULL; +} + +__isl_give isl_space *isl_qpolynomial_fold_get_domain_space( + __isl_keep isl_qpolynomial_fold *fold) +{ + return fold ? isl_space_copy(fold->dim) : NULL; +} + +__isl_give isl_space *isl_qpolynomial_fold_get_space( + __isl_keep isl_qpolynomial_fold *fold) +{ + isl_space *space; + if (!fold) + return NULL; + space = isl_space_copy(fold->dim); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, 1); + return space; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_domain_space( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *dim) +{ + int i; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold || !dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_reset_domain_space(fold->qp[i], + isl_space_copy(dim)); + if (!fold->qp[i]) + goto error; + } + + isl_space_free(fold->dim); + fold->dim = dim; + + return fold; +error: + isl_qpolynomial_fold_free(fold); + isl_space_free(dim); + return NULL; +} + +/* Reset the space of "fold". This function is called from isl_pw_templ.c + * and doesn't know if the space of an element object is represented + * directly or through its domain. It therefore passes along both. + */ +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_space_and_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space, + __isl_take isl_space *domain) +{ + isl_space_free(space); + return isl_qpolynomial_fold_reset_domain_space(fold, domain); +} + +int isl_qpolynomial_fold_involves_dims(__isl_keep isl_qpolynomial_fold *fold, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!fold) + return -1; + if (fold->n == 0 || n == 0) + return 0; + + for (i = 0; i < fold->n; ++i) { + int involves = isl_qpolynomial_involves_dims(fold->qp[i], + type, first, n); + if (involves < 0 || involves) + return involves; + } + return 0; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_set_dim_name( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type type, unsigned pos, const char *s) +{ + int i; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + fold->dim = isl_space_set_dim_name(fold->dim, type, pos, s); + if (!fold->dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_set_dim_name(fold->qp[i], + type, pos, s); + if (!fold->qp[i]) + goto error; + } + + return fold; +error: + isl_qpolynomial_fold_free(fold); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_drop_dims( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + enum isl_dim_type set_type; + + if (!fold) + return NULL; + if (n == 0) + return fold; + + set_type = type == isl_dim_in ? isl_dim_set : type; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + fold->dim = isl_space_drop_dims(fold->dim, set_type, first, n); + if (!fold->dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_drop_dims(fold->qp[i], + type, first, n); + if (!fold->qp[i]) + goto error; + } + + return fold; +error: + isl_qpolynomial_fold_free(fold); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_insert_dims( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!fold) + return NULL; + if (n == 0 && !isl_space_is_named_or_nested(fold->dim, type)) + return fold; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + fold->dim = isl_space_insert_dims(fold->dim, type, first, n); + if (!fold->dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_insert_dims(fold->qp[i], + type, first, n); + if (!fold->qp[i]) + goto error; + } + + return fold; +error: + isl_qpolynomial_fold_free(fold); + return NULL; +} + +/* Determine the sign of the constant quasipolynomial "qp". + * + * Return + * -1 if qp <= 0 + * 1 if qp >= 0 + * 0 if unknown + * + * For qp == 0, we can return either -1 or 1. In practice, we return 1. + * For qp == NaN, the sign is undefined, so we return 0. + */ +static int isl_qpolynomial_cst_sign(__isl_keep isl_qpolynomial *qp) +{ + struct isl_upoly_cst *cst; + + if (isl_qpolynomial_is_nan(qp)) + return 0; + + cst = isl_upoly_as_cst(qp->upoly); + if (!cst) + return 0; + + return isl_int_sgn(cst->n) < 0 ? -1 : 1; +} + +static int isl_qpolynomial_aff_sign(__isl_keep isl_set *set, + __isl_keep isl_qpolynomial *qp) +{ + enum isl_lp_result res; + isl_vec *aff; + isl_int opt; + int sgn = 0; + + aff = isl_qpolynomial_extract_affine(qp); + if (!aff) + return 0; + + isl_int_init(opt); + + res = isl_set_solve_lp(set, 0, aff->el + 1, aff->el[0], + &opt, NULL, NULL); + if (res == isl_lp_error) + goto done; + if (res == isl_lp_empty || + (res == isl_lp_ok && !isl_int_is_neg(opt))) { + sgn = 1; + goto done; + } + + res = isl_set_solve_lp(set, 1, aff->el + 1, aff->el[0], + &opt, NULL, NULL); + if (res == isl_lp_ok && !isl_int_is_pos(opt)) + sgn = -1; + +done: + isl_int_clear(opt); + isl_vec_free(aff); + return sgn; +} + +/* Determine, if possible, the sign of the quasipolynomial "qp" on + * the domain "set". + * + * If qp is a constant, then the problem is trivial. + * If qp is linear, then we check if the minimum of the corresponding + * affine constraint is non-negative or if the maximum is non-positive. + * + * Otherwise, we check if the outermost variable "v" has a lower bound "l" + * in "set". If so, we write qp(v,v') as + * + * q(v,v') * (v - l) + r(v') + * + * if q(v,v') and r(v') have the same known sign, then the original + * quasipolynomial has the same sign as well. + * + * Return + * -1 if qp <= 0 + * 1 if qp >= 0 + * 0 if unknown + */ +static int isl_qpolynomial_sign(__isl_keep isl_set *set, + __isl_keep isl_qpolynomial *qp) +{ + int d; + int i; + int is; + struct isl_upoly_rec *rec; + isl_vec *v; + isl_int l; + enum isl_lp_result res; + int sgn = 0; + + is = isl_qpolynomial_is_cst(qp, NULL, NULL); + if (is < 0) + return 0; + if (is) + return isl_qpolynomial_cst_sign(qp); + + is = isl_qpolynomial_is_affine(qp); + if (is < 0) + return 0; + if (is) + return isl_qpolynomial_aff_sign(set, qp); + + if (qp->div->n_row > 0) + return 0; + + rec = isl_upoly_as_rec(qp->upoly); + if (!rec) + return 0; + + d = isl_space_dim(qp->dim, isl_dim_all); + v = isl_vec_alloc(set->ctx, 2 + d); + if (!v) + return 0; + + isl_seq_clr(v->el + 1, 1 + d); + isl_int_set_si(v->el[0], 1); + isl_int_set_si(v->el[2 + qp->upoly->var], 1); + + isl_int_init(l); + + res = isl_set_solve_lp(set, 0, v->el + 1, v->el[0], &l, NULL, NULL); + if (res == isl_lp_ok) { + isl_qpolynomial *min; + isl_qpolynomial *base; + isl_qpolynomial *r, *q; + isl_qpolynomial *t; + + min = isl_qpolynomial_cst_on_domain(isl_space_copy(qp->dim), l); + base = isl_qpolynomial_var_pow_on_domain(isl_space_copy(qp->dim), + qp->upoly->var, 1); + + r = isl_qpolynomial_alloc(isl_space_copy(qp->dim), 0, + isl_upoly_copy(rec->p[rec->n - 1])); + q = isl_qpolynomial_copy(r); + + for (i = rec->n - 2; i >= 0; --i) { + r = isl_qpolynomial_mul(r, isl_qpolynomial_copy(min)); + t = isl_qpolynomial_alloc(isl_space_copy(qp->dim), 0, + isl_upoly_copy(rec->p[i])); + r = isl_qpolynomial_add(r, t); + if (i == 0) + break; + q = isl_qpolynomial_mul(q, isl_qpolynomial_copy(base)); + q = isl_qpolynomial_add(q, isl_qpolynomial_copy(r)); + } + + if (isl_qpolynomial_is_zero(q)) + sgn = isl_qpolynomial_sign(set, r); + else if (isl_qpolynomial_is_zero(r)) + sgn = isl_qpolynomial_sign(set, q); + else { + int sgn_q, sgn_r; + sgn_r = isl_qpolynomial_sign(set, r); + sgn_q = isl_qpolynomial_sign(set, q); + if (sgn_r == sgn_q) + sgn = sgn_r; + } + + isl_qpolynomial_free(min); + isl_qpolynomial_free(base); + isl_qpolynomial_free(q); + isl_qpolynomial_free(r); + } + + isl_int_clear(l); + + isl_vec_free(v); + + return sgn; +} + +/* Combine "fold1" and "fold2" into a single reduction, eliminating + * those elements of one reduction that are already covered by the other + * reduction on "set". + * + * If "fold1" or "fold2" is an empty reduction, then return + * the other reduction. + * If "fold1" or "fold2" is a NaN, then return this NaN. + */ +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold_on_domain( + __isl_keep isl_set *set, + __isl_take isl_qpolynomial_fold *fold1, + __isl_take isl_qpolynomial_fold *fold2) +{ + int i, j; + int n1; + struct isl_qpolynomial_fold *res = NULL; + int better; + + if (!fold1 || !fold2) + goto error; + + isl_assert(fold1->dim->ctx, fold1->type == fold2->type, goto error); + isl_assert(fold1->dim->ctx, isl_space_is_equal(fold1->dim, fold2->dim), + goto error); + + better = fold1->type == isl_fold_max ? -1 : 1; + + if (isl_qpolynomial_fold_is_empty(fold1) || + isl_qpolynomial_fold_is_nan(fold2)) { + isl_qpolynomial_fold_free(fold1); + return fold2; + } + + if (isl_qpolynomial_fold_is_empty(fold2) || + isl_qpolynomial_fold_is_nan(fold1)) { + isl_qpolynomial_fold_free(fold2); + return fold1; + } + + res = qpolynomial_fold_alloc(fold1->type, isl_space_copy(fold1->dim), + fold1->n + fold2->n); + if (!res) + goto error; + + for (i = 0; i < fold1->n; ++i) { + res->qp[res->n] = isl_qpolynomial_copy(fold1->qp[i]); + if (!res->qp[res->n]) + goto error; + res->n++; + } + n1 = res->n; + + for (i = 0; i < fold2->n; ++i) { + for (j = n1 - 1; j >= 0; --j) { + isl_qpolynomial *d; + int sgn, equal; + equal = isl_qpolynomial_plain_is_equal(res->qp[j], + fold2->qp[i]); + if (equal < 0) + goto error; + if (equal) + break; + d = isl_qpolynomial_sub( + isl_qpolynomial_copy(res->qp[j]), + isl_qpolynomial_copy(fold2->qp[i])); + sgn = isl_qpolynomial_sign(set, d); + isl_qpolynomial_free(d); + if (sgn == 0) + continue; + if (sgn != better) + break; + isl_qpolynomial_free(res->qp[j]); + if (j != n1 - 1) + res->qp[j] = res->qp[n1 - 1]; + n1--; + if (n1 != res->n - 1) + res->qp[n1] = res->qp[res->n - 1]; + res->n--; + } + if (j >= 0) + continue; + res->qp[res->n] = isl_qpolynomial_copy(fold2->qp[i]); + if (!res->qp[res->n]) + goto error; + res->n++; + } + + isl_qpolynomial_fold_free(fold1); + isl_qpolynomial_fold_free(fold2); + + return res; +error: + isl_qpolynomial_fold_free(res); + isl_qpolynomial_fold_free(fold1); + isl_qpolynomial_fold_free(fold2); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_add_qpolynomial( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_qpolynomial *qp) +{ + int i; + + if (!fold || !qp) + goto error; + + if (isl_qpolynomial_is_zero(qp)) { + isl_qpolynomial_free(qp); + return fold; + } + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_add(fold->qp[i], + isl_qpolynomial_copy(qp)); + if (!fold->qp[i]) + goto error; + } + + isl_qpolynomial_free(qp); + return fold; +error: + isl_qpolynomial_fold_free(fold); + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_add_on_domain( + __isl_keep isl_set *dom, + __isl_take isl_qpolynomial_fold *fold1, + __isl_take isl_qpolynomial_fold *fold2) +{ + int i; + isl_qpolynomial_fold *res = NULL; + + if (!fold1 || !fold2) + goto error; + + if (isl_qpolynomial_fold_is_empty(fold1)) { + isl_qpolynomial_fold_free(fold1); + return fold2; + } + + if (isl_qpolynomial_fold_is_empty(fold2)) { + isl_qpolynomial_fold_free(fold2); + return fold1; + } + + if (fold1->n == 1 && fold2->n != 1) + return isl_qpolynomial_fold_add_on_domain(dom, fold2, fold1); + + if (fold2->n == 1) { + res = isl_qpolynomial_fold_add_qpolynomial(fold1, + isl_qpolynomial_copy(fold2->qp[0])); + isl_qpolynomial_fold_free(fold2); + return res; + } + + res = isl_qpolynomial_fold_add_qpolynomial( + isl_qpolynomial_fold_copy(fold1), + isl_qpolynomial_copy(fold2->qp[0])); + + for (i = 1; i < fold2->n; ++i) { + isl_qpolynomial_fold *res_i; + res_i = isl_qpolynomial_fold_add_qpolynomial( + isl_qpolynomial_fold_copy(fold1), + isl_qpolynomial_copy(fold2->qp[i])); + res = isl_qpolynomial_fold_fold_on_domain(dom, res, res_i); + } + + isl_qpolynomial_fold_free(fold1); + isl_qpolynomial_fold_free(fold2); + return res; +error: + isl_qpolynomial_fold_free(res); + isl_qpolynomial_fold_free(fold1); + isl_qpolynomial_fold_free(fold2); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute_equalities( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_basic_set *eq) +{ + int i; + + if (!fold || !eq) + goto error; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_substitute_equalities(fold->qp[i], + isl_basic_set_copy(eq)); + if (!fold->qp[i]) + goto error; + } + + isl_basic_set_free(eq); + return fold; +error: + isl_basic_set_free(eq); + isl_qpolynomial_fold_free(fold); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context) +{ + int i; + + if (!fold || !context) + goto error; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_gist(fold->qp[i], + isl_set_copy(context)); + if (!fold->qp[i]) + goto error; + } + + isl_set_free(context); + return fold; +error: + isl_set_free(context); + isl_qpolynomial_fold_free(fold); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist_params( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context) +{ + isl_space *space = isl_qpolynomial_fold_get_domain_space(fold); + isl_set *dom_context = isl_set_universe(space); + dom_context = isl_set_intersect_params(dom_context, context); + return isl_qpolynomial_fold_gist(fold, dom_context); +} + +#define HAS_TYPE + +#undef PW +#define PW isl_pw_qpolynomial_fold +#undef EL +#define EL isl_qpolynomial_fold +#undef EL_IS_ZERO +#define EL_IS_ZERO is_empty +#undef ZERO +#define ZERO zero +#undef IS_ZERO +#define IS_ZERO is_zero +#undef FIELD +#define FIELD fold +#undef DEFAULT_IS_ZERO +#define DEFAULT_IS_ZERO 1 + +#define NO_NEG +#define NO_SUB +#define NO_PULLBACK + +#include + +#undef UNION +#define UNION isl_union_pw_qpolynomial_fold +#undef PART +#define PART isl_pw_qpolynomial_fold +#undef PARTS +#define PARTS pw_qpolynomial_fold + +#define NO_SUB + +#include +#include + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_empty(enum isl_fold type, + __isl_take isl_space *dim) +{ + return qpolynomial_fold_alloc(type, dim, 0); +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_alloc( + enum isl_fold type, __isl_take isl_qpolynomial *qp) +{ + isl_qpolynomial_fold *fold; + + if (!qp) + return NULL; + + fold = qpolynomial_fold_alloc(type, isl_space_copy(qp->dim), 1); + if (!fold) + goto error; + + fold->qp[0] = qp; + fold->n++; + + return fold; +error: + isl_qpolynomial_fold_free(fold); + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_copy( + __isl_keep isl_qpolynomial_fold *fold) +{ + if (!fold) + return NULL; + + fold->ref++; + return fold; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_dup( + __isl_keep isl_qpolynomial_fold *fold) +{ + int i; + isl_qpolynomial_fold *dup; + + if (!fold) + return NULL; + dup = qpolynomial_fold_alloc(fold->type, + isl_space_copy(fold->dim), fold->n); + if (!dup) + return NULL; + + dup->n = fold->n; + for (i = 0; i < fold->n; ++i) { + dup->qp[i] = isl_qpolynomial_copy(fold->qp[i]); + if (!dup->qp[i]) + goto error; + } + + return dup; +error: + isl_qpolynomial_fold_free(dup); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_cow( + __isl_take isl_qpolynomial_fold *fold) +{ + if (!fold) + return NULL; + + if (fold->ref == 1) + return fold; + fold->ref--; + return isl_qpolynomial_fold_dup(fold); +} + +void isl_qpolynomial_fold_free(__isl_take isl_qpolynomial_fold *fold) +{ + int i; + + if (!fold) + return; + if (--fold->ref > 0) + return; + + for (i = 0; i < fold->n; ++i) + isl_qpolynomial_free(fold->qp[i]); + isl_space_free(fold->dim); + free(fold); +} + +int isl_qpolynomial_fold_is_empty(__isl_keep isl_qpolynomial_fold *fold) +{ + if (!fold) + return -1; + + return fold->n == 0; +} + +/* Does "fold" represent max(NaN) or min(NaN)? + */ +isl_bool isl_qpolynomial_fold_is_nan(__isl_keep isl_qpolynomial_fold *fold) +{ + if (!fold) + return isl_bool_error; + if (fold->n != 1) + return isl_bool_false; + return isl_qpolynomial_is_nan(fold->qp[0]); +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold( + __isl_take isl_qpolynomial_fold *fold1, + __isl_take isl_qpolynomial_fold *fold2) +{ + int i; + struct isl_qpolynomial_fold *res = NULL; + + if (!fold1 || !fold2) + goto error; + + isl_assert(fold1->dim->ctx, fold1->type == fold2->type, goto error); + isl_assert(fold1->dim->ctx, isl_space_is_equal(fold1->dim, fold2->dim), + goto error); + + if (isl_qpolynomial_fold_is_empty(fold1)) { + isl_qpolynomial_fold_free(fold1); + return fold2; + } + + if (isl_qpolynomial_fold_is_empty(fold2)) { + isl_qpolynomial_fold_free(fold2); + return fold1; + } + + res = qpolynomial_fold_alloc(fold1->type, isl_space_copy(fold1->dim), + fold1->n + fold2->n); + if (!res) + goto error; + + for (i = 0; i < fold1->n; ++i) { + res->qp[res->n] = isl_qpolynomial_copy(fold1->qp[i]); + if (!res->qp[res->n]) + goto error; + res->n++; + } + + for (i = 0; i < fold2->n; ++i) { + res->qp[res->n] = isl_qpolynomial_copy(fold2->qp[i]); + if (!res->qp[res->n]) + goto error; + res->n++; + } + + isl_qpolynomial_fold_free(fold1); + isl_qpolynomial_fold_free(fold2); + + return res; +error: + isl_qpolynomial_fold_free(res); + isl_qpolynomial_fold_free(fold1); + isl_qpolynomial_fold_free(fold2); + return NULL; +} + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fold( + __isl_take isl_pw_qpolynomial_fold *pw1, + __isl_take isl_pw_qpolynomial_fold *pw2) +{ + int i, j, n; + struct isl_pw_qpolynomial_fold *res; + isl_set *set; + + if (!pw1 || !pw2) + goto error; + + isl_assert(pw1->dim->ctx, isl_space_is_equal(pw1->dim, pw2->dim), goto error); + + if (isl_pw_qpolynomial_fold_is_zero(pw1)) { + isl_pw_qpolynomial_fold_free(pw1); + return pw2; + } + + if (isl_pw_qpolynomial_fold_is_zero(pw2)) { + isl_pw_qpolynomial_fold_free(pw2); + return pw1; + } + + if (pw1->type != pw2->type) + isl_die(pw1->dim->ctx, isl_error_invalid, + "fold types don't match", goto error); + + n = (pw1->n + 1) * (pw2->n + 1); + res = isl_pw_qpolynomial_fold_alloc_size(isl_space_copy(pw1->dim), + pw1->type, n); + + for (i = 0; i < pw1->n; ++i) { + set = isl_set_copy(pw1->p[i].set); + for (j = 0; j < pw2->n; ++j) { + struct isl_set *common; + isl_qpolynomial_fold *sum; + set = isl_set_subtract(set, + isl_set_copy(pw2->p[j].set)); + common = isl_set_intersect(isl_set_copy(pw1->p[i].set), + isl_set_copy(pw2->p[j].set)); + if (isl_set_plain_is_empty(common)) { + isl_set_free(common); + continue; + } + + sum = isl_qpolynomial_fold_fold_on_domain(common, + isl_qpolynomial_fold_copy(pw1->p[i].fold), + isl_qpolynomial_fold_copy(pw2->p[j].fold)); + + res = isl_pw_qpolynomial_fold_add_piece(res, common, sum); + } + res = isl_pw_qpolynomial_fold_add_piece(res, set, + isl_qpolynomial_fold_copy(pw1->p[i].fold)); + } + + for (j = 0; j < pw2->n; ++j) { + set = isl_set_copy(pw2->p[j].set); + for (i = 0; i < pw1->n; ++i) + set = isl_set_subtract(set, isl_set_copy(pw1->p[i].set)); + res = isl_pw_qpolynomial_fold_add_piece(res, set, + isl_qpolynomial_fold_copy(pw2->p[j].fold)); + } + + isl_pw_qpolynomial_fold_free(pw1); + isl_pw_qpolynomial_fold_free(pw2); + + return res; +error: + isl_pw_qpolynomial_fold_free(pw1); + isl_pw_qpolynomial_fold_free(pw2); + return NULL; +} + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold( + __isl_take isl_union_pw_qpolynomial_fold *u, + __isl_take isl_pw_qpolynomial_fold *part) +{ + struct isl_hash_table_entry *entry; + + u = isl_union_pw_qpolynomial_fold_cow(u); + + if (!part || !u) + goto error; + + isl_assert(u->space->ctx, + isl_space_match(part->dim, isl_dim_param, u->space, isl_dim_param), + goto error); + + entry = isl_union_pw_qpolynomial_fold_find_part_entry(u, part->dim, 1); + if (!entry) + goto error; + + if (!entry->data) + entry->data = part; + else { + entry->data = isl_pw_qpolynomial_fold_fold(entry->data, + isl_pw_qpolynomial_fold_copy(part)); + if (!entry->data) + goto error; + isl_pw_qpolynomial_fold_free(part); + } + + return u; +error: + isl_pw_qpolynomial_fold_free(part); + isl_union_pw_qpolynomial_fold_free(u); + return NULL; +} + +static isl_stat fold_part(__isl_take isl_pw_qpolynomial_fold *part, void *user) +{ + isl_union_pw_qpolynomial_fold **u; + u = (isl_union_pw_qpolynomial_fold **)user; + + *u = isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold(*u, part); + + return isl_stat_ok; +} + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold( + __isl_take isl_union_pw_qpolynomial_fold *u1, + __isl_take isl_union_pw_qpolynomial_fold *u2) +{ + u1 = isl_union_pw_qpolynomial_fold_cow(u1); + + if (!u1 || !u2) + goto error; + + if (isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold(u2, + &fold_part, &u1) < 0) + goto error; + + isl_union_pw_qpolynomial_fold_free(u2); + + return u1; +error: + isl_union_pw_qpolynomial_fold_free(u1); + isl_union_pw_qpolynomial_fold_free(u2); + return NULL; +} + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_from_pw_qpolynomial( + enum isl_fold type, __isl_take isl_pw_qpolynomial *pwqp) +{ + int i; + isl_pw_qpolynomial_fold *pwf; + + if (!pwqp) + return NULL; + + pwf = isl_pw_qpolynomial_fold_alloc_size(isl_space_copy(pwqp->dim), + type, pwqp->n); + + for (i = 0; i < pwqp->n; ++i) + pwf = isl_pw_qpolynomial_fold_add_piece(pwf, + isl_set_copy(pwqp->p[i].set), + isl_qpolynomial_fold_alloc(type, + isl_qpolynomial_copy(pwqp->p[i].qp))); + + isl_pw_qpolynomial_free(pwqp); + + return pwf; +} + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add( + __isl_take isl_pw_qpolynomial_fold *pwf1, + __isl_take isl_pw_qpolynomial_fold *pwf2) +{ + return isl_pw_qpolynomial_fold_union_add_(pwf1, pwf2); +} + +/* Compare two quasi-polynomial reductions. + * + * Return -1 if "fold1" is "smaller" than "fold2", 1 if "fold1" is "greater" + * than "fold2" and 0 if they are equal. + */ +int isl_qpolynomial_fold_plain_cmp(__isl_keep isl_qpolynomial_fold *fold1, + __isl_keep isl_qpolynomial_fold *fold2) +{ + int i; + + if (fold1 == fold2) + return 0; + if (!fold1) + return -1; + if (!fold2) + return 1; + + if (fold1->n != fold2->n) + return fold1->n - fold2->n; + + for (i = 0; i < fold1->n; ++i) { + int cmp; + + cmp = isl_qpolynomial_plain_cmp(fold1->qp[i], fold2->qp[i]); + if (cmp != 0) + return cmp; + } + + return 0; +} + +int isl_qpolynomial_fold_plain_is_equal(__isl_keep isl_qpolynomial_fold *fold1, + __isl_keep isl_qpolynomial_fold *fold2) +{ + int i; + + if (!fold1 || !fold2) + return -1; + + if (fold1->n != fold2->n) + return 0; + + /* We probably want to sort the qps first... */ + for (i = 0; i < fold1->n; ++i) { + int eq = isl_qpolynomial_plain_is_equal(fold1->qp[i], fold2->qp[i]); + if (eq < 0 || !eq) + return eq; + } + + return 1; +} + +__isl_give isl_val *isl_qpolynomial_fold_eval( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_point *pnt) +{ + isl_ctx *ctx; + isl_val *v; + + if (!fold || !pnt) + goto error; + ctx = isl_point_get_ctx(pnt); + isl_assert(pnt->dim->ctx, isl_space_is_equal(pnt->dim, fold->dim), goto error); + isl_assert(pnt->dim->ctx, + fold->type == isl_fold_max || fold->type == isl_fold_min, + goto error); + + if (fold->n == 0) + v = isl_val_zero(ctx); + else { + int i; + v = isl_qpolynomial_eval(isl_qpolynomial_copy(fold->qp[0]), + isl_point_copy(pnt)); + for (i = 1; i < fold->n; ++i) { + isl_val *v_i; + v_i = isl_qpolynomial_eval( + isl_qpolynomial_copy(fold->qp[i]), + isl_point_copy(pnt)); + if (fold->type == isl_fold_max) + v = isl_val_max(v, v_i); + else + v = isl_val_min(v, v_i); + } + } + isl_qpolynomial_fold_free(fold); + isl_point_free(pnt); + + return v; +error: + isl_qpolynomial_fold_free(fold); + isl_point_free(pnt); + return NULL; +} + +size_t isl_pw_qpolynomial_fold_size(__isl_keep isl_pw_qpolynomial_fold *pwf) +{ + int i; + size_t n = 0; + + for (i = 0; i < pwf->n; ++i) + n += pwf->p[i].fold->n; + + return n; +} + +__isl_give isl_val *isl_qpolynomial_fold_opt_on_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *set, int max) +{ + int i; + isl_val *opt; + + if (!set || !fold) + goto error; + + if (fold->n == 0) { + opt = isl_val_zero(isl_set_get_ctx(set)); + isl_set_free(set); + isl_qpolynomial_fold_free(fold); + return opt; + } + + opt = isl_qpolynomial_opt_on_domain(isl_qpolynomial_copy(fold->qp[0]), + isl_set_copy(set), max); + for (i = 1; i < fold->n; ++i) { + isl_val *opt_i; + opt_i = isl_qpolynomial_opt_on_domain( + isl_qpolynomial_copy(fold->qp[i]), + isl_set_copy(set), max); + if (max) + opt = isl_val_max(opt, opt_i); + else + opt = isl_val_min(opt, opt_i); + } + + isl_set_free(set); + isl_qpolynomial_fold_free(fold); + + return opt; +error: + isl_set_free(set); + isl_qpolynomial_fold_free(fold); + return NULL; +} + +/* Check whether for each quasi-polynomial in "fold2" there is + * a quasi-polynomial in "fold1" that dominates it on "set". + */ +static int qpolynomial_fold_covers_on_domain(__isl_keep isl_set *set, + __isl_keep isl_qpolynomial_fold *fold1, + __isl_keep isl_qpolynomial_fold *fold2) +{ + int i, j; + int covers; + + if (!set || !fold1 || !fold2) + return -1; + + covers = fold1->type == isl_fold_max ? 1 : -1; + + for (i = 0; i < fold2->n; ++i) { + for (j = 0; j < fold1->n; ++j) { + isl_qpolynomial *d; + int sgn; + + d = isl_qpolynomial_sub( + isl_qpolynomial_copy(fold1->qp[j]), + isl_qpolynomial_copy(fold2->qp[i])); + sgn = isl_qpolynomial_sign(set, d); + isl_qpolynomial_free(d); + if (sgn == covers) + break; + } + if (j >= fold1->n) + return 0; + } + + return 1; +} + +/* Check whether "pwf1" dominated "pwf2", i.e., the domain of "pwf1" contains + * that of "pwf2" and on each cell, the corresponding fold from pwf1 dominates + * that of pwf2. + */ +int isl_pw_qpolynomial_fold_covers(__isl_keep isl_pw_qpolynomial_fold *pwf1, + __isl_keep isl_pw_qpolynomial_fold *pwf2) +{ + int i, j; + isl_set *dom1, *dom2; + int is_subset; + + if (!pwf1 || !pwf2) + return -1; + + if (pwf2->n == 0) + return 1; + if (pwf1->n == 0) + return 0; + + dom1 = isl_pw_qpolynomial_fold_domain(isl_pw_qpolynomial_fold_copy(pwf1)); + dom2 = isl_pw_qpolynomial_fold_domain(isl_pw_qpolynomial_fold_copy(pwf2)); + is_subset = isl_set_is_subset(dom2, dom1); + isl_set_free(dom1); + isl_set_free(dom2); + + if (is_subset < 0 || !is_subset) + return is_subset; + + for (i = 0; i < pwf2->n; ++i) { + for (j = 0; j < pwf1->n; ++j) { + int is_empty; + isl_set *common; + int covers; + + common = isl_set_intersect(isl_set_copy(pwf1->p[j].set), + isl_set_copy(pwf2->p[i].set)); + is_empty = isl_set_is_empty(common); + if (is_empty < 0 || is_empty) { + isl_set_free(common); + if (is_empty < 0) + return -1; + continue; + } + covers = qpolynomial_fold_covers_on_domain(common, + pwf1->p[j].fold, pwf2->p[i].fold); + isl_set_free(common); + if (covers < 0 || !covers) + return covers; + } + } + + return 1; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_morph_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_morph *morph) +{ + int i; + isl_ctx *ctx; + + if (!fold || !morph) + goto error; + + ctx = fold->dim->ctx; + isl_assert(ctx, isl_space_is_equal(fold->dim, morph->dom->dim), goto error); + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + goto error; + + isl_space_free(fold->dim); + fold->dim = isl_space_copy(morph->ran->dim); + if (!fold->dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_morph_domain(fold->qp[i], + isl_morph_copy(morph)); + if (!fold->qp[i]) + goto error; + } + + isl_morph_free(morph); + + return fold; +error: + isl_qpolynomial_fold_free(fold); + isl_morph_free(morph); + return NULL; +} + +enum isl_fold isl_qpolynomial_fold_get_type(__isl_keep isl_qpolynomial_fold *fold) +{ + if (!fold) + return isl_fold_list; + return fold->type; +} + +enum isl_fold isl_union_pw_qpolynomial_fold_get_type( + __isl_keep isl_union_pw_qpolynomial_fold *upwf) +{ + if (!upwf) + return isl_fold_list; + return upwf->type; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_lift( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *dim) +{ + int i; + + if (!fold || !dim) + goto error; + + if (isl_space_is_equal(fold->dim, dim)) { + isl_space_free(dim); + return fold; + } + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + goto error; + + isl_space_free(fold->dim); + fold->dim = isl_space_copy(dim); + if (!fold->dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_lift(fold->qp[i], + isl_space_copy(dim)); + if (!fold->qp[i]) + goto error; + } + + isl_space_free(dim); + + return fold; +error: + isl_qpolynomial_fold_free(fold); + isl_space_free(dim); + return NULL; +} + +isl_stat isl_qpolynomial_fold_foreach_qpolynomial( + __isl_keep isl_qpolynomial_fold *fold, + isl_stat (*fn)(__isl_take isl_qpolynomial *qp, void *user), void *user) +{ + int i; + + if (!fold) + return isl_stat_error; + + for (i = 0; i < fold->n; ++i) + if (fn(isl_qpolynomial_copy(fold->qp[i]), user) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_move_dims( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + int i; + + if (n == 0) + return fold; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + + fold->dim = isl_space_move_dims(fold->dim, dst_type, dst_pos, + src_type, src_pos, n); + if (!fold->dim) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_move_dims(fold->qp[i], + dst_type, dst_pos, src_type, src_pos, n); + if (!fold->qp[i]) + goto error; + } + + return fold; +error: + isl_qpolynomial_fold_free(fold); + return NULL; +} + +/* For each 0 <= i < "n", replace variable "first" + i of type "type" + * in fold->qp[k] by subs[i]. + */ +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute( + __isl_take isl_qpolynomial_fold *fold, + enum isl_dim_type type, unsigned first, unsigned n, + __isl_keep isl_qpolynomial **subs) +{ + int i; + + if (n == 0) + return fold; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_substitute(fold->qp[i], + type, first, n, subs); + if (!fold->qp[i]) + goto error; + } + + return fold; +error: + isl_qpolynomial_fold_free(fold); + return NULL; +} + +static isl_stat add_pwqp(__isl_take isl_pw_qpolynomial *pwqp, void *user) +{ + isl_ctx *ctx; + isl_pw_qpolynomial_fold *pwf; + isl_union_pw_qpolynomial_fold **upwf; + struct isl_hash_table_entry *entry; + + upwf = (isl_union_pw_qpolynomial_fold **)user; + + ctx = pwqp->dim->ctx; + entry = isl_union_pw_qpolynomial_fold_find_part_entry(*upwf, + pwqp->dim, 1); + if (!entry) + goto error; + + pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial((*upwf)->type, pwqp); + if (!entry->data) + entry->data = pwf; + else { + entry->data = isl_pw_qpolynomial_fold_add(entry->data, pwf); + if (!entry->data) + return isl_stat_error; + if (isl_pw_qpolynomial_fold_is_zero(entry->data)) + *upwf = isl_union_pw_qpolynomial_fold_remove_part_entry( + *upwf, entry); + } + + return isl_stat_ok; +error: + isl_pw_qpolynomial_free(pwqp); + return isl_stat_error; +} + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_add_union_pw_qpolynomial( + __isl_take isl_union_pw_qpolynomial_fold *upwf, + __isl_take isl_union_pw_qpolynomial *upwqp) +{ + upwf = isl_union_pw_qpolynomial_fold_align_params(upwf, + isl_union_pw_qpolynomial_get_space(upwqp)); + upwqp = isl_union_pw_qpolynomial_align_params(upwqp, + isl_union_pw_qpolynomial_fold_get_space(upwf)); + + upwf = isl_union_pw_qpolynomial_fold_cow(upwf); + if (!upwf || !upwqp) + goto error; + + if (isl_union_pw_qpolynomial_foreach_pw_qpolynomial(upwqp, &add_pwqp, + &upwf) < 0) + goto error; + + isl_union_pw_qpolynomial_free(upwqp); + + return upwf; +error: + isl_union_pw_qpolynomial_fold_free(upwf); + isl_union_pw_qpolynomial_free(upwqp); + return NULL; +} + +static int join_compatible(__isl_keep isl_space *dim1, __isl_keep isl_space *dim2) +{ + int m; + m = isl_space_match(dim1, isl_dim_param, dim2, isl_dim_param); + if (m < 0 || !m) + return m; + return isl_space_tuple_is_equal(dim1, isl_dim_out, dim2, isl_dim_in); +} + +/* Compute the intersection of the range of the map and the domain + * of the piecewise quasipolynomial reduction and then compute a bound + * on the associated quasipolynomial reduction over all elements + * in this intersection. + * + * We first introduce some unconstrained dimensions in the + * piecewise quasipolynomial, intersect the resulting domain + * with the wrapped map and the compute the sum. + */ +__isl_give isl_pw_qpolynomial_fold *isl_map_apply_pw_qpolynomial_fold( + __isl_take isl_map *map, __isl_take isl_pw_qpolynomial_fold *pwf, + int *tight) +{ + isl_ctx *ctx; + isl_set *dom; + isl_space *map_dim; + isl_space *pwf_dim; + unsigned n_in; + int ok; + + ctx = isl_map_get_ctx(map); + if (!ctx) + goto error; + + map_dim = isl_map_get_space(map); + pwf_dim = isl_pw_qpolynomial_fold_get_space(pwf); + ok = join_compatible(map_dim, pwf_dim); + isl_space_free(map_dim); + isl_space_free(pwf_dim); + if (!ok) + isl_die(ctx, isl_error_invalid, "incompatible dimensions", + goto error); + + n_in = isl_map_dim(map, isl_dim_in); + pwf = isl_pw_qpolynomial_fold_insert_dims(pwf, isl_dim_in, 0, n_in); + + dom = isl_map_wrap(map); + pwf = isl_pw_qpolynomial_fold_reset_domain_space(pwf, + isl_set_get_space(dom)); + + pwf = isl_pw_qpolynomial_fold_intersect_domain(pwf, dom); + pwf = isl_pw_qpolynomial_fold_bound(pwf, tight); + + return pwf; +error: + isl_map_free(map); + isl_pw_qpolynomial_fold_free(pwf); + return NULL; +} + +__isl_give isl_pw_qpolynomial_fold *isl_set_apply_pw_qpolynomial_fold( + __isl_take isl_set *set, __isl_take isl_pw_qpolynomial_fold *pwf, + int *tight) +{ + return isl_map_apply_pw_qpolynomial_fold(set, pwf, tight); +} + +struct isl_apply_fold_data { + isl_union_pw_qpolynomial_fold *upwf; + isl_union_pw_qpolynomial_fold *res; + isl_map *map; + int tight; +}; + +static isl_stat pw_qpolynomial_fold_apply( + __isl_take isl_pw_qpolynomial_fold *pwf, void *user) +{ + isl_space *map_dim; + isl_space *pwf_dim; + struct isl_apply_fold_data *data = user; + int ok; + + map_dim = isl_map_get_space(data->map); + pwf_dim = isl_pw_qpolynomial_fold_get_space(pwf); + ok = join_compatible(map_dim, pwf_dim); + isl_space_free(map_dim); + isl_space_free(pwf_dim); + + if (ok) { + pwf = isl_map_apply_pw_qpolynomial_fold(isl_map_copy(data->map), + pwf, data->tight ? &data->tight : NULL); + data->res = isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold( + data->res, pwf); + } else + isl_pw_qpolynomial_fold_free(pwf); + + return isl_stat_ok; +} + +static isl_stat map_apply(__isl_take isl_map *map, void *user) +{ + struct isl_apply_fold_data *data = user; + isl_stat r; + + data->map = map; + r = isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold( + data->upwf, &pw_qpolynomial_fold_apply, data); + + isl_map_free(map); + return r; +} + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_map_apply_union_pw_qpolynomial_fold( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_qpolynomial_fold *upwf, int *tight) +{ + isl_space *dim; + enum isl_fold type; + struct isl_apply_fold_data data; + + upwf = isl_union_pw_qpolynomial_fold_align_params(upwf, + isl_union_map_get_space(umap)); + umap = isl_union_map_align_params(umap, + isl_union_pw_qpolynomial_fold_get_space(upwf)); + + data.upwf = upwf; + data.tight = tight ? 1 : 0; + dim = isl_union_pw_qpolynomial_fold_get_space(upwf); + type = isl_union_pw_qpolynomial_fold_get_type(upwf); + data.res = isl_union_pw_qpolynomial_fold_zero(dim, type); + if (isl_union_map_foreach_map(umap, &map_apply, &data) < 0) + goto error; + + isl_union_map_free(umap); + isl_union_pw_qpolynomial_fold_free(upwf); + + if (tight) + *tight = data.tight; + + return data.res; +error: + isl_union_map_free(umap); + isl_union_pw_qpolynomial_fold_free(upwf); + isl_union_pw_qpolynomial_fold_free(data.res); + return NULL; +} + +__isl_give isl_union_pw_qpolynomial_fold *isl_union_set_apply_union_pw_qpolynomial_fold( + __isl_take isl_union_set *uset, + __isl_take isl_union_pw_qpolynomial_fold *upwf, int *tight) +{ + return isl_union_map_apply_union_pw_qpolynomial_fold(uset, upwf, tight); +} + +/* Reorder the dimension of "fold" according to the given reordering. + */ +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_realign_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_reordering *r) +{ + int i; + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold || !r) + goto error; + + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_realign_domain(fold->qp[i], + isl_reordering_copy(r)); + if (!fold->qp[i]) + goto error; + } + + fold = isl_qpolynomial_fold_reset_domain_space(fold, + isl_space_copy(r->dim)); + + isl_reordering_free(r); + + return fold; +error: + isl_qpolynomial_fold_free(fold); + isl_reordering_free(r); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_mul_isl_int( + __isl_take isl_qpolynomial_fold *fold, isl_int v) +{ + int i; + + if (isl_int_is_one(v)) + return fold; + if (fold && isl_int_is_zero(v)) { + isl_qpolynomial_fold *zero; + isl_space *dim = isl_space_copy(fold->dim); + zero = isl_qpolynomial_fold_empty(fold->type, dim); + isl_qpolynomial_fold_free(fold); + return zero; + } + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + return NULL; + + if (isl_int_is_neg(v)) + fold->type = isl_fold_type_negate(fold->type); + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_mul_isl_int(fold->qp[i], v); + if (!fold->qp[i]) + goto error; + } + + return fold; +error: + isl_qpolynomial_fold_free(fold); + return NULL; +} + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale( + __isl_take isl_qpolynomial_fold *fold, isl_int v) +{ + return isl_qpolynomial_fold_mul_isl_int(fold, v); +} + +/* Multiply "fold" by "v". + */ +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_val( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v) +{ + int i; + + if (!fold || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return fold; + } + if (isl_val_is_zero(v)) { + isl_qpolynomial_fold *zero; + isl_space *space = isl_qpolynomial_fold_get_domain_space(fold); + zero = isl_qpolynomial_fold_empty(fold->type, space); + isl_qpolynomial_fold_free(fold); + isl_val_free(v); + return zero; + } + if (!isl_val_is_rat(v)) + isl_die(isl_qpolynomial_fold_get_ctx(fold), isl_error_invalid, + "expecting rational factor", goto error); + + fold = isl_qpolynomial_fold_cow(fold); + if (!fold) + goto error; + + if (isl_val_is_neg(v)) + fold->type = isl_fold_type_negate(fold->type); + for (i = 0; i < fold->n; ++i) { + fold->qp[i] = isl_qpolynomial_scale_val(fold->qp[i], + isl_val_copy(v)); + if (!fold->qp[i]) + goto error; + } + + isl_val_free(v); + return fold; +error: + isl_val_free(v); + isl_qpolynomial_fold_free(fold); + return NULL; +} + +/* Divide "fold" by "v". + */ +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_down_val( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v) +{ + if (!fold || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return fold; + } + if (!isl_val_is_rat(v)) + isl_die(isl_qpolynomial_fold_get_ctx(fold), isl_error_invalid, + "expecting rational factor", goto error); + if (isl_val_is_zero(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "cannot scale down by zero", goto error); + + return isl_qpolynomial_fold_scale_val(fold, isl_val_inv(v)); +error: + isl_val_free(v); + isl_qpolynomial_fold_free(fold); + return NULL; +} Index: lib/Analysis/isl/isl_gmp.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_gmp.c @@ -0,0 +1,24 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include + +uint32_t isl_gmp_hash(mpz_t v, uint32_t hash) +{ + int sa = v[0]._mp_size; + int abs_sa = sa < 0 ? -sa : sa; + unsigned char *data = (unsigned char *)v[0]._mp_d; + unsigned char *end = data + abs_sa * sizeof(v[0]._mp_d[0]); + + if (sa < 0) + isl_hash_byte(hash, 0xFF); + for (; data < end; ++data) + isl_hash_byte(hash, *data); + return hash; +} Index: lib/Analysis/isl/isl_hash.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_hash.c @@ -0,0 +1,235 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include "isl_config.h" + +uint32_t isl_hash_string(uint32_t hash, const char *s) +{ + for (; *s; s++) + isl_hash_byte(hash, *s); + return hash; +} + +uint32_t isl_hash_mem(uint32_t hash, const void *p, size_t len) +{ + int i; + const char *s = p; + for (i = 0; i < len; ++i) + isl_hash_byte(hash, s[i]); + return hash; +} + +static unsigned int round_up(unsigned int v) +{ + int old_v = v; + + while (v) { + old_v = v; + v ^= v & -v; + } + return old_v << 1; +} + +int isl_hash_table_init(struct isl_ctx *ctx, struct isl_hash_table *table, + int min_size) +{ + size_t size; + + if (!table) + return -1; + + if (min_size < 2) + min_size = 2; + table->bits = ffs(round_up(4 * (min_size + 1) / 3 - 1)) - 1; + table->n = 0; + + size = 1 << table->bits; + table->entries = isl_calloc_array(ctx, struct isl_hash_table_entry, + size); + if (!table->entries) + return -1; + + return 0; +} + +/* Dummy comparison function that always returns false. + */ +static int no(const void *entry, const void *val) +{ + return 0; +} + +/* Extend "table" to twice its size. + * Return 0 on success and -1 on error. + * + * We reuse isl_hash_table_find to create entries in the extended table. + * Since all entries in the original table are assumed to be different, + * there is no need to compare them against each other. + */ +static int grow_table(struct isl_ctx *ctx, struct isl_hash_table *table) +{ + int n; + size_t old_size, size; + struct isl_hash_table_entry *entries; + uint32_t h; + + entries = table->entries; + old_size = 1 << table->bits; + size = 2 * old_size; + table->entries = isl_calloc_array(ctx, struct isl_hash_table_entry, + size); + if (!table->entries) { + table->entries = entries; + return -1; + } + + n = table->n; + table->n = 0; + table->bits++; + + for (h = 0; h < old_size; ++h) { + struct isl_hash_table_entry *entry; + + if (!entries[h].data) + continue; + + entry = isl_hash_table_find(ctx, table, entries[h].hash, + &no, NULL, 1); + if (!entry) { + table->bits--; + free(table->entries); + table->entries = entries; + table->n = n; + return -1; + } + + *entry = entries[h]; + } + + free(entries); + + return 0; +} + +struct isl_hash_table *isl_hash_table_alloc(struct isl_ctx *ctx, int min_size) +{ + struct isl_hash_table *table = NULL; + + table = isl_alloc_type(ctx, struct isl_hash_table); + if (isl_hash_table_init(ctx, table, min_size)) + goto error; + return table; +error: + isl_hash_table_free(ctx, table); + return NULL; +} + +void isl_hash_table_clear(struct isl_hash_table *table) +{ + if (!table) + return; + free(table->entries); +} + +void isl_hash_table_free(struct isl_ctx *ctx, struct isl_hash_table *table) +{ + if (!table) + return; + isl_hash_table_clear(table); + free(table); +} + +/* A dummy entry that can be used to make a distinction between + * a missing entry and an error condition. + * It is used by isl_union_*_find_part_entry. + */ +static struct isl_hash_table_entry none = { 0, NULL }; +struct isl_hash_table_entry *isl_hash_table_entry_none = &none; + +struct isl_hash_table_entry *isl_hash_table_find(struct isl_ctx *ctx, + struct isl_hash_table *table, + uint32_t key_hash, + int (*eq)(const void *entry, const void *val), + const void *val, int reserve) +{ + size_t size; + uint32_t h, key_bits; + + key_bits = isl_hash_bits(key_hash, table->bits); + size = 1 << table->bits; + for (h = key_bits; table->entries[h].data; h = (h+1) % size) + if (table->entries[h].hash == key_hash && + eq(table->entries[h].data, val)) + return &table->entries[h]; + + if (!reserve) + return NULL; + + if (4 * table->n >= 3 * size) { + if (grow_table(ctx, table) < 0) + return NULL; + return isl_hash_table_find(ctx, table, key_hash, eq, val, 1); + } + + table->n++; + table->entries[h].hash = key_hash; + + return &table->entries[h]; +} + +isl_stat isl_hash_table_foreach(isl_ctx *ctx, struct isl_hash_table *table, + isl_stat (*fn)(void **entry, void *user), void *user) +{ + size_t size; + uint32_t h; + + if (!table->entries) + return isl_stat_error; + + size = 1 << table->bits; + for (h = 0; h < size; ++ h) + if (table->entries[h].data && + fn(&table->entries[h].data, user) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +void isl_hash_table_remove(struct isl_ctx *ctx, + struct isl_hash_table *table, + struct isl_hash_table_entry *entry) +{ + int h, h2; + size_t size; + + if (!table || !entry) + return; + + size = 1 << table->bits; + h = entry - table->entries; + isl_assert(ctx, h >= 0 && h < size, return); + + for (h2 = h+1; table->entries[h2 % size].data; h2++) { + uint32_t bits = isl_hash_bits(table->entries[h2 % size].hash, + table->bits); + uint32_t offset = (size + bits - (h+1)) % size; + if (offset <= h2 - (h+1)) + continue; + *entry = table->entries[h2 % size]; + h = h2; + entry = &table->entries[h % size]; + } + + entry->hash = 0; + entry->data = NULL; + table->n--; +} Index: lib/Analysis/isl/isl_hash_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_hash_private.h @@ -0,0 +1,8 @@ +#ifndef ISL_HASH_PRIVATE +#define ISL_HASH_PRIVATE + +#include + +extern struct isl_hash_table_entry *isl_hash_table_entry_none; + +#endif Index: lib/Analysis/isl/isl_hide_deprecated.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_hide_deprecated.h @@ -0,0 +1,52 @@ +#define isl_aff_get_constant isl_gmp_aff_get_constant +#define isl_aff_get_coefficient isl_gmp_aff_get_coefficient +#define isl_aff_get_denominator isl_gmp_aff_get_denominator +#define isl_aff_set_constant isl_gmp_aff_set_constant +#define isl_aff_set_coefficient isl_gmp_aff_set_coefficient +#define isl_aff_set_denominator isl_gmp_aff_set_denominator +#define isl_aff_add_constant isl_gmp_aff_add_constant +#define isl_aff_add_constant_num isl_gmp_aff_add_constant_num +#define isl_aff_add_coefficient isl_gmp_aff_add_coefficient +#define isl_aff_mod isl_gmp_aff_mod +#define isl_aff_scale isl_gmp_aff_scale +#define isl_aff_scale_down isl_gmp_aff_scale_down +#define isl_pw_aff_mod isl_gmp_pw_aff_mod +#define isl_pw_aff_scale isl_gmp_pw_aff_scale +#define isl_pw_aff_scale_down isl_gmp_pw_aff_scale_down +#define isl_multi_aff_scale isl_gmp_multi_aff_scale +#define isl_ast_expr_get_int isl_gmp_ast_expr_get_int +#define isl_constraint_get_constant isl_gmp_constraint_get_constant +#define isl_constraint_get_coefficient isl_gmp_constraint_get_coefficient +#define isl_constraint_set_constant isl_gmp_constraint_set_constant +#define isl_constraint_set_coefficient isl_gmp_constraint_set_coefficient +#define isl_basic_set_max isl_gmp_basic_set_max +#define isl_set_min isl_gmp_set_min +#define isl_set_max isl_gmp_set_max +#define isl_gmp_hash isl_gmp_gmp_hash +#define isl_basic_map_plain_is_fixed isl_gmp_basic_map_plain_is_fixed +#define isl_map_fix isl_gmp_map_fix +#define isl_map_plain_is_fixed isl_gmp_map_plain_is_fixed +#define isl_map_fixed_power isl_gmp_map_fixed_power +#define isl_mat_get_element isl_gmp_mat_get_element +#define isl_mat_set_element isl_gmp_mat_set_element +#define isl_point_get_coordinate isl_gmp_point_get_coordinate +#define isl_point_set_coordinate isl_gmp_point_set_coordinate +#define isl_qpolynomial_rat_cst_on_domain isl_gmp_qpolynomial_rat_cst_on_domain +#define isl_qpolynomial_is_cst isl_gmp_qpolynomial_is_cst +#define isl_qpolynomial_scale isl_gmp_qpolynomial_scale +#define isl_term_get_num isl_gmp_term_get_num +#define isl_term_get_den isl_gmp_term_get_den +#define isl_qpolynomial_fold_scale isl_gmp_qpolynomial_fold_scale +#define isl_pw_qpolynomial_fold_fix_dim isl_gmp_pw_qpolynomial_fold_fix_dim +#define isl_basic_set_fix isl_gmp_basic_set_fix +#define isl_set_lower_bound isl_gmp_set_lower_bound +#define isl_set_upper_bound isl_gmp_set_upper_bound +#define isl_set_fix isl_gmp_set_fix +#define isl_set_plain_is_fixed isl_gmp_set_plain_is_fixed +#define isl_union_map_fixed_power isl_gmp_union_map_fixed_power +#define isl_val_int_from_isl_int isl_gmp_val_int_from_isl_int +#define isl_val_get_num_isl_int isl_gmp_val_get_num_isl_int +#define isl_vec_get_element isl_gmp_vec_get_element +#define isl_vec_set_element isl_gmp_vec_set_element +#define isl_vec_set isl_gmp_vec_set +#define isl_vec_fdiv_r isl_gmp_vec_fdiv_r Index: lib/Analysis/isl/isl_id.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_id.c @@ -0,0 +1,244 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include + +#undef BASE +#define BASE id + +#include + +/* A special, static isl_id to use as domains (and ranges) + * of sets and parameters domains. + * The user should never get a hold on this isl_id. + */ +isl_id isl_id_none = { + .ref = -1, + .ctx = NULL, + .name = "#none", + .user = NULL +}; + +isl_ctx *isl_id_get_ctx(__isl_keep isl_id *id) +{ + return id ? id->ctx : NULL; +} + +void *isl_id_get_user(__isl_keep isl_id *id) +{ + return id ? id->user : NULL; +} + +const char *isl_id_get_name(__isl_keep isl_id *id) +{ + return id ? id->name : NULL; +} + +static __isl_give isl_id *id_alloc(isl_ctx *ctx, const char *name, void *user) +{ + const char *copy = name ? strdup(name) : NULL; + isl_id *id; + + if (name && !copy) + return NULL; + id = isl_calloc_type(ctx, struct isl_id); + if (!id) + goto error; + + id->ctx = ctx; + isl_ctx_ref(id->ctx); + id->ref = 1; + id->name = copy; + id->user = user; + + id->hash = isl_hash_init(); + if (name) + id->hash = isl_hash_string(id->hash, name); + else + id->hash = isl_hash_builtin(id->hash, user); + + return id; +error: + free((char *)copy); + return NULL; +} + +uint32_t isl_id_get_hash(__isl_keep isl_id *id) +{ + return id ? id->hash : 0; +} + +struct isl_name_and_user { + const char *name; + void *user; +}; + +static int isl_id_has_name_and_user(const void *entry, const void *val) +{ + isl_id *id = (isl_id *)entry; + struct isl_name_and_user *nu = (struct isl_name_and_user *) val; + + if (id->user != nu->user) + return 0; + if (id->name == nu->name) + return 1; + if (!id->name || !nu->name) + return 0; + + return !strcmp(id->name, nu->name); +} + +__isl_give isl_id *isl_id_alloc(isl_ctx *ctx, const char *name, void *user) +{ + struct isl_hash_table_entry *entry; + uint32_t id_hash; + struct isl_name_and_user nu = { name, user }; + + if (!ctx) + return NULL; + + id_hash = isl_hash_init(); + if (name) + id_hash = isl_hash_string(id_hash, name); + else + id_hash = isl_hash_builtin(id_hash, user); + entry = isl_hash_table_find(ctx, &ctx->id_table, id_hash, + isl_id_has_name_and_user, &nu, 1); + if (!entry) + return NULL; + if (entry->data) + return isl_id_copy(entry->data); + entry->data = id_alloc(ctx, name, user); + if (!entry->data) + ctx->id_table.n--; + return entry->data; +} + +/* If the id has a negative refcount, then it is a static isl_id + * which should not be changed. + */ +__isl_give isl_id *isl_id_copy(isl_id *id) +{ + if (!id) + return NULL; + + if (id->ref < 0) + return id; + + id->ref++; + return id; +} + +/* Compare two isl_ids. + * + * The order is fairly arbitrary. We do keep the comparison of + * the user pointers as a last resort since these pointer values + * may not be stable across different systems or even different runs. + */ +int isl_id_cmp(__isl_keep isl_id *id1, __isl_keep isl_id *id2) +{ + if (id1 == id2) + return 0; + if (!id1) + return -1; + if (!id2) + return 1; + if (!id1->name != !id2->name) + return !id1->name - !id2->name; + if (id1->name) { + int cmp = strcmp(id1->name, id2->name); + if (cmp != 0) + return cmp; + } + if (id1->user < id2->user) + return -1; + else + return 1; +} + +static int isl_id_eq(const void *entry, const void *name) +{ + return entry == name; +} + +uint32_t isl_hash_id(uint32_t hash, __isl_keep isl_id *id) +{ + if (id) + isl_hash_hash(hash, id->hash); + + return hash; +} + +/* Replace the free_user callback by "free_user". + */ +__isl_give isl_id *isl_id_set_free_user(__isl_take isl_id *id, + void (*free_user)(void *user)) +{ + if (!id) + return NULL; + + id->free_user = free_user; + + return id; +} + +/* If the id has a negative refcount, then it is a static isl_id + * and should not be freed. + */ +__isl_null isl_id *isl_id_free(__isl_take isl_id *id) +{ + struct isl_hash_table_entry *entry; + + if (!id) + return NULL; + + if (id->ref < 0) + return NULL; + + if (--id->ref > 0) + return NULL; + + entry = isl_hash_table_find(id->ctx, &id->ctx->id_table, id->hash, + isl_id_eq, id, 0); + if (!entry) + isl_die(id->ctx, isl_error_unknown, + "unable to find id", (void)0); + else + isl_hash_table_remove(id->ctx, &id->ctx->id_table, entry); + + if (id->free_user) + id->free_user(id->user); + + free((char *)id->name); + isl_ctx_deref(id->ctx); + free(id); + + return NULL; +} + +__isl_give isl_printer *isl_printer_print_id(__isl_take isl_printer *p, + __isl_keep isl_id *id) +{ + if (!id) + goto error; + + if (id->name) + p = isl_printer_print_str(p, id->name); + if (id->user) { + char buffer[50]; + snprintf(buffer, sizeof(buffer), "@%p", id->user); + p = isl_printer_print_str(p, buffer); + } + return p; +error: + isl_printer_free(p); + return NULL; +} Index: lib/Analysis/isl/isl_id_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_id_private.h @@ -0,0 +1,41 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_ID_PRIVATE_H +#define ISL_ID_PRIVATE_H + +#include + +/* Represent a name and/or user pointer. + * + * If "free_user" is set, then it will be called on "user" when + * the last instance of the isl_id is freed. + */ +struct isl_id { + int ref; + isl_ctx *ctx; + + const char *name; + void *user; + uint32_t hash; + + __isl_give void (*free_user)(void *user); +}; + +#undef EL +#define EL isl_id + +#include + +uint32_t isl_hash_id(uint32_t hash, __isl_keep isl_id *id); +int isl_id_cmp(__isl_keep isl_id *id1, __isl_keep isl_id *id2); + +extern isl_id isl_id_none; + +#endif Index: lib/Analysis/isl/isl_id_to_ast_expr.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_id_to_ast_expr.c @@ -0,0 +1,15 @@ +#include +#include + +#define isl_id_is_equal(id1,id2) id1 == id2 + +#define ISL_KEY isl_id +#define ISL_VAL isl_ast_expr +#define ISL_HMAP_SUFFIX id_to_ast_expr +#define ISL_HMAP isl_id_to_ast_expr +#define ISL_KEY_IS_EQUAL isl_id_is_equal +#define ISL_VAL_IS_EQUAL isl_ast_expr_is_equal +#define ISL_KEY_PRINT isl_printer_print_id +#define ISL_VAL_PRINT isl_printer_print_ast_expr + +#include Index: lib/Analysis/isl/isl_id_to_id.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_id_to_id.c @@ -0,0 +1,14 @@ +#include + +#define isl_id_is_equal(id1,id2) id1 == id2 + +#define ISL_KEY isl_id +#define ISL_VAL isl_id +#define ISL_HMAP_SUFFIX id_to_id +#define ISL_HMAP isl_id_to_id +#define ISL_KEY_IS_EQUAL isl_id_is_equal +#define ISL_VAL_IS_EQUAL isl_id_is_equal +#define ISL_KEY_PRINT isl_printer_print_id +#define ISL_VAL_PRINT isl_printer_print_id + +#include Index: lib/Analysis/isl/isl_id_to_pw_aff.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_id_to_pw_aff.c @@ -0,0 +1,15 @@ +#include +#include + +#define isl_id_is_equal(id1,id2) id1 == id2 + +#define ISL_KEY isl_id +#define ISL_VAL isl_pw_aff +#define ISL_HMAP_SUFFIX id_to_pw_aff +#define ISL_HMAP isl_id_to_pw_aff +#define ISL_KEY_IS_EQUAL isl_id_is_equal +#define ISL_VAL_IS_EQUAL isl_pw_aff_plain_is_equal +#define ISL_KEY_PRINT isl_printer_print_id +#define ISL_VAL_PRINT isl_printer_print_pw_aff + +#include Index: lib/Analysis/isl/isl_ilp.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ilp.c @@ -0,0 +1,836 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include "isl_sample.h" +#include +#include "isl_equalities.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* Given a basic set "bset", construct a basic set U such that for + * each element x in U, the whole unit box positioned at x is inside + * the given basic set. + * Note that U may not contain all points that satisfy this property. + * + * We simply add the sum of all negative coefficients to the constant + * term. This ensures that if x satisfies the resulting constraints, + * then x plus any sum of unit vectors satisfies the original constraints. + */ +static struct isl_basic_set *unit_box_base_points(struct isl_basic_set *bset) +{ + int i, j, k; + struct isl_basic_set *unit_box = NULL; + unsigned total; + + if (!bset) + goto error; + + if (bset->n_eq != 0) { + isl_space *space = isl_basic_set_get_space(bset); + isl_basic_set_free(bset); + return isl_basic_set_empty(space); + } + + total = isl_basic_set_total_dim(bset); + unit_box = isl_basic_set_alloc_space(isl_basic_set_get_space(bset), + 0, 0, bset->n_ineq); + + for (i = 0; i < bset->n_ineq; ++i) { + k = isl_basic_set_alloc_inequality(unit_box); + if (k < 0) + goto error; + isl_seq_cpy(unit_box->ineq[k], bset->ineq[i], 1 + total); + for (j = 0; j < total; ++j) { + if (isl_int_is_nonneg(unit_box->ineq[k][1 + j])) + continue; + isl_int_add(unit_box->ineq[k][0], + unit_box->ineq[k][0], unit_box->ineq[k][1 + j]); + } + } + + isl_basic_set_free(bset); + return unit_box; +error: + isl_basic_set_free(bset); + isl_basic_set_free(unit_box); + return NULL; +} + +/* Find an integer point in "bset", preferably one that is + * close to minimizing "f". + * + * We first check if we can easily put unit boxes inside bset. + * If so, we take the best base point of any of the unit boxes we can find + * and round it up to the nearest integer. + * If not, we simply pick any integer point in "bset". + */ +static struct isl_vec *initial_solution(struct isl_basic_set *bset, isl_int *f) +{ + enum isl_lp_result res; + struct isl_basic_set *unit_box; + struct isl_vec *sol; + + unit_box = unit_box_base_points(isl_basic_set_copy(bset)); + + res = isl_basic_set_solve_lp(unit_box, 0, f, bset->ctx->one, + NULL, NULL, &sol); + if (res == isl_lp_ok) { + isl_basic_set_free(unit_box); + return isl_vec_ceil(sol); + } + + isl_basic_set_free(unit_box); + + return isl_basic_set_sample_vec(isl_basic_set_copy(bset)); +} + +/* Restrict "bset" to those points with values for f in the interval [l, u]. + */ +static struct isl_basic_set *add_bounds(struct isl_basic_set *bset, + isl_int *f, isl_int l, isl_int u) +{ + int k; + unsigned total; + + total = isl_basic_set_total_dim(bset); + bset = isl_basic_set_extend_constraints(bset, 0, 2); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_cpy(bset->ineq[k], f, 1 + total); + isl_int_sub(bset->ineq[k][0], bset->ineq[k][0], l); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_neg(bset->ineq[k], f, 1 + total); + isl_int_add(bset->ineq[k][0], bset->ineq[k][0], u); + + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Find an integer point in "bset" that minimizes f (in any) such that + * the value of f lies inside the interval [l, u]. + * Return this integer point if it can be found. + * Otherwise, return sol. + * + * We perform a number of steps until l > u. + * In each step, we look for an integer point with value in either + * the whole interval [l, u] or half of the interval [l, l+floor(u-l-1/2)]. + * The choice depends on whether we have found an integer point in the + * previous step. If so, we look for the next point in half of the remaining + * interval. + * If we find a point, the current solution is updated and u is set + * to its value minus 1. + * If no point can be found, we update l to the upper bound of the interval + * we checked (u or l+floor(u-l-1/2)) plus 1. + */ +static struct isl_vec *solve_ilp_search(struct isl_basic_set *bset, + isl_int *f, isl_int *opt, struct isl_vec *sol, isl_int l, isl_int u) +{ + isl_int tmp; + int divide = 1; + + isl_int_init(tmp); + + while (isl_int_le(l, u)) { + struct isl_basic_set *slice; + struct isl_vec *sample; + + if (!divide) + isl_int_set(tmp, u); + else { + isl_int_sub(tmp, u, l); + isl_int_fdiv_q_ui(tmp, tmp, 2); + isl_int_add(tmp, tmp, l); + } + slice = add_bounds(isl_basic_set_copy(bset), f, l, tmp); + sample = isl_basic_set_sample_vec(slice); + if (!sample) { + isl_vec_free(sol); + sol = NULL; + break; + } + if (sample->size > 0) { + isl_vec_free(sol); + sol = sample; + isl_seq_inner_product(f, sol->el, sol->size, opt); + isl_int_sub_ui(u, *opt, 1); + divide = 1; + } else { + isl_vec_free(sample); + if (!divide) + break; + isl_int_add_ui(l, tmp, 1); + divide = 0; + } + } + + isl_int_clear(tmp); + + return sol; +} + +/* Find an integer point in "bset" that minimizes f (if any). + * If sol_p is not NULL then the integer point is returned in *sol_p. + * The optimal value of f is returned in *opt. + * + * The algorithm maintains a currently best solution and an interval [l, u] + * of values of f for which integer solutions could potentially still be found. + * The initial value of the best solution so far is any solution. + * The initial value of l is minimal value of f over the rationals + * (rounded up to the nearest integer). + * The initial value of u is the value of f at the initial solution minus 1. + * + * We then call solve_ilp_search to perform a binary search on the interval. + */ +static enum isl_lp_result solve_ilp(struct isl_basic_set *bset, + isl_int *f, isl_int *opt, + struct isl_vec **sol_p) +{ + enum isl_lp_result res; + isl_int l, u; + struct isl_vec *sol; + + res = isl_basic_set_solve_lp(bset, 0, f, bset->ctx->one, + opt, NULL, &sol); + if (res == isl_lp_ok && isl_int_is_one(sol->el[0])) { + if (sol_p) + *sol_p = sol; + else + isl_vec_free(sol); + return isl_lp_ok; + } + isl_vec_free(sol); + if (res == isl_lp_error || res == isl_lp_empty) + return res; + + sol = initial_solution(bset, f); + if (!sol) + return isl_lp_error; + if (sol->size == 0) { + isl_vec_free(sol); + return isl_lp_empty; + } + if (res == isl_lp_unbounded) { + isl_vec_free(sol); + return isl_lp_unbounded; + } + + isl_int_init(l); + isl_int_init(u); + + isl_int_set(l, *opt); + + isl_seq_inner_product(f, sol->el, sol->size, opt); + isl_int_sub_ui(u, *opt, 1); + + sol = solve_ilp_search(bset, f, opt, sol, l, u); + if (!sol) + res = isl_lp_error; + + isl_int_clear(l); + isl_int_clear(u); + + if (sol_p) + *sol_p = sol; + else + isl_vec_free(sol); + + return res; +} + +static enum isl_lp_result solve_ilp_with_eq(struct isl_basic_set *bset, int max, + isl_int *f, isl_int *opt, + struct isl_vec **sol_p) +{ + unsigned dim; + enum isl_lp_result res; + struct isl_mat *T = NULL; + struct isl_vec *v; + + bset = isl_basic_set_copy(bset); + dim = isl_basic_set_total_dim(bset); + v = isl_vec_alloc(bset->ctx, 1 + dim); + if (!v) + goto error; + isl_seq_cpy(v->el, f, 1 + dim); + bset = isl_basic_set_remove_equalities(bset, &T, NULL); + v = isl_vec_mat_product(v, isl_mat_copy(T)); + if (!v) + goto error; + res = isl_basic_set_solve_ilp(bset, max, v->el, opt, sol_p); + isl_vec_free(v); + if (res == isl_lp_ok && sol_p) { + *sol_p = isl_mat_vec_product(T, *sol_p); + if (!*sol_p) + res = isl_lp_error; + } else + isl_mat_free(T); + isl_basic_set_free(bset); + return res; +error: + isl_mat_free(T); + isl_basic_set_free(bset); + return isl_lp_error; +} + +/* Find an integer point in "bset" that minimizes (or maximizes if max is set) + * f (if any). + * If sol_p is not NULL then the integer point is returned in *sol_p. + * The optimal value of f is returned in *opt. + * + * If there is any equality among the points in "bset", then we first + * project it out. Otherwise, we continue with solve_ilp above. + */ +enum isl_lp_result isl_basic_set_solve_ilp(struct isl_basic_set *bset, int max, + isl_int *f, isl_int *opt, + struct isl_vec **sol_p) +{ + unsigned dim; + enum isl_lp_result res; + + if (!bset) + return isl_lp_error; + if (sol_p) + *sol_p = NULL; + + isl_assert(bset->ctx, isl_basic_set_n_param(bset) == 0, goto error); + + if (isl_basic_set_plain_is_empty(bset)) + return isl_lp_empty; + + if (bset->n_eq) + return solve_ilp_with_eq(bset, max, f, opt, sol_p); + + dim = isl_basic_set_total_dim(bset); + + if (max) + isl_seq_neg(f, f, 1 + dim); + + res = solve_ilp(bset, f, opt, sol_p); + + if (max) { + isl_seq_neg(f, f, 1 + dim); + isl_int_neg(*opt, *opt); + } + + return res; +error: + isl_basic_set_free(bset); + return isl_lp_error; +} + +static enum isl_lp_result basic_set_opt(__isl_keep isl_basic_set *bset, int max, + __isl_keep isl_aff *obj, isl_int *opt) +{ + enum isl_lp_result res; + + if (!obj) + return isl_lp_error; + bset = isl_basic_set_copy(bset); + bset = isl_basic_set_underlying_set(bset); + res = isl_basic_set_solve_ilp(bset, max, obj->v->el + 1, opt, NULL); + isl_basic_set_free(bset); + return res; +} + +static __isl_give isl_mat *extract_divs(__isl_keep isl_basic_set *bset) +{ + int i; + isl_ctx *ctx = isl_basic_set_get_ctx(bset); + isl_mat *div; + + div = isl_mat_alloc(ctx, bset->n_div, + 1 + 1 + isl_basic_set_total_dim(bset)); + if (!div) + return NULL; + + for (i = 0; i < bset->n_div; ++i) + isl_seq_cpy(div->row[i], bset->div[i], div->n_col); + + return div; +} + +enum isl_lp_result isl_basic_set_opt(__isl_keep isl_basic_set *bset, int max, + __isl_keep isl_aff *obj, isl_int *opt) +{ + int *exp1 = NULL; + int *exp2 = NULL; + isl_ctx *ctx; + isl_mat *bset_div = NULL; + isl_mat *div = NULL; + enum isl_lp_result res; + int bset_n_div, obj_n_div; + + if (!bset || !obj) + return isl_lp_error; + + ctx = isl_aff_get_ctx(obj); + if (!isl_space_is_equal(bset->dim, obj->ls->dim)) + isl_die(ctx, isl_error_invalid, + "spaces don't match", return isl_lp_error); + if (!isl_int_is_one(obj->v->el[0])) + isl_die(ctx, isl_error_unsupported, + "expecting integer affine expression", + return isl_lp_error); + + bset_n_div = isl_basic_set_dim(bset, isl_dim_div); + obj_n_div = isl_aff_dim(obj, isl_dim_div); + if (bset_n_div == 0 && obj_n_div == 0) + return basic_set_opt(bset, max, obj, opt); + + bset = isl_basic_set_copy(bset); + obj = isl_aff_copy(obj); + + bset_div = extract_divs(bset); + exp1 = isl_alloc_array(ctx, int, bset_n_div); + exp2 = isl_alloc_array(ctx, int, obj_n_div); + if (!bset_div || (bset_n_div && !exp1) || (obj_n_div && !exp2)) + goto error; + + div = isl_merge_divs(bset_div, obj->ls->div, exp1, exp2); + + bset = isl_basic_set_expand_divs(bset, isl_mat_copy(div), exp1); + obj = isl_aff_expand_divs(obj, isl_mat_copy(div), exp2); + + res = basic_set_opt(bset, max, obj, opt); + + isl_mat_free(bset_div); + isl_mat_free(div); + free(exp1); + free(exp2); + isl_basic_set_free(bset); + isl_aff_free(obj); + + return res; +error: + isl_mat_free(div); + isl_mat_free(bset_div); + free(exp1); + free(exp2); + isl_basic_set_free(bset); + isl_aff_free(obj); + return isl_lp_error; +} + +/* Compute the minimum (maximum if max is set) of the integer affine + * expression obj over the points in set and put the result in *opt. + * + * The parameters are assumed to have been aligned. + */ +static enum isl_lp_result isl_set_opt_aligned(__isl_keep isl_set *set, int max, + __isl_keep isl_aff *obj, isl_int *opt) +{ + int i; + enum isl_lp_result res; + int empty = 1; + isl_int opt_i; + + if (!set || !obj) + return isl_lp_error; + if (set->n == 0) + return isl_lp_empty; + + res = isl_basic_set_opt(set->p[0], max, obj, opt); + if (res == isl_lp_error || res == isl_lp_unbounded) + return res; + if (set->n == 1) + return res; + if (res == isl_lp_ok) + empty = 0; + + isl_int_init(opt_i); + for (i = 1; i < set->n; ++i) { + res = isl_basic_set_opt(set->p[i], max, obj, &opt_i); + if (res == isl_lp_error || res == isl_lp_unbounded) { + isl_int_clear(opt_i); + return res; + } + if (res == isl_lp_ok) + empty = 0; + if (max ? isl_int_gt(opt_i, *opt) : isl_int_lt(opt_i, *opt)) + isl_int_set(*opt, opt_i); + } + isl_int_clear(opt_i); + + return empty ? isl_lp_empty : isl_lp_ok; +} + +/* Compute the minimum (maximum if max is set) of the integer affine + * expression obj over the points in set and put the result in *opt. + */ +enum isl_lp_result isl_set_opt(__isl_keep isl_set *set, int max, + __isl_keep isl_aff *obj, isl_int *opt) +{ + enum isl_lp_result res; + + if (!set || !obj) + return isl_lp_error; + + if (isl_space_match(set->dim, isl_dim_param, + obj->ls->dim, isl_dim_param)) + return isl_set_opt_aligned(set, max, obj, opt); + + set = isl_set_copy(set); + obj = isl_aff_copy(obj); + set = isl_set_align_params(set, isl_aff_get_domain_space(obj)); + obj = isl_aff_align_params(obj, isl_set_get_space(set)); + + res = isl_set_opt_aligned(set, max, obj, opt); + + isl_set_free(set); + isl_aff_free(obj); + + return res; +} + +enum isl_lp_result isl_basic_set_max(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj, isl_int *opt) +{ + return isl_basic_set_opt(bset, 1, obj, opt); +} + +enum isl_lp_result isl_set_max(__isl_keep isl_set *set, + __isl_keep isl_aff *obj, isl_int *opt) +{ + return isl_set_opt(set, 1, obj, opt); +} + +enum isl_lp_result isl_set_min(__isl_keep isl_set *set, + __isl_keep isl_aff *obj, isl_int *opt) +{ + return isl_set_opt(set, 0, obj, opt); +} + +/* Convert the result of a function that returns an isl_lp_result + * to an isl_val. The numerator of "v" is set to the optimal value + * if lp_res is isl_lp_ok. "max" is set if a maximum was computed. + * + * Return "v" with denominator set to 1 if lp_res is isl_lp_ok. + * Return NULL on error. + * Return a NaN if lp_res is isl_lp_empty. + * Return infinity or negative infinity if lp_res is isl_lp_unbounded, + * depending on "max". + */ +static __isl_give isl_val *convert_lp_result(enum isl_lp_result lp_res, + __isl_take isl_val *v, int max) +{ + isl_ctx *ctx; + + if (lp_res == isl_lp_ok) { + isl_int_set_si(v->d, 1); + return isl_val_normalize(v); + } + ctx = isl_val_get_ctx(v); + isl_val_free(v); + if (lp_res == isl_lp_error) + return NULL; + if (lp_res == isl_lp_empty) + return isl_val_nan(ctx); + if (max) + return isl_val_infty(ctx); + else + return isl_val_neginfty(ctx); +} + +/* Return the minimum (maximum if max is set) of the integer affine + * expression "obj" over the points in "bset". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "bset" is empty. + * + * Call isl_basic_set_opt and translate the results. + */ +__isl_give isl_val *isl_basic_set_opt_val(__isl_keep isl_basic_set *bset, + int max, __isl_keep isl_aff *obj) +{ + isl_ctx *ctx; + isl_val *res; + enum isl_lp_result lp_res; + + if (!bset || !obj) + return NULL; + + ctx = isl_aff_get_ctx(obj); + res = isl_val_alloc(ctx); + if (!res) + return NULL; + lp_res = isl_basic_set_opt(bset, max, obj, &res->n); + return convert_lp_result(lp_res, res, max); +} + +/* Return the maximum of the integer affine + * expression "obj" over the points in "bset". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "bset" is empty. + */ +__isl_give isl_val *isl_basic_set_max_val(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj) +{ + return isl_basic_set_opt_val(bset, 1, obj); +} + +/* Return the minimum (maximum if max is set) of the integer affine + * expression "obj" over the points in "set". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "set" is empty. + * + * Call isl_set_opt and translate the results. + */ +__isl_give isl_val *isl_set_opt_val(__isl_keep isl_set *set, int max, + __isl_keep isl_aff *obj) +{ + isl_ctx *ctx; + isl_val *res; + enum isl_lp_result lp_res; + + if (!set || !obj) + return NULL; + + ctx = isl_aff_get_ctx(obj); + res = isl_val_alloc(ctx); + if (!res) + return NULL; + lp_res = isl_set_opt(set, max, obj, &res->n); + return convert_lp_result(lp_res, res, max); +} + +/* Return the minimum of the integer affine + * expression "obj" over the points in "set". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "set" is empty. + */ +__isl_give isl_val *isl_set_min_val(__isl_keep isl_set *set, + __isl_keep isl_aff *obj) +{ + return isl_set_opt_val(set, 0, obj); +} + +/* Return the maximum of the integer affine + * expression "obj" over the points in "set". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "set" is empty. + */ +__isl_give isl_val *isl_set_max_val(__isl_keep isl_set *set, + __isl_keep isl_aff *obj) +{ + return isl_set_opt_val(set, 1, obj); +} + +/* Return the optimum (min or max depending on "max") of "v1" and "v2", + * where either may be NaN, signifying an uninitialized value. + * That is, if either is NaN, then return the other one. + */ +static __isl_give isl_val *val_opt(__isl_take isl_val *v1, + __isl_take isl_val *v2, int max) +{ + if (!v1 || !v2) + goto error; + if (isl_val_is_nan(v1)) { + isl_val_free(v1); + return v2; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v2); + return v1; + } + if (max) + return isl_val_max(v1, v2); + else + return isl_val_min(v1, v2); +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Internal data structure for isl_set_opt_pw_aff. + * + * "max" is set if the maximum should be computed. + * "set" is the set over which the optimum should be computed. + * "res" contains the current optimum and is initialized to NaN. + */ +struct isl_set_opt_data { + int max; + isl_set *set; + + isl_val *res; +}; + +/* Update the optimum in data->res with respect to the affine function + * "aff" defined over "set". + */ +static isl_stat piece_opt(__isl_take isl_set *set, __isl_take isl_aff *aff, + void *user) +{ + struct isl_set_opt_data *data = user; + isl_val *opt; + + set = isl_set_intersect(set, isl_set_copy(data->set)); + opt = isl_set_opt_val(set, data->max, aff); + isl_set_free(set); + isl_aff_free(aff); + + data->res = val_opt(data->res, opt, data->max); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Return the minimum (maximum if "max" is set) of the integer piecewise affine + * expression "obj" over the points in "set". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if the intersection of "set" with the domain of "obj" is empty. + * + * Initialize the result to NaN and then update it for each of the pieces + * in "obj". + */ +static __isl_give isl_val *isl_set_opt_pw_aff(__isl_keep isl_set *set, int max, + __isl_keep isl_pw_aff *obj) +{ + struct isl_set_opt_data data = { max, set }; + + data.res = isl_val_nan(isl_set_get_ctx(set)); + if (isl_pw_aff_foreach_piece(obj, &piece_opt, &data) < 0) + return isl_val_free(data.res); + + return data.res; +} + +/* Internal data structure for isl_union_set_opt_union_pw_aff. + * + * "max" is set if the maximum should be computed. + * "obj" is the objective function that needs to be optimized. + * "res" contains the current optimum and is initialized to NaN. + */ +struct isl_union_set_opt_data { + int max; + isl_union_pw_aff *obj; + + isl_val *res; +}; + +/* Update the optimum in data->res with the optimum over "set". + * Do so by first extracting the matching objective function + * from data->obj. + */ +static isl_stat set_opt(__isl_take isl_set *set, void *user) +{ + struct isl_union_set_opt_data *data = user; + isl_space *space; + isl_pw_aff *pa; + isl_val *opt; + + space = isl_set_get_space(set); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, 1); + pa = isl_union_pw_aff_extract_pw_aff(data->obj, space); + opt = isl_set_opt_pw_aff(set, data->max, pa); + isl_pw_aff_free(pa); + isl_set_free(set); + + data->res = val_opt(data->res, opt, data->max); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Return the minimum (maximum if "max" is set) of the integer piecewise affine + * expression "obj" over the points in "uset". + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if the intersection of "uset" with the domain of "obj" is empty. + * + * Initialize the result to NaN and then update it for each of the sets + * in "uset". + */ +static __isl_give isl_val *isl_union_set_opt_union_pw_aff( + __isl_keep isl_union_set *uset, int max, + __isl_keep isl_union_pw_aff *obj) +{ + struct isl_union_set_opt_data data = { max, obj }; + + data.res = isl_val_nan(isl_union_set_get_ctx(uset)); + if (isl_union_set_foreach_set(uset, &set_opt, &data) < 0) + return isl_val_free(data.res); + + return data.res; +} + +/* Return a list of minima (maxima if "max" is set) over the points in "uset" + * for each of the expressions in "obj". + * + * An element in the list is infinity or negative infinity if the optimal + * value of the corresponding expression is unbounded and + * NaN if the intersection of "uset" with the domain of the expression + * is empty. + * + * Iterate over all the expressions in "obj" and collect the results. + */ +static __isl_give isl_multi_val *isl_union_set_opt_multi_union_pw_aff( + __isl_keep isl_union_set *uset, int max, + __isl_keep isl_multi_union_pw_aff *obj) +{ + int i, n; + isl_multi_val *mv; + + if (!uset || !obj) + return NULL; + + n = isl_multi_union_pw_aff_dim(obj, isl_dim_set); + mv = isl_multi_val_zero(isl_multi_union_pw_aff_get_space(obj)); + + for (i = 0; i < n; ++i) { + isl_val *v; + isl_union_pw_aff *upa; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(obj, i); + v = isl_union_set_opt_union_pw_aff(uset, max, upa); + isl_union_pw_aff_free(upa); + mv = isl_multi_val_set_val(mv, i, v); + } + + return mv; +} + +/* Return a list of minima over the points in "uset" + * for each of the expressions in "obj". + * + * An element in the list is infinity or negative infinity if the optimal + * value of the corresponding expression is unbounded and + * NaN if the intersection of "uset" with the domain of the expression + * is empty. + */ +__isl_give isl_multi_val *isl_union_set_min_multi_union_pw_aff( + __isl_keep isl_union_set *uset, __isl_keep isl_multi_union_pw_aff *obj) +{ + return isl_union_set_opt_multi_union_pw_aff(uset, 0, obj); +} Index: lib/Analysis/isl/isl_ilp_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_ilp_private.h @@ -0,0 +1,11 @@ +#ifndef ISL_ILP_PRIVATE_H +#define ISL_ILP_PRIVATE_H + +#include +#include +#include + +enum isl_lp_result isl_basic_set_solve_ilp(__isl_keep isl_basic_set *bset, + int max, isl_int *f, isl_int *opt, __isl_give isl_vec **sol_p); + +#endif Index: lib/Analysis/isl/isl_imath.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_imath.h @@ -0,0 +1,8 @@ +#include +#include + +uint32_t isl_imath_hash(mp_int v, uint32_t hash); +int isl_imath_fits_ulong_p(mp_int op); +int isl_imath_fits_slong_p(mp_int op); +void isl_imath_addmul_ui(mp_int rop, mp_int op1, unsigned long op2); +void isl_imath_submul_ui(mp_int rop, mp_int op1, unsigned long op2); Index: lib/Analysis/isl/isl_imath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_imath.c @@ -0,0 +1,55 @@ +#include + +uint32_t isl_imath_hash(mp_int v, uint32_t hash) +{ + unsigned const char *data = (unsigned char *)v->digits; + unsigned const char *end = data + v->used * sizeof(v->digits[0]); + + if (v->sign == 1) + isl_hash_byte(hash, 0xFF); + for (; data < end; ++data) + isl_hash_byte(hash, *data); + return hash; +} + +/* Try a standard conversion that fits into a long. + */ +int isl_imath_fits_slong_p(mp_int op) +{ + long out; + mp_result res = mp_int_to_int(op, &out); + return res == MP_OK; +} + +/* Try a standard conversion that fits into an unsigned long. + */ +int isl_imath_fits_ulong_p(mp_int op) +{ + unsigned long out; + mp_result res = mp_int_to_uint(op, &out); + return res == MP_OK; +} + +void isl_imath_addmul_ui(mp_int rop, mp_int op1, unsigned long op2) +{ + mpz_t temp; + mp_int_init(&temp); + + mp_int_set_uvalue(&temp, op2); + mp_int_mul(op1, &temp, &temp); + mp_int_add(rop, &temp, rop); + + mp_int_clear(&temp); +} + +void isl_imath_submul_ui(mp_int rop, mp_int op1, unsigned long op2) +{ + mpz_t temp; + mp_int_init(&temp); + + mp_int_set_uvalue(&temp, op2); + mp_int_mul(op1, &temp, &temp); + mp_int_sub(rop, &temp, rop); + + mp_int_clear(&temp); +} Index: lib/Analysis/isl/isl_input.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_input.c @@ -0,0 +1,3958 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "isl_polynomial_private.h" +#include +#include +#include +#include +#include +#include +#include + +struct variable { + char *name; + int pos; + struct variable *next; +}; + +struct vars { + struct isl_ctx *ctx; + int n; + struct variable *v; +}; + +static struct vars *vars_new(struct isl_ctx *ctx) +{ + struct vars *v; + v = isl_alloc_type(ctx, struct vars); + if (!v) + return NULL; + v->ctx = ctx; + v->n = 0; + v->v = NULL; + return v; +} + +static void variable_free(struct variable *var) +{ + while (var) { + struct variable *next = var->next; + free(var->name); + free(var); + var = next; + } +} + +static void vars_free(struct vars *v) +{ + if (!v) + return; + variable_free(v->v); + free(v); +} + +static void vars_drop(struct vars *v, int n) +{ + struct variable *var; + + if (!v || !v->v) + return; + + v->n -= n; + + var = v->v; + while (--n >= 0) { + struct variable *next = var->next; + free(var->name); + free(var); + var = next; + } + v->v = var; +} + +static struct variable *variable_new(struct vars *v, const char *name, int len, + int pos) +{ + struct variable *var; + var = isl_calloc_type(v->ctx, struct variable); + if (!var) + goto error; + var->name = strdup(name); + var->name[len] = '\0'; + var->pos = pos; + var->next = v->v; + return var; +error: + variable_free(v->v); + return NULL; +} + +static int vars_pos(struct vars *v, const char *s, int len) +{ + int pos; + struct variable *q; + + if (len == -1) + len = strlen(s); + for (q = v->v; q; q = q->next) { + if (strncmp(q->name, s, len) == 0 && q->name[len] == '\0') + break; + } + if (q) + pos = q->pos; + else { + pos = v->n; + v->v = variable_new(v, s, len, v->n); + if (!v->v) + return -1; + v->n++; + } + return pos; +} + +static int vars_add_anon(struct vars *v) +{ + v->v = variable_new(v, "", 0, v->n); + + if (!v->v) + return -1; + v->n++; + + return 0; +} + +/* Obtain next token, with some preprocessing. + * In particular, evaluate expressions of the form x^y, + * with x and y values. + */ +static struct isl_token *next_token(__isl_keep isl_stream *s) +{ + struct isl_token *tok, *tok2; + + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) + return tok; + if (!isl_stream_eat_if_available(s, '^')) + return tok; + tok2 = isl_stream_next_token(s); + if (!tok2 || tok2->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok2, "expecting constant value"); + goto error; + } + + isl_int_pow_ui(tok->u.v, tok->u.v, isl_int_get_ui(tok2->u.v)); + + isl_token_free(tok2); + return tok; +error: + isl_token_free(tok); + isl_token_free(tok2); + return NULL; +} + +/* Read an isl_val from "s". + * + * The following token sequences are recognized + * + * "infty" -> infty + * "-" "infty" -> -infty + * "NaN" -> NaN + * n "/" d -> n/d + * v -> v + * + * where n, d and v are integer constants. + */ +__isl_give isl_val *isl_stream_read_val(__isl_keep isl_stream *s) +{ + struct isl_token *tok = NULL; + struct isl_token *tok2 = NULL; + isl_val *val; + + tok = next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + if (tok->type == ISL_TOKEN_INFTY) { + isl_token_free(tok); + return isl_val_infty(s->ctx); + } + if (tok->type == '-' && + isl_stream_eat_if_available(s, ISL_TOKEN_INFTY)) { + isl_token_free(tok); + return isl_val_neginfty(s->ctx); + } + if (tok->type == ISL_TOKEN_NAN) { + isl_token_free(tok); + return isl_val_nan(s->ctx); + } + if (tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting value"); + goto error; + } + + if (isl_stream_eat_if_available(s, '/')) { + tok2 = next_token(s); + if (!tok2) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + if (tok2->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok2, "expecting value"); + goto error; + } + val = isl_val_rat_from_isl_int(s->ctx, tok->u.v, tok2->u.v); + val = isl_val_normalize(val); + } else { + val = isl_val_int_from_isl_int(s->ctx, tok->u.v); + } + + isl_token_free(tok); + isl_token_free(tok2); + return val; +error: + isl_token_free(tok); + isl_token_free(tok2); + return NULL; +} + +/* Read an isl_val from "str". + */ +struct isl_val *isl_val_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + isl_val *val; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + val = isl_stream_read_val(s); + isl_stream_free(s); + return val; +} + +static int accept_cst_factor(__isl_keep isl_stream *s, isl_int *f) +{ + struct isl_token *tok; + + tok = next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting constant value"); + goto error; + } + + isl_int_mul(*f, *f, tok->u.v); + + isl_token_free(tok); + + if (isl_stream_eat_if_available(s, '*')) + return accept_cst_factor(s, f); + + return 0; +error: + isl_token_free(tok); + return -1; +} + +/* Given an affine expression aff, return an affine expression + * for aff % d, with d the next token on the stream, which is + * assumed to be a constant. + * + * We introduce an integer division q = [aff/d] and the result + * is set to aff - d q. + */ +static __isl_give isl_pw_aff *affine_mod(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_pw_aff *aff) +{ + struct isl_token *tok; + isl_pw_aff *q; + + tok = next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting constant value"); + goto error; + } + + q = isl_pw_aff_copy(aff); + q = isl_pw_aff_scale_down(q, tok->u.v); + q = isl_pw_aff_floor(q); + q = isl_pw_aff_scale(q, tok->u.v); + + aff = isl_pw_aff_sub(aff, q); + + isl_token_free(tok); + return aff; +error: + isl_pw_aff_free(aff); + isl_token_free(tok); + return NULL; +} + +static __isl_give isl_pw_aff *accept_affine(__isl_keep isl_stream *s, + __isl_take isl_space *space, struct vars *v); +static __isl_give isl_pw_aff_list *accept_affine_list(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v); + +static __isl_give isl_pw_aff *accept_minmax(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v) +{ + struct isl_token *tok; + isl_pw_aff_list *list = NULL; + int min; + + tok = isl_stream_next_token(s); + if (!tok) + goto error; + min = tok->type == ISL_TOKEN_MIN; + isl_token_free(tok); + + if (isl_stream_eat(s, '(')) + goto error; + + list = accept_affine_list(s, isl_space_copy(dim), v); + if (!list) + goto error; + + if (isl_stream_eat(s, ')')) + goto error; + + isl_space_free(dim); + return min ? isl_pw_aff_list_min(list) : isl_pw_aff_list_max(list); +error: + isl_space_free(dim); + isl_pw_aff_list_free(list); + return NULL; +} + +/* Is "tok" the start of an integer division? + */ +static int is_start_of_div(struct isl_token *tok) +{ + if (!tok) + return 0; + if (tok->type == '[') + return 1; + if (tok->type == ISL_TOKEN_FLOOR) + return 1; + if (tok->type == ISL_TOKEN_CEIL) + return 1; + if (tok->type == ISL_TOKEN_FLOORD) + return 1; + if (tok->type == ISL_TOKEN_CEILD) + return 1; + return 0; +} + +/* Read an integer division from "s" and return it as an isl_pw_aff. + * + * The integer division can be of the form + * + * [] + * floor() + * ceil() + * floord(,) + * ceild(,) + */ +static __isl_give isl_pw_aff *accept_div(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v) +{ + struct isl_token *tok; + int f = 0; + int c = 0; + int extra = 0; + isl_pw_aff *pwaff = NULL; + + if (isl_stream_eat_if_available(s, ISL_TOKEN_FLOORD)) + extra = f = 1; + else if (isl_stream_eat_if_available(s, ISL_TOKEN_CEILD)) + extra = c = 1; + else if (isl_stream_eat_if_available(s, ISL_TOKEN_FLOOR)) + f = 1; + else if (isl_stream_eat_if_available(s, ISL_TOKEN_CEIL)) + c = 1; + if (f || c) { + if (isl_stream_eat(s, '(')) + goto error; + } else { + if (isl_stream_eat(s, '[')) + goto error; + } + + pwaff = accept_affine(s, isl_space_copy(dim), v); + + if (extra) { + if (isl_stream_eat(s, ',')) + goto error; + + tok = next_token(s); + if (!tok) + goto error; + if (tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expected denominator"); + isl_stream_push_token(s, tok); + goto error; + } + isl_pw_aff_scale_down(pwaff, tok->u.v); + isl_token_free(tok); + } + + if (c) + pwaff = isl_pw_aff_ceil(pwaff); + else + pwaff = isl_pw_aff_floor(pwaff); + + if (f || c) { + if (isl_stream_eat(s, ')')) + goto error; + } else { + if (isl_stream_eat(s, ']')) + goto error; + } + + isl_space_free(dim); + return pwaff; +error: + isl_space_free(dim); + isl_pw_aff_free(pwaff); + return NULL; +} + +static __isl_give isl_pw_aff *accept_affine_factor(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v) +{ + struct isl_token *tok = NULL; + isl_pw_aff *res = NULL; + + tok = next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + + if (tok->type == ISL_TOKEN_AFF) { + res = isl_pw_aff_copy(tok->u.pwaff); + isl_token_free(tok); + } else if (tok->type == ISL_TOKEN_IDENT) { + int n = v->n; + int pos = vars_pos(v, tok->u.s, -1); + isl_aff *aff; + + if (pos < 0) + goto error; + if (pos >= n) { + vars_drop(v, v->n - n); + isl_stream_error(s, tok, "unknown identifier"); + goto error; + } + + aff = isl_aff_zero_on_domain(isl_local_space_from_space(isl_space_copy(dim))); + if (!aff) + goto error; + isl_int_set_si(aff->v->el[2 + pos], 1); + res = isl_pw_aff_from_aff(aff); + isl_token_free(tok); + } else if (tok->type == ISL_TOKEN_VALUE) { + if (isl_stream_eat_if_available(s, '*')) { + res = accept_affine_factor(s, isl_space_copy(dim), v); + res = isl_pw_aff_scale(res, tok->u.v); + } else { + isl_local_space *ls; + isl_aff *aff; + ls = isl_local_space_from_space(isl_space_copy(dim)); + aff = isl_aff_zero_on_domain(ls); + aff = isl_aff_add_constant(aff, tok->u.v); + res = isl_pw_aff_from_aff(aff); + } + isl_token_free(tok); + } else if (tok->type == '(') { + isl_token_free(tok); + tok = NULL; + res = accept_affine(s, isl_space_copy(dim), v); + if (!res) + goto error; + if (isl_stream_eat(s, ')')) + goto error; + } else if (is_start_of_div(tok)) { + isl_stream_push_token(s, tok); + tok = NULL; + res = accept_div(s, isl_space_copy(dim), v); + } else if (tok->type == ISL_TOKEN_MIN || tok->type == ISL_TOKEN_MAX) { + isl_stream_push_token(s, tok); + tok = NULL; + res = accept_minmax(s, isl_space_copy(dim), v); + } else { + isl_stream_error(s, tok, "expecting factor"); + goto error; + } + if (isl_stream_eat_if_available(s, '%') || + isl_stream_eat_if_available(s, ISL_TOKEN_MOD)) { + isl_space_free(dim); + return affine_mod(s, v, res); + } + if (isl_stream_eat_if_available(s, '*')) { + isl_int f; + isl_int_init(f); + isl_int_set_si(f, 1); + if (accept_cst_factor(s, &f) < 0) { + isl_int_clear(f); + goto error2; + } + res = isl_pw_aff_scale(res, f); + isl_int_clear(f); + } + if (isl_stream_eat_if_available(s, '/')) { + isl_int f; + isl_int_init(f); + isl_int_set_si(f, 1); + if (accept_cst_factor(s, &f) < 0) { + isl_int_clear(f); + goto error2; + } + res = isl_pw_aff_scale_down(res, f); + isl_int_clear(f); + } + + isl_space_free(dim); + return res; +error: + isl_token_free(tok); +error2: + isl_pw_aff_free(res); + isl_space_free(dim); + return NULL; +} + +static __isl_give isl_pw_aff *add_cst(__isl_take isl_pw_aff *pwaff, isl_int v) +{ + isl_aff *aff; + isl_space *space; + + space = isl_pw_aff_get_domain_space(pwaff); + aff = isl_aff_zero_on_domain(isl_local_space_from_space(space)); + aff = isl_aff_add_constant(aff, v); + + return isl_pw_aff_add(pwaff, isl_pw_aff_from_aff(aff)); +} + +/* Return a piecewise affine expression defined on the specified domain + * that represents NaN. + */ +static __isl_give isl_pw_aff *nan_on_domain(__isl_keep isl_space *space) +{ + isl_local_space *ls; + + ls = isl_local_space_from_space(isl_space_copy(space)); + return isl_pw_aff_nan_on_domain(ls); +} + +static __isl_give isl_pw_aff *accept_affine(__isl_keep isl_stream *s, + __isl_take isl_space *space, struct vars *v) +{ + struct isl_token *tok = NULL; + isl_local_space *ls; + isl_pw_aff *res; + int sign = 1; + + ls = isl_local_space_from_space(isl_space_copy(space)); + res = isl_pw_aff_from_aff(isl_aff_zero_on_domain(ls)); + if (!res) + goto error; + + for (;;) { + tok = next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + if (tok->type == '-') { + sign = -sign; + isl_token_free(tok); + continue; + } + if (tok->type == '(' || is_start_of_div(tok) || + tok->type == ISL_TOKEN_MIN || tok->type == ISL_TOKEN_MAX || + tok->type == ISL_TOKEN_IDENT || + tok->type == ISL_TOKEN_AFF) { + isl_pw_aff *term; + isl_stream_push_token(s, tok); + tok = NULL; + term = accept_affine_factor(s, + isl_space_copy(space), v); + if (sign < 0) + res = isl_pw_aff_sub(res, term); + else + res = isl_pw_aff_add(res, term); + if (!res) + goto error; + sign = 1; + } else if (tok->type == ISL_TOKEN_VALUE) { + if (sign < 0) + isl_int_neg(tok->u.v, tok->u.v); + if (isl_stream_eat_if_available(s, '*') || + isl_stream_next_token_is(s, ISL_TOKEN_IDENT)) { + isl_pw_aff *term; + term = accept_affine_factor(s, + isl_space_copy(space), v); + term = isl_pw_aff_scale(term, tok->u.v); + res = isl_pw_aff_add(res, term); + if (!res) + goto error; + } else { + res = add_cst(res, tok->u.v); + } + sign = 1; + } else if (tok->type == ISL_TOKEN_NAN) { + res = isl_pw_aff_add(res, nan_on_domain(space)); + } else { + isl_stream_error(s, tok, "unexpected isl_token"); + isl_stream_push_token(s, tok); + isl_pw_aff_free(res); + isl_space_free(space); + return NULL; + } + isl_token_free(tok); + + tok = next_token(s); + if (tok && tok->type == '-') { + sign = -sign; + isl_token_free(tok); + } else if (tok && tok->type == '+') { + /* nothing */ + isl_token_free(tok); + } else if (tok && tok->type == ISL_TOKEN_VALUE && + isl_int_is_neg(tok->u.v)) { + isl_stream_push_token(s, tok); + } else { + if (tok) + isl_stream_push_token(s, tok); + break; + } + } + + isl_space_free(space); + return res; +error: + isl_space_free(space); + isl_token_free(tok); + isl_pw_aff_free(res); + return NULL; +} + +/* Is "type" the type of a comparison operator between lists + * of affine expressions? + */ +static int is_list_comparator_type(int type) +{ + switch (type) { + case ISL_TOKEN_LEX_LT: + case ISL_TOKEN_LEX_GT: + case ISL_TOKEN_LEX_LE: + case ISL_TOKEN_LEX_GE: + return 1; + default: + return 0; + } +} + +static int is_comparator(struct isl_token *tok) +{ + if (!tok) + return 0; + if (is_list_comparator_type(tok->type)) + return 1; + + switch (tok->type) { + case ISL_TOKEN_LT: + case ISL_TOKEN_GT: + case ISL_TOKEN_LE: + case ISL_TOKEN_GE: + case ISL_TOKEN_NE: + case '=': + return 1; + default: + return 0; + } +} + +static __isl_give isl_map *read_formula(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational); +static __isl_give isl_pw_aff *accept_extended_affine(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v, int rational); + +/* Accept a ternary operator, given the first argument. + */ +static __isl_give isl_pw_aff *accept_ternary(__isl_keep isl_stream *s, + __isl_take isl_map *cond, struct vars *v, int rational) +{ + isl_space *dim; + isl_pw_aff *pwaff1 = NULL, *pwaff2 = NULL, *pa_cond; + + if (!cond) + return NULL; + + if (isl_stream_eat(s, '?')) + goto error; + + dim = isl_space_wrap(isl_map_get_space(cond)); + pwaff1 = accept_extended_affine(s, dim, v, rational); + if (!pwaff1) + goto error; + + if (isl_stream_eat(s, ':')) + goto error; + + dim = isl_pw_aff_get_domain_space(pwaff1); + pwaff2 = accept_extended_affine(s, dim, v, rational); + if (!pwaff1) + goto error; + + pa_cond = isl_set_indicator_function(isl_map_wrap(cond)); + return isl_pw_aff_cond(pa_cond, pwaff1, pwaff2); +error: + isl_map_free(cond); + isl_pw_aff_free(pwaff1); + isl_pw_aff_free(pwaff2); + return NULL; +} + +/* Set *line and *col to those of the next token, if any. + */ +static void set_current_line_col(__isl_keep isl_stream *s, int *line, int *col) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return; + + *line = tok->line; + *col = tok->col; + isl_stream_push_token(s, tok); +} + +/* Push a token encapsulating "pa" onto "s", with the given + * line and column. + */ +static int push_aff(__isl_keep isl_stream *s, int line, int col, + __isl_take isl_pw_aff *pa) +{ + struct isl_token *tok; + + tok = isl_token_new(s->ctx, line, col, 0); + if (!tok) + goto error; + tok->type = ISL_TOKEN_AFF; + tok->u.pwaff = pa; + isl_stream_push_token(s, tok); + + return 0; +error: + isl_pw_aff_free(pa); + return -1; +} + +/* Accept an affine expression that may involve ternary operators. + * We first read an affine expression. + * If it is not followed by a comparison operator, we simply return it. + * Otherwise, we assume the affine expression is part of the first + * argument of a ternary operator and try to parse that. + */ +static __isl_give isl_pw_aff *accept_extended_affine(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v, int rational) +{ + isl_space *space; + isl_map *cond; + isl_pw_aff *pwaff; + struct isl_token *tok; + int line = -1, col = -1; + int is_comp; + + set_current_line_col(s, &line, &col); + + pwaff = accept_affine(s, dim, v); + if (rational) + pwaff = isl_pw_aff_set_rational(pwaff); + if (!pwaff) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) + return isl_pw_aff_free(pwaff); + + is_comp = is_comparator(tok); + isl_stream_push_token(s, tok); + if (!is_comp) + return pwaff; + + space = isl_pw_aff_get_domain_space(pwaff); + cond = isl_map_universe(isl_space_unwrap(space)); + + if (push_aff(s, line, col, pwaff) < 0) + cond = isl_map_free(cond); + if (!cond) + return NULL; + + cond = read_formula(s, v, cond, rational); + + return accept_ternary(s, cond, v, rational); +} + +static __isl_give isl_map *read_var_def(__isl_keep isl_stream *s, + __isl_take isl_map *map, enum isl_dim_type type, struct vars *v, + int rational) +{ + isl_pw_aff *def; + int pos; + isl_map *def_map; + + if (type == isl_dim_param) + pos = isl_map_dim(map, isl_dim_param); + else { + pos = isl_map_dim(map, isl_dim_in); + if (type == isl_dim_out) + pos += isl_map_dim(map, isl_dim_out); + type = isl_dim_in; + } + --pos; + + def = accept_extended_affine(s, isl_space_wrap(isl_map_get_space(map)), + v, rational); + def_map = isl_map_from_pw_aff(def); + def_map = isl_map_equate(def_map, type, pos, isl_dim_out, 0); + def_map = isl_set_unwrap(isl_map_domain(def_map)); + + map = isl_map_intersect(map, def_map); + + return map; +} + +static __isl_give isl_pw_aff_list *accept_affine_list(__isl_keep isl_stream *s, + __isl_take isl_space *dim, struct vars *v) +{ + isl_pw_aff *pwaff; + isl_pw_aff_list *list; + struct isl_token *tok = NULL; + + pwaff = accept_affine(s, isl_space_copy(dim), v); + list = isl_pw_aff_list_from_pw_aff(pwaff); + if (!list) + goto error; + + for (;;) { + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + if (tok->type != ',') { + isl_stream_push_token(s, tok); + break; + } + isl_token_free(tok); + + pwaff = accept_affine(s, isl_space_copy(dim), v); + list = isl_pw_aff_list_concat(list, + isl_pw_aff_list_from_pw_aff(pwaff)); + if (!list) + goto error; + } + + isl_space_free(dim); + return list; +error: + isl_space_free(dim); + isl_pw_aff_list_free(list); + return NULL; +} + +static __isl_give isl_map *read_defined_var_list(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + struct isl_token *tok; + + while ((tok = isl_stream_next_token(s)) != NULL) { + int p; + int n = v->n; + + if (tok->type != ISL_TOKEN_IDENT) + break; + + p = vars_pos(v, tok->u.s, -1); + if (p < 0) + goto error; + if (p < n) { + isl_stream_error(s, tok, "expecting unique identifier"); + goto error; + } + + map = isl_map_add_dims(map, isl_dim_out, 1); + + isl_token_free(tok); + tok = isl_stream_next_token(s); + if (tok && tok->type == '=') { + isl_token_free(tok); + map = read_var_def(s, map, isl_dim_out, v, rational); + tok = isl_stream_next_token(s); + } + + if (!tok || tok->type != ',') + break; + + isl_token_free(tok); + } + if (tok) + isl_stream_push_token(s, tok); + + return map; +error: + isl_token_free(tok); + isl_map_free(map); + return NULL; +} + +static int next_is_tuple(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + int is_tuple; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type == '[') { + isl_stream_push_token(s, tok); + return 1; + } + if (tok->type != ISL_TOKEN_IDENT && !tok->is_keyword) { + isl_stream_push_token(s, tok); + return 0; + } + + is_tuple = isl_stream_next_token_is(s, '['); + + isl_stream_push_token(s, tok); + + return is_tuple; +} + +/* Is "pa" an expression in term of earlier dimensions? + * The alternative is that the dimension is defined to be equal to itself, + * meaning that it has a universe domain and an expression that depends + * on itself. "i" is the position of the expression in a sequence + * of "n" expressions. The final dimensions of "pa" correspond to + * these "n" expressions. + */ +static int pw_aff_is_expr(__isl_keep isl_pw_aff *pa, int i, int n) +{ + isl_aff *aff; + + if (!pa) + return -1; + if (pa->n != 1) + return 1; + if (!isl_set_plain_is_universe(pa->p[0].set)) + return 1; + + aff = pa->p[0].aff; + if (isl_int_is_zero(aff->v->el[aff->v->size - n + i])) + return 1; + return 0; +} + +/* Does the tuple contain any dimensions that are defined + * in terms of earlier dimensions? + */ +static int tuple_has_expr(__isl_keep isl_multi_pw_aff *tuple) +{ + int i, n; + int has_expr = 0; + isl_pw_aff *pa; + + if (!tuple) + return -1; + n = isl_multi_pw_aff_dim(tuple, isl_dim_out); + for (i = 0; i < n; ++i) { + pa = isl_multi_pw_aff_get_pw_aff(tuple, i); + has_expr = pw_aff_is_expr(pa, i, n); + isl_pw_aff_free(pa); + if (has_expr < 0 || has_expr) + break; + } + + return has_expr; +} + +/* Set the name of dimension "pos" in "space" to "name". + * During printing, we add primes if the same name appears more than once + * to distinguish the occurrences. Here, we remove those primes from "name" + * before setting the name of the dimension. + */ +static __isl_give isl_space *space_set_dim_name(__isl_take isl_space *space, + int pos, char *name) +{ + char *prime; + + if (!name) + return space; + + prime = strchr(name, '\''); + if (prime) + *prime = '\0'; + space = isl_space_set_dim_name(space, isl_dim_out, pos, name); + if (prime) + *prime = '\''; + + return space; +} + +/* Accept a piecewise affine expression. + * + * At the outer level, the piecewise affine expression may be of the form + * + * aff1 : condition1; aff2 : conditions2; ... + * + * or simply + * + * aff + * + * each of the affine expressions may in turn include ternary operators. + * + * There may be parentheses around some subexpression of "aff1" + * around "aff1" itself, around "aff1 : condition1" and/or + * around the entire piecewise affine expression. + * We therefore remove the opening parenthesis (if any) from the stream + * in case the closing parenthesis follows the colon, but if the closing + * parenthesis is the first thing in the stream after the parsed affine + * expression, we push the parsed expression onto the stream and parse + * again in case the parentheses enclose some subexpression of "aff1". + */ +static __isl_give isl_pw_aff *accept_piecewise_affine(__isl_keep isl_stream *s, + __isl_take isl_space *space, struct vars *v, int rational) +{ + isl_pw_aff *res; + isl_space *res_space; + + res_space = isl_space_from_domain(isl_space_copy(space)); + res_space = isl_space_add_dims(res_space, isl_dim_out, 1); + res = isl_pw_aff_empty(res_space); + do { + isl_pw_aff *pa; + int seen_paren; + int line = -1, col = -1; + + set_current_line_col(s, &line, &col); + seen_paren = isl_stream_eat_if_available(s, '('); + if (seen_paren) + pa = accept_piecewise_affine(s, isl_space_copy(space), + v, rational); + else + pa = accept_extended_affine(s, isl_space_copy(space), + v, rational); + if (seen_paren && isl_stream_eat_if_available(s, ')')) { + seen_paren = 0; + if (push_aff(s, line, col, pa) < 0) + goto error; + pa = accept_extended_affine(s, isl_space_copy(space), + v, rational); + } + if (isl_stream_eat_if_available(s, ':')) { + isl_space *dom_space; + isl_set *dom; + + dom_space = isl_pw_aff_get_domain_space(pa); + dom = isl_set_universe(dom_space); + dom = read_formula(s, v, dom, rational); + pa = isl_pw_aff_intersect_domain(pa, dom); + } + + res = isl_pw_aff_union_add(res, pa); + + if (seen_paren && isl_stream_eat(s, ')')) + goto error; + } while (isl_stream_eat_if_available(s, ';')); + + isl_space_free(space); + + return res; +error: + isl_space_free(space); + return isl_pw_aff_free(res); +} + +/* Read an affine expression from "s" for use in read_tuple. + * + * accept_extended_affine requires a wrapped space as input. + * read_tuple on the other hand expects each isl_pw_aff + * to have an anonymous space. We therefore adjust the space + * of the isl_pw_aff before returning it. + */ +static __isl_give isl_pw_aff *read_tuple_var_def(__isl_keep isl_stream *s, + struct vars *v, int rational) +{ + isl_space *space; + isl_pw_aff *def; + + space = isl_space_wrap(isl_space_alloc(s->ctx, 0, v->n, 0)); + + def = accept_piecewise_affine(s, space, v, rational); + + space = isl_space_set_alloc(s->ctx, 0, v->n); + def = isl_pw_aff_reset_domain_space(def, space); + + return def; +} + +/* Read a list of tuple elements by calling "read_el" on each of them and + * return a space with the same number of set dimensions derived from + * the parameter space "space" and possibly updated by "read_el". + * The elements in the list are separated by either "," or "][". + * If "comma" is set then only "," is allowed. + */ +static __isl_give isl_space *read_tuple_list(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, int comma, + __isl_give isl_space *(*read_el)(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, + void *user), + void *user) +{ + if (!space) + return NULL; + + space = isl_space_set_from_params(space); + + if (isl_stream_next_token_is(s, ']')) + return space; + + for (;;) { + struct isl_token *tok; + + space = isl_space_add_dims(space, isl_dim_set, 1); + + space = read_el(s, v, space, rational, user); + if (!space) + return NULL; + + tok = isl_stream_next_token(s); + if (!comma && tok && tok->type == ']' && + isl_stream_next_token_is(s, '[')) { + isl_token_free(tok); + tok = isl_stream_next_token(s); + } else if (!tok || tok->type != ',') { + if (tok) + isl_stream_push_token(s, tok); + break; + } + + isl_token_free(tok); + } + + return space; +} + +/* Read a tuple space from "s" derived from the parameter space "space". + * Call "read_el" on each element in the tuples. + */ +static __isl_give isl_space *read_tuple_space(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, int comma, + __isl_give isl_space *(*read_el)(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, + void *user), + void *user) +{ + struct isl_token *tok; + char *name = NULL; + isl_space *res = NULL; + + tok = isl_stream_next_token(s); + if (!tok) + goto error; + if (tok->type == ISL_TOKEN_IDENT || tok->is_keyword) { + name = strdup(tok->u.s); + isl_token_free(tok); + if (!name) + goto error; + } else + isl_stream_push_token(s, tok); + if (isl_stream_eat(s, '[')) + goto error; + if (next_is_tuple(s)) { + isl_space *out; + res = read_tuple_space(s, v, isl_space_copy(space), + rational, comma, read_el, user); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + out = read_tuple_space(s, v, isl_space_copy(space), + rational, comma, read_el, user); + res = isl_space_range_product(res, out); + } else + res = read_tuple_list(s, v, isl_space_copy(space), + rational, comma, read_el, user); + if (isl_stream_eat(s, ']')) + goto error; + + if (name) { + res = isl_space_set_tuple_name(res, isl_dim_set, name); + free(name); + } + + isl_space_free(space); + return res; +error: + free(name); + isl_space_free(res); + isl_space_free(space); + return NULL; +} + +/* Construct an isl_pw_aff defined on a space with v->n variables + * that is equal to the last of those variables. + */ +static __isl_give isl_pw_aff *identity_tuple_el(struct vars *v) +{ + isl_space *space; + isl_aff *aff; + + space = isl_space_set_alloc(v->ctx, 0, v->n); + aff = isl_aff_zero_on_domain(isl_local_space_from_space(space)); + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, v->n - 1, 1); + return isl_pw_aff_from_aff(aff); +} + +/* This function is called for each element in a tuple inside read_tuple. + * Add a new variable to "v" and construct a corresponding isl_pw_aff defined + * over a space containing all variables in "v" defined so far. + * The isl_pw_aff expresses the new variable in terms of earlier variables + * if a definition is provided. Otherwise, it is represented as being + * equal to itself. + * Add the isl_pw_aff to *list. + * If the new variable was named, then adjust "space" accordingly and + * return the updated space. + */ +static __isl_give isl_space *read_tuple_pw_aff_el(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, void *user) +{ + isl_pw_aff_list **list = (isl_pw_aff_list **) user; + isl_pw_aff *pa; + struct isl_token *tok; + int new_name = 0; + + tok = next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return isl_space_free(space); + } + + if (tok->type == ISL_TOKEN_IDENT) { + int n = v->n; + int p = vars_pos(v, tok->u.s, -1); + if (p < 0) + goto error; + new_name = p >= n; + } + + if (tok->type == '*') { + if (vars_add_anon(v) < 0) + goto error; + isl_token_free(tok); + pa = identity_tuple_el(v); + } else if (new_name) { + int pos = isl_space_dim(space, isl_dim_out) - 1; + space = space_set_dim_name(space, pos, v->v->name); + isl_token_free(tok); + if (isl_stream_eat_if_available(s, '=')) + pa = read_tuple_var_def(s, v, rational); + else + pa = identity_tuple_el(v); + } else { + isl_stream_push_token(s, tok); + tok = NULL; + if (vars_add_anon(v) < 0) + goto error; + pa = read_tuple_var_def(s, v, rational); + } + + *list = isl_pw_aff_list_add(*list, pa); + if (!*list) + return isl_space_free(space); + + return space; +error: + isl_token_free(tok); + return isl_space_free(space); +} + +/* Read a tuple and represent it as an isl_multi_pw_aff. + * The range space of the isl_multi_pw_aff is the space of the tuple. + * The domain space is an anonymous space + * with a dimension for each variable in the set of variables in "v", + * including the variables in the range. + * If a given dimension is not defined in terms of earlier dimensions in + * the input, then the corresponding isl_pw_aff is set equal to one time + * the variable corresponding to the dimension being defined. + * + * The elements in the tuple are collected in a list by read_tuple_pw_aff_el. + * Each element in this list is defined over a space representing + * the variables defined so far. We need to adjust the earlier + * elements to have as many variables in the domain as the final + * element in the list. + */ +static __isl_give isl_multi_pw_aff *read_tuple(__isl_keep isl_stream *s, + struct vars *v, int rational, int comma) +{ + int i, n; + isl_space *space; + isl_pw_aff_list *list; + + space = isl_space_params_alloc(v->ctx, 0); + list = isl_pw_aff_list_alloc(s->ctx, 0); + space = read_tuple_space(s, v, space, rational, comma, + &read_tuple_pw_aff_el, &list); + n = isl_space_dim(space, isl_dim_set); + for (i = 0; i + 1 < n; ++i) { + isl_pw_aff *pa; + + pa = isl_pw_aff_list_get_pw_aff(list, i); + pa = isl_pw_aff_add_dims(pa, isl_dim_in, n - (i + 1)); + list = isl_pw_aff_list_set_pw_aff(list, i, pa); + } + + space = isl_space_from_range(space); + space = isl_space_add_dims(space, isl_dim_in, v->n); + return isl_multi_pw_aff_from_pw_aff_list(space, list); +} + +/* Add the tuple represented by the isl_multi_pw_aff "tuple" to "map". + * We first create the appropriate space in "map" based on the range + * space of this isl_multi_pw_aff. Then, we add equalities based + * on the affine expressions. These live in an anonymous space, + * however, so we first need to reset the space to that of "map". + */ +static __isl_give isl_map *map_from_tuple(__isl_take isl_multi_pw_aff *tuple, + __isl_take isl_map *map, enum isl_dim_type type, struct vars *v, + int rational) +{ + int i, n; + isl_ctx *ctx; + isl_space *space = NULL; + + if (!map || !tuple) + goto error; + ctx = isl_multi_pw_aff_get_ctx(tuple); + n = isl_multi_pw_aff_dim(tuple, isl_dim_out); + space = isl_space_range(isl_multi_pw_aff_get_space(tuple)); + if (!space) + goto error; + + if (type == isl_dim_param) { + if (isl_space_has_tuple_name(space, isl_dim_set) || + isl_space_is_wrapping(space)) { + isl_die(ctx, isl_error_invalid, + "parameter tuples cannot be named or nested", + goto error); + } + map = isl_map_add_dims(map, type, n); + for (i = 0; i < n; ++i) { + isl_id *id; + if (!isl_space_has_dim_name(space, isl_dim_set, i)) + isl_die(ctx, isl_error_invalid, + "parameters must be named", + goto error); + id = isl_space_get_dim_id(space, isl_dim_set, i); + map = isl_map_set_dim_id(map, isl_dim_param, i, id); + } + } else if (type == isl_dim_in) { + isl_set *set; + + set = isl_set_universe(isl_space_copy(space)); + if (rational) + set = isl_set_set_rational(set); + set = isl_set_intersect_params(set, isl_map_params(map)); + map = isl_map_from_domain(set); + } else { + isl_set *set; + + set = isl_set_universe(isl_space_copy(space)); + if (rational) + set = isl_set_set_rational(set); + map = isl_map_from_domain_and_range(isl_map_domain(map), set); + } + + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + isl_space *space; + isl_aff *aff; + isl_set *set; + isl_map *map_i; + + pa = isl_multi_pw_aff_get_pw_aff(tuple, i); + space = isl_pw_aff_get_domain_space(pa); + aff = isl_aff_zero_on_domain(isl_local_space_from_space(space)); + aff = isl_aff_add_coefficient_si(aff, + isl_dim_in, v->n - n + i, -1); + pa = isl_pw_aff_add(pa, isl_pw_aff_from_aff(aff)); + if (rational) + pa = isl_pw_aff_set_rational(pa); + set = isl_pw_aff_zero_set(pa); + map_i = isl_map_from_range(set); + map_i = isl_map_reset_space(map_i, isl_map_get_space(map)); + map = isl_map_intersect(map, map_i); + } + + isl_space_free(space); + isl_multi_pw_aff_free(tuple); + return map; +error: + isl_space_free(space); + isl_multi_pw_aff_free(tuple); + isl_map_free(map); + return NULL; +} + +/* Read a tuple from "s" and add it to "map". + * The tuple is initially represented as an isl_multi_pw_aff and + * then added to "map". + */ +static __isl_give isl_map *read_map_tuple(__isl_keep isl_stream *s, + __isl_take isl_map *map, enum isl_dim_type type, struct vars *v, + int rational, int comma) +{ + isl_multi_pw_aff *tuple; + + tuple = read_tuple(s, v, rational, comma); + if (!tuple) + return isl_map_free(map); + + return map_from_tuple(tuple, map, type, v, rational); +} + +/* Given two equal-length lists of piecewise affine expression with the space + * of "set" as domain, construct a set in the same space that expresses + * that "left" and "right" satisfy the comparison "type". + * + * A space is constructed of the same dimension as the number of elements + * in the two lists. The comparison is then expressed in a map from + * this space to itself and wrapped into a set. Finally the two lists + * of piecewise affine expressions are plugged into this set. + * + * Let S be the space of "set" and T the constructed space. + * The lists are first changed into two isl_multi_pw_affs in S -> T and + * then combined into an isl_multi_pw_aff in S -> [T -> T], + * while the comparison is first expressed in T -> T, then [T -> T] + * and finally in S. + */ +static __isl_give isl_set *list_cmp(__isl_keep isl_set *set, int type, + __isl_take isl_pw_aff_list *left, __isl_take isl_pw_aff_list *right) +{ + isl_space *space; + int n; + isl_multi_pw_aff *mpa1, *mpa2; + + if (!set || !left || !right) + goto error; + + space = isl_set_get_space(set); + n = isl_pw_aff_list_n_pw_aff(left); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, n); + mpa1 = isl_multi_pw_aff_from_pw_aff_list(isl_space_copy(space), left); + mpa2 = isl_multi_pw_aff_from_pw_aff_list(isl_space_copy(space), right); + mpa1 = isl_multi_pw_aff_range_product(mpa1, mpa2); + + space = isl_space_range(space); + switch (type) { + case ISL_TOKEN_LEX_LT: + set = isl_map_wrap(isl_map_lex_lt(space)); + break; + case ISL_TOKEN_LEX_GT: + set = isl_map_wrap(isl_map_lex_gt(space)); + break; + case ISL_TOKEN_LEX_LE: + set = isl_map_wrap(isl_map_lex_le(space)); + break; + case ISL_TOKEN_LEX_GE: + set = isl_map_wrap(isl_map_lex_ge(space)); + break; + default: + isl_multi_pw_aff_free(mpa1); + isl_space_free(space); + isl_die(isl_set_get_ctx(set), isl_error_internal, + "unhandled list comparison type", return NULL); + } + set = isl_set_preimage_multi_pw_aff(set, mpa1); + return set; +error: + isl_pw_aff_list_free(left); + isl_pw_aff_list_free(right); + return NULL; +} + +/* Construct constraints of the form + * + * a op b + * + * where a is an element in "left", op is an operator of type "type" and + * b is an element in "right", add the constraints to "set" and return + * the result. + * "rational" is set if the constraints should be treated as + * a rational constraints. + * + * If "type" is the type of a comparison operator between lists + * of affine expressions, then a single (compound) constraint + * is constructed by list_cmp instead. + */ +static __isl_give isl_set *construct_constraints( + __isl_take isl_set *set, int type, + __isl_keep isl_pw_aff_list *left, __isl_keep isl_pw_aff_list *right, + int rational) +{ + isl_set *cond; + + left = isl_pw_aff_list_copy(left); + right = isl_pw_aff_list_copy(right); + if (rational) { + left = isl_pw_aff_list_set_rational(left); + right = isl_pw_aff_list_set_rational(right); + } + if (is_list_comparator_type(type)) + cond = list_cmp(set, type, left, right); + else if (type == ISL_TOKEN_LE) + cond = isl_pw_aff_list_le_set(left, right); + else if (type == ISL_TOKEN_GE) + cond = isl_pw_aff_list_ge_set(left, right); + else if (type == ISL_TOKEN_LT) + cond = isl_pw_aff_list_lt_set(left, right); + else if (type == ISL_TOKEN_GT) + cond = isl_pw_aff_list_gt_set(left, right); + else if (type == ISL_TOKEN_NE) + cond = isl_pw_aff_list_ne_set(left, right); + else + cond = isl_pw_aff_list_eq_set(left, right); + + return isl_set_intersect(set, cond); +} + +/* Read a constraint from "s", add it to "map" and return the result. + * "v" contains a description of the identifiers parsed so far. + * "rational" is set if the constraint should be treated as + * a rational constraint. + * The constraint read from "s" may be applied to multiple pairs + * of affine expressions and may be chained. + * In particular, a list of affine expressions is read, followed + * by a comparison operator and another list of affine expressions. + * The comparison operator is then applied to each pair of elements + * in the two lists and the results are added to "map". + * However, if the operator expects two lists of affine expressions, + * then it is applied directly to those lists and the two lists + * are required to have the same length. + * If the next token is another comparison operator, then another + * list of affine expressions is read and the process repeats. + * + * The processing is performed on a wrapped copy of "map" because + * an affine expression cannot have a binary relation as domain. + */ +static __isl_give isl_map *add_constraint(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + struct isl_token *tok; + int type; + isl_pw_aff_list *list1 = NULL, *list2 = NULL; + int n1, n2; + isl_set *set; + + set = isl_map_wrap(map); + list1 = accept_affine_list(s, isl_set_get_space(set), v); + if (!list1) + goto error; + tok = isl_stream_next_token(s); + if (!is_comparator(tok)) { + isl_stream_error(s, tok, "missing operator"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + type = tok->type; + isl_token_free(tok); + for (;;) { + list2 = accept_affine_list(s, isl_set_get_space(set), v); + if (!list2) + goto error; + n1 = isl_pw_aff_list_n_pw_aff(list1); + n2 = isl_pw_aff_list_n_pw_aff(list2); + if (is_list_comparator_type(type) && n1 != n2) { + isl_stream_error(s, NULL, + "list arguments not of same size"); + goto error; + } + + set = construct_constraints(set, type, list1, list2, rational); + isl_pw_aff_list_free(list1); + list1 = list2; + + tok = isl_stream_next_token(s); + if (!is_comparator(tok)) { + if (tok) + isl_stream_push_token(s, tok); + break; + } + type = tok->type; + isl_token_free(tok); + } + isl_pw_aff_list_free(list1); + + return isl_set_unwrap(set); +error: + isl_pw_aff_list_free(list1); + isl_pw_aff_list_free(list2); + isl_set_free(set); + return NULL; +} + +static __isl_give isl_map *read_exists(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + int n = v->n; + int seen_paren = isl_stream_eat_if_available(s, '('); + + map = isl_map_from_domain(isl_map_wrap(map)); + map = read_defined_var_list(s, v, map, rational); + + if (isl_stream_eat(s, ':')) + goto error; + + map = read_formula(s, v, map, rational); + map = isl_set_unwrap(isl_map_domain(map)); + + vars_drop(v, v->n - n); + if (seen_paren && isl_stream_eat(s, ')')) + goto error; + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Parse an expression between parentheses and push the result + * back on the stream. + * + * The parsed expression may be either an affine expression + * or a condition. The first type is pushed onto the stream + * as an isl_pw_aff, while the second is pushed as an isl_map. + * + * If the initial token indicates the start of a condition, + * we parse it as such. + * Otherwise, we first parse an affine expression and push + * that onto the stream. If the affine expression covers the + * entire expression between parentheses, we return. + * Otherwise, we assume that the affine expression is the + * start of a condition and continue parsing. + */ +static int resolve_paren_expr(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + struct isl_token *tok, *tok2; + int line, col; + isl_pw_aff *pwaff; + + tok = isl_stream_next_token(s); + if (!tok || tok->type != '(') + goto error; + + if (isl_stream_next_token_is(s, '(')) + if (resolve_paren_expr(s, v, isl_map_copy(map), rational)) + goto error; + + if (isl_stream_next_token_is(s, ISL_TOKEN_EXISTS) || + isl_stream_next_token_is(s, ISL_TOKEN_NOT) || + isl_stream_next_token_is(s, ISL_TOKEN_TRUE) || + isl_stream_next_token_is(s, ISL_TOKEN_FALSE) || + isl_stream_next_token_is(s, ISL_TOKEN_MAP)) { + map = read_formula(s, v, map, rational); + if (isl_stream_eat(s, ')')) + goto error; + tok->type = ISL_TOKEN_MAP; + tok->u.map = map; + isl_stream_push_token(s, tok); + return 0; + } + + tok2 = isl_stream_next_token(s); + if (!tok2) + goto error; + line = tok2->line; + col = tok2->col; + isl_stream_push_token(s, tok2); + + pwaff = accept_affine(s, isl_space_wrap(isl_map_get_space(map)), v); + if (!pwaff) + goto error; + + tok2 = isl_token_new(s->ctx, line, col, 0); + if (!tok2) + goto error2; + tok2->type = ISL_TOKEN_AFF; + tok2->u.pwaff = pwaff; + + if (isl_stream_eat_if_available(s, ')')) { + isl_stream_push_token(s, tok2); + isl_token_free(tok); + isl_map_free(map); + return 0; + } + + isl_stream_push_token(s, tok2); + + map = read_formula(s, v, map, rational); + if (isl_stream_eat(s, ')')) + goto error; + + tok->type = ISL_TOKEN_MAP; + tok->u.map = map; + isl_stream_push_token(s, tok); + + return 0; +error2: + isl_pw_aff_free(pwaff); +error: + isl_token_free(tok); + isl_map_free(map); + return -1; +} + +static __isl_give isl_map *read_conjunct(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + if (isl_stream_next_token_is(s, '(')) + if (resolve_paren_expr(s, v, isl_map_copy(map), rational)) + goto error; + + if (isl_stream_next_token_is(s, ISL_TOKEN_MAP)) { + struct isl_token *tok; + tok = isl_stream_next_token(s); + if (!tok) + goto error; + isl_map_free(map); + map = isl_map_copy(tok->u.map); + isl_token_free(tok); + return map; + } + + if (isl_stream_eat_if_available(s, ISL_TOKEN_EXISTS)) + return read_exists(s, v, map, rational); + + if (isl_stream_eat_if_available(s, ISL_TOKEN_TRUE)) + return map; + + if (isl_stream_eat_if_available(s, ISL_TOKEN_FALSE)) { + isl_space *dim = isl_map_get_space(map); + isl_map_free(map); + return isl_map_empty(dim); + } + + return add_constraint(s, v, map, rational); +error: + isl_map_free(map); + return NULL; +} + +static __isl_give isl_map *read_conjuncts(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + isl_map *res; + int negate; + + negate = isl_stream_eat_if_available(s, ISL_TOKEN_NOT); + res = read_conjunct(s, v, isl_map_copy(map), rational); + if (negate) + res = isl_map_subtract(isl_map_copy(map), res); + + while (res && isl_stream_eat_if_available(s, ISL_TOKEN_AND)) { + isl_map *res_i; + + negate = isl_stream_eat_if_available(s, ISL_TOKEN_NOT); + res_i = read_conjunct(s, v, isl_map_copy(map), rational); + if (negate) + res = isl_map_subtract(res, res_i); + else + res = isl_map_intersect(res, res_i); + } + + isl_map_free(map); + return res; +} + +static struct isl_map *read_disjuncts(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + isl_map *res; + + if (isl_stream_next_token_is(s, '}')) { + isl_space *dim = isl_map_get_space(map); + isl_map_free(map); + return isl_map_universe(dim); + } + + res = read_conjuncts(s, v, isl_map_copy(map), rational); + while (isl_stream_eat_if_available(s, ISL_TOKEN_OR)) { + isl_map *res_i; + + res_i = read_conjuncts(s, v, isl_map_copy(map), rational); + res = isl_map_union(res, res_i); + } + + isl_map_free(map); + return res; +} + +/* Read a first order formula from "s", add the corresponding + * constraints to "map" and return the result. + * + * In particular, read a formula of the form + * + * a + * + * or + * + * a implies b + * + * where a and b are disjunctions. + * + * In the first case, map is replaced by + * + * map \cap { [..] : a } + * + * In the second case, it is replaced by + * + * (map \setminus { [..] : a}) \cup (map \cap { [..] : b }) + */ +static __isl_give isl_map *read_formula(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_map *map, int rational) +{ + isl_map *res; + + res = read_disjuncts(s, v, isl_map_copy(map), rational); + + if (isl_stream_eat_if_available(s, ISL_TOKEN_IMPLIES)) { + isl_map *res2; + + res = isl_map_subtract(isl_map_copy(map), res); + res2 = read_disjuncts(s, v, map, rational); + res = isl_map_union(res, res2); + } else + isl_map_free(map); + + return res; +} + +static int polylib_pos_to_isl_pos(__isl_keep isl_basic_map *bmap, int pos) +{ + if (pos < isl_basic_map_dim(bmap, isl_dim_out)) + return 1 + isl_basic_map_dim(bmap, isl_dim_param) + + isl_basic_map_dim(bmap, isl_dim_in) + pos; + pos -= isl_basic_map_dim(bmap, isl_dim_out); + + if (pos < isl_basic_map_dim(bmap, isl_dim_in)) + return 1 + isl_basic_map_dim(bmap, isl_dim_param) + pos; + pos -= isl_basic_map_dim(bmap, isl_dim_in); + + if (pos < isl_basic_map_dim(bmap, isl_dim_div)) + return 1 + isl_basic_map_dim(bmap, isl_dim_param) + + isl_basic_map_dim(bmap, isl_dim_in) + + isl_basic_map_dim(bmap, isl_dim_out) + pos; + pos -= isl_basic_map_dim(bmap, isl_dim_div); + + if (pos < isl_basic_map_dim(bmap, isl_dim_param)) + return 1 + pos; + + return 0; +} + +static __isl_give isl_basic_map *basic_map_read_polylib_constraint( + __isl_keep isl_stream *s, __isl_take isl_basic_map *bmap) +{ + int j; + struct isl_token *tok; + int type; + int k; + isl_int *c; + + if (!bmap) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting coefficient"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + if (!tok->on_new_line) { + isl_stream_error(s, tok, "coefficient should appear on new line"); + isl_stream_push_token(s, tok); + goto error; + } + + type = isl_int_get_si(tok->u.v); + isl_token_free(tok); + + isl_assert(s->ctx, type == 0 || type == 1, goto error); + if (type == 0) { + k = isl_basic_map_alloc_equality(bmap); + c = bmap->eq[k]; + } else { + k = isl_basic_map_alloc_inequality(bmap); + c = bmap->ineq[k]; + } + if (k < 0) + goto error; + + for (j = 0; j < 1 + isl_basic_map_total_dim(bmap); ++j) { + int pos; + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting coefficient"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + if (tok->on_new_line) { + isl_stream_error(s, tok, + "coefficient should not appear on new line"); + isl_stream_push_token(s, tok); + goto error; + } + pos = polylib_pos_to_isl_pos(bmap, j); + isl_int_set(c[pos], tok->u.v); + isl_token_free(tok); + } + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +static __isl_give isl_basic_map *basic_map_read_polylib( + __isl_keep isl_stream *s) +{ + int i; + struct isl_token *tok; + struct isl_token *tok2; + int n_row, n_col; + int on_new_line; + unsigned in = 0, out, local = 0; + struct isl_basic_map *bmap = NULL; + int nparam = 0; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + tok2 = isl_stream_next_token(s); + if (!tok2) { + isl_token_free(tok); + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + if (tok->type != ISL_TOKEN_VALUE || tok2->type != ISL_TOKEN_VALUE) { + isl_stream_push_token(s, tok2); + isl_stream_push_token(s, tok); + isl_stream_error(s, NULL, + "expecting constraint matrix dimensions"); + return NULL; + } + n_row = isl_int_get_si(tok->u.v); + n_col = isl_int_get_si(tok2->u.v); + on_new_line = tok2->on_new_line; + isl_token_free(tok2); + isl_token_free(tok); + isl_assert(s->ctx, !on_new_line, return NULL); + isl_assert(s->ctx, n_row >= 0, return NULL); + isl_assert(s->ctx, n_col >= 2 + nparam, return NULL); + tok = isl_stream_next_token_on_same_line(s); + if (tok) { + if (tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, + "expecting number of output dimensions"); + isl_stream_push_token(s, tok); + goto error; + } + out = isl_int_get_si(tok->u.v); + isl_token_free(tok); + + tok = isl_stream_next_token_on_same_line(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, + "expecting number of input dimensions"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + in = isl_int_get_si(tok->u.v); + isl_token_free(tok); + + tok = isl_stream_next_token_on_same_line(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, + "expecting number of existentials"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + local = isl_int_get_si(tok->u.v); + isl_token_free(tok); + + tok = isl_stream_next_token_on_same_line(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, + "expecting number of parameters"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + nparam = isl_int_get_si(tok->u.v); + isl_token_free(tok); + if (n_col != 1 + out + in + local + nparam + 1) { + isl_stream_error(s, NULL, + "dimensions don't match"); + goto error; + } + } else + out = n_col - 2 - nparam; + bmap = isl_basic_map_alloc(s->ctx, nparam, in, out, local, n_row, n_row); + if (!bmap) + return NULL; + + for (i = 0; i < local; ++i) { + int k = isl_basic_map_alloc_div(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->div[k], 1 + 1 + nparam + in + out + local); + } + + for (i = 0; i < n_row; ++i) + bmap = basic_map_read_polylib_constraint(s, bmap); + + tok = isl_stream_next_token_on_same_line(s); + if (tok) { + isl_stream_error(s, tok, "unexpected extra token on line"); + isl_stream_push_token(s, tok); + goto error; + } + + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +static struct isl_map *map_read_polylib(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + struct isl_token *tok2; + int i, n; + struct isl_map *map; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + tok2 = isl_stream_next_token_on_same_line(s); + if (tok2 && tok2->type == ISL_TOKEN_VALUE) { + isl_stream_push_token(s, tok2); + isl_stream_push_token(s, tok); + return isl_map_from_basic_map(basic_map_read_polylib(s)); + } + if (tok2) { + isl_stream_error(s, tok2, "unexpected token"); + isl_stream_push_token(s, tok2); + isl_stream_push_token(s, tok); + return NULL; + } + n = isl_int_get_si(tok->u.v); + isl_token_free(tok); + + isl_assert(s->ctx, n >= 1, return NULL); + + map = isl_map_from_basic_map(basic_map_read_polylib(s)); + + for (i = 1; map && i < n; ++i) + map = isl_map_union(map, + isl_map_from_basic_map(basic_map_read_polylib(s))); + + return map; +} + +static int optional_power(__isl_keep isl_stream *s) +{ + int pow; + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return 1; + if (tok->type != '^') { + isl_stream_push_token(s, tok); + return 1; + } + isl_token_free(tok); + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting exponent"); + if (tok) + isl_stream_push_token(s, tok); + return 1; + } + pow = isl_int_get_si(tok->u.v); + isl_token_free(tok); + return pow; +} + +static __isl_give isl_pw_qpolynomial *read_term(__isl_keep isl_stream *s, + __isl_keep isl_map *map, struct vars *v); + +static __isl_give isl_pw_qpolynomial *read_factor(__isl_keep isl_stream *s, + __isl_keep isl_map *map, struct vars *v) +{ + isl_pw_qpolynomial *pwqp; + struct isl_token *tok; + + tok = next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + if (tok->type == '(') { + int pow; + + isl_token_free(tok); + pwqp = read_term(s, map, v); + if (!pwqp) + return NULL; + if (isl_stream_eat(s, ')')) + goto error; + pow = optional_power(s); + pwqp = isl_pw_qpolynomial_pow(pwqp, pow); + } else if (tok->type == ISL_TOKEN_VALUE) { + struct isl_token *tok2; + isl_qpolynomial *qp; + + tok2 = isl_stream_next_token(s); + if (tok2 && tok2->type == '/') { + isl_token_free(tok2); + tok2 = next_token(s); + if (!tok2 || tok2->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok2, "expected denominator"); + isl_token_free(tok); + isl_token_free(tok2); + return NULL; + } + qp = isl_qpolynomial_rat_cst_on_domain(isl_map_get_space(map), + tok->u.v, tok2->u.v); + isl_token_free(tok2); + } else { + isl_stream_push_token(s, tok2); + qp = isl_qpolynomial_cst_on_domain(isl_map_get_space(map), + tok->u.v); + } + isl_token_free(tok); + pwqp = isl_pw_qpolynomial_from_qpolynomial(qp); + } else if (tok->type == ISL_TOKEN_INFTY) { + isl_qpolynomial *qp; + isl_token_free(tok); + qp = isl_qpolynomial_infty_on_domain(isl_map_get_space(map)); + pwqp = isl_pw_qpolynomial_from_qpolynomial(qp); + } else if (tok->type == ISL_TOKEN_NAN) { + isl_qpolynomial *qp; + isl_token_free(tok); + qp = isl_qpolynomial_nan_on_domain(isl_map_get_space(map)); + pwqp = isl_pw_qpolynomial_from_qpolynomial(qp); + } else if (tok->type == ISL_TOKEN_IDENT) { + int n = v->n; + int pos = vars_pos(v, tok->u.s, -1); + int pow; + isl_qpolynomial *qp; + if (pos < 0) { + isl_token_free(tok); + return NULL; + } + if (pos >= n) { + vars_drop(v, v->n - n); + isl_stream_error(s, tok, "unknown identifier"); + isl_token_free(tok); + return NULL; + } + isl_token_free(tok); + pow = optional_power(s); + qp = isl_qpolynomial_var_pow_on_domain(isl_map_get_space(map), pos, pow); + pwqp = isl_pw_qpolynomial_from_qpolynomial(qp); + } else if (is_start_of_div(tok)) { + isl_pw_aff *pwaff; + int pow; + + isl_stream_push_token(s, tok); + pwaff = accept_div(s, isl_map_get_space(map), v); + pow = optional_power(s); + pwqp = isl_pw_qpolynomial_from_pw_aff(pwaff); + pwqp = isl_pw_qpolynomial_pow(pwqp, pow); + } else if (tok->type == '-') { + isl_token_free(tok); + pwqp = read_factor(s, map, v); + pwqp = isl_pw_qpolynomial_neg(pwqp); + } else { + isl_stream_error(s, tok, "unexpected isl_token"); + isl_stream_push_token(s, tok); + return NULL; + } + + if (isl_stream_eat_if_available(s, '*') || + isl_stream_next_token_is(s, ISL_TOKEN_IDENT)) { + isl_pw_qpolynomial *pwqp2; + + pwqp2 = read_factor(s, map, v); + pwqp = isl_pw_qpolynomial_mul(pwqp, pwqp2); + } + + return pwqp; +error: + isl_pw_qpolynomial_free(pwqp); + return NULL; +} + +static __isl_give isl_pw_qpolynomial *read_term(__isl_keep isl_stream *s, + __isl_keep isl_map *map, struct vars *v) +{ + struct isl_token *tok; + isl_pw_qpolynomial *pwqp; + + pwqp = read_factor(s, map, v); + + for (;;) { + tok = next_token(s); + if (!tok) + return pwqp; + + if (tok->type == '+') { + isl_pw_qpolynomial *pwqp2; + + isl_token_free(tok); + pwqp2 = read_factor(s, map, v); + pwqp = isl_pw_qpolynomial_add(pwqp, pwqp2); + } else if (tok->type == '-') { + isl_pw_qpolynomial *pwqp2; + + isl_token_free(tok); + pwqp2 = read_factor(s, map, v); + pwqp = isl_pw_qpolynomial_sub(pwqp, pwqp2); + } else if (tok->type == ISL_TOKEN_VALUE && + isl_int_is_neg(tok->u.v)) { + isl_pw_qpolynomial *pwqp2; + + isl_stream_push_token(s, tok); + pwqp2 = read_factor(s, map, v); + pwqp = isl_pw_qpolynomial_add(pwqp, pwqp2); + } else { + isl_stream_push_token(s, tok); + break; + } + } + + return pwqp; +} + +static __isl_give isl_map *read_optional_formula(__isl_keep isl_stream *s, + __isl_take isl_map *map, struct vars *v, int rational) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + if (tok->type == ':' || + (tok->type == ISL_TOKEN_OR && !strcmp(tok->u.s, "|"))) { + isl_token_free(tok); + map = read_formula(s, v, map, rational); + } else + isl_stream_push_token(s, tok); + + return map; +error: + isl_map_free(map); + return NULL; +} + +static struct isl_obj obj_read_poly(__isl_keep isl_stream *s, + __isl_take isl_map *map, struct vars *v, int n) +{ + struct isl_obj obj = { isl_obj_pw_qpolynomial, NULL }; + isl_pw_qpolynomial *pwqp; + struct isl_set *set; + + pwqp = read_term(s, map, v); + map = read_optional_formula(s, map, v, 0); + set = isl_map_range(map); + + pwqp = isl_pw_qpolynomial_intersect_domain(pwqp, set); + + vars_drop(v, v->n - n); + + obj.v = pwqp; + return obj; +} + +static struct isl_obj obj_read_poly_or_fold(__isl_keep isl_stream *s, + __isl_take isl_set *set, struct vars *v, int n) +{ + struct isl_obj obj = { isl_obj_pw_qpolynomial_fold, NULL }; + isl_pw_qpolynomial *pwqp; + isl_pw_qpolynomial_fold *pwf = NULL; + + if (!isl_stream_eat_if_available(s, ISL_TOKEN_MAX)) + return obj_read_poly(s, set, v, n); + + if (isl_stream_eat(s, '(')) + goto error; + + pwqp = read_term(s, set, v); + pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial(isl_fold_max, pwqp); + + while (isl_stream_eat_if_available(s, ',')) { + isl_pw_qpolynomial_fold *pwf_i; + pwqp = read_term(s, set, v); + pwf_i = isl_pw_qpolynomial_fold_from_pw_qpolynomial(isl_fold_max, + pwqp); + pwf = isl_pw_qpolynomial_fold_fold(pwf, pwf_i); + } + + if (isl_stream_eat(s, ')')) + goto error; + + set = read_optional_formula(s, set, v, 0); + pwf = isl_pw_qpolynomial_fold_intersect_domain(pwf, set); + + vars_drop(v, v->n - n); + + obj.v = pwf; + return obj; +error: + isl_set_free(set); + isl_pw_qpolynomial_fold_free(pwf); + obj.type = isl_obj_none; + return obj; +} + +static int is_rational(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type == ISL_TOKEN_RAT && isl_stream_next_token_is(s, ':')) { + isl_token_free(tok); + isl_stream_eat(s, ':'); + return 1; + } + + isl_stream_push_token(s, tok); + + return 0; +} + +static struct isl_obj obj_read_body(__isl_keep isl_stream *s, + __isl_take isl_map *map, struct vars *v) +{ + struct isl_token *tok; + struct isl_obj obj = { isl_obj_set, NULL }; + int n = v->n; + int rational; + + rational = is_rational(s); + if (rational) + map = isl_map_set_rational(map); + + if (isl_stream_next_token_is(s, ':')) { + obj.type = isl_obj_set; + obj.v = read_optional_formula(s, map, v, rational); + return obj; + } + + if (!next_is_tuple(s)) + return obj_read_poly_or_fold(s, map, v, n); + + map = read_map_tuple(s, map, isl_dim_in, v, rational, 0); + if (!map) + goto error; + tok = isl_stream_next_token(s); + if (!tok) + goto error; + if (tok->type == ISL_TOKEN_TO) { + obj.type = isl_obj_map; + isl_token_free(tok); + if (!next_is_tuple(s)) { + isl_set *set = isl_map_domain(map); + return obj_read_poly_or_fold(s, set, v, n); + } + map = read_map_tuple(s, map, isl_dim_out, v, rational, 0); + if (!map) + goto error; + } else { + map = isl_map_domain(map); + isl_stream_push_token(s, tok); + } + + map = read_optional_formula(s, map, v, rational); + + vars_drop(v, v->n - n); + + obj.v = map; + return obj; +error: + isl_map_free(map); + obj.type = isl_obj_none; + return obj; +} + +static struct isl_obj to_union(isl_ctx *ctx, struct isl_obj obj) +{ + if (obj.type == isl_obj_map) { + obj.v = isl_union_map_from_map(obj.v); + obj.type = isl_obj_union_map; + } else if (obj.type == isl_obj_set) { + obj.v = isl_union_set_from_set(obj.v); + obj.type = isl_obj_union_set; + } else if (obj.type == isl_obj_pw_qpolynomial) { + obj.v = isl_union_pw_qpolynomial_from_pw_qpolynomial(obj.v); + obj.type = isl_obj_union_pw_qpolynomial; + } else if (obj.type == isl_obj_pw_qpolynomial_fold) { + obj.v = isl_union_pw_qpolynomial_fold_from_pw_qpolynomial_fold(obj.v); + obj.type = isl_obj_union_pw_qpolynomial_fold; + } else + isl_assert(ctx, 0, goto error); + return obj; +error: + obj.type->free(obj.v); + obj.type = isl_obj_none; + return obj; +} + +static struct isl_obj obj_add(__isl_keep isl_stream *s, + struct isl_obj obj1, struct isl_obj obj2) +{ + if (obj1.type == isl_obj_set && obj2.type == isl_obj_union_set) + obj1 = to_union(s->ctx, obj1); + if (obj1.type == isl_obj_union_set && obj2.type == isl_obj_set) + obj2 = to_union(s->ctx, obj2); + if (obj1.type == isl_obj_map && obj2.type == isl_obj_union_map) + obj1 = to_union(s->ctx, obj1); + if (obj1.type == isl_obj_union_map && obj2.type == isl_obj_map) + obj2 = to_union(s->ctx, obj2); + if (obj1.type == isl_obj_pw_qpolynomial && + obj2.type == isl_obj_union_pw_qpolynomial) + obj1 = to_union(s->ctx, obj1); + if (obj1.type == isl_obj_union_pw_qpolynomial && + obj2.type == isl_obj_pw_qpolynomial) + obj2 = to_union(s->ctx, obj2); + if (obj1.type == isl_obj_pw_qpolynomial_fold && + obj2.type == isl_obj_union_pw_qpolynomial_fold) + obj1 = to_union(s->ctx, obj1); + if (obj1.type == isl_obj_union_pw_qpolynomial_fold && + obj2.type == isl_obj_pw_qpolynomial_fold) + obj2 = to_union(s->ctx, obj2); + if (obj1.type != obj2.type) { + isl_stream_error(s, NULL, + "attempt to combine incompatible objects"); + goto error; + } + if (!obj1.type->add) + isl_die(s->ctx, isl_error_internal, + "combination not supported on object type", goto error); + if (obj1.type == isl_obj_map && !isl_map_has_equal_space(obj1.v, obj2.v)) { + obj1 = to_union(s->ctx, obj1); + obj2 = to_union(s->ctx, obj2); + } + if (obj1.type == isl_obj_set && !isl_set_has_equal_space(obj1.v, obj2.v)) { + obj1 = to_union(s->ctx, obj1); + obj2 = to_union(s->ctx, obj2); + } + if (obj1.type == isl_obj_pw_qpolynomial && + !isl_pw_qpolynomial_has_equal_space(obj1.v, obj2.v)) { + obj1 = to_union(s->ctx, obj1); + obj2 = to_union(s->ctx, obj2); + } + if (obj1.type == isl_obj_pw_qpolynomial_fold && + !isl_pw_qpolynomial_fold_has_equal_space(obj1.v, obj2.v)) { + obj1 = to_union(s->ctx, obj1); + obj2 = to_union(s->ctx, obj2); + } + obj1.v = obj1.type->add(obj1.v, obj2.v); + return obj1; +error: + obj1.type->free(obj1.v); + obj2.type->free(obj2.v); + obj1.type = isl_obj_none; + obj1.v = NULL; + return obj1; +} + +/* Are the first two tokens on "s", "domain" (either as a string + * or as an identifier) followed by ":"? + */ +static int next_is_domain_colon(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + char *name; + int res; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type != ISL_TOKEN_IDENT && tok->type != ISL_TOKEN_STRING) { + isl_stream_push_token(s, tok); + return 0; + } + + name = isl_token_get_str(s->ctx, tok); + res = !strcmp(name, "domain") && isl_stream_next_token_is(s, ':'); + free(name); + + isl_stream_push_token(s, tok); + + return res; +} + +/* Do the first tokens on "s" look like a schedule? + * + * The root of a schedule is always a domain node, so the first thing + * we expect in the stream is a domain key, i.e., "domain" followed + * by ":". If the schedule was printed in YAML flow style, then + * we additionally expect a "{" to open the outer mapping. + */ +static int next_is_schedule(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + int is_schedule; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type != '{') { + isl_stream_push_token(s, tok); + return next_is_domain_colon(s); + } + + is_schedule = next_is_domain_colon(s); + isl_stream_push_token(s, tok); + + return is_schedule; +} + +/* Read an isl_schedule from "s" and store it in an isl_obj. + */ +static struct isl_obj schedule_read(__isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj.type = isl_obj_schedule; + obj.v = isl_stream_read_schedule(s); + + return obj; +} + +static struct isl_obj obj_read(__isl_keep isl_stream *s) +{ + isl_map *map = NULL; + struct isl_token *tok; + struct vars *v = NULL; + struct isl_obj obj = { isl_obj_set, NULL }; + + if (next_is_schedule(s)) + return schedule_read(s); + + tok = next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + if (tok->type == ISL_TOKEN_VALUE) { + struct isl_token *tok2; + struct isl_map *map; + + tok2 = isl_stream_next_token(s); + if (!tok2 || tok2->type != ISL_TOKEN_VALUE || + isl_int_is_neg(tok2->u.v)) { + if (tok2) + isl_stream_push_token(s, tok2); + obj.type = isl_obj_val; + obj.v = isl_val_int_from_isl_int(s->ctx, tok->u.v); + isl_token_free(tok); + return obj; + } + isl_stream_push_token(s, tok2); + isl_stream_push_token(s, tok); + map = map_read_polylib(s); + if (!map) + goto error; + if (isl_map_may_be_set(map)) + obj.v = isl_map_range(map); + else { + obj.type = isl_obj_map; + obj.v = map; + } + return obj; + } + v = vars_new(s->ctx); + if (!v) { + isl_stream_push_token(s, tok); + goto error; + } + map = isl_map_universe(isl_space_params_alloc(s->ctx, 0)); + if (tok->type == '[') { + isl_stream_push_token(s, tok); + map = read_map_tuple(s, map, isl_dim_param, v, 0, 0); + if (!map) + goto error; + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_TO) { + isl_stream_error(s, tok, "expecting '->'"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + isl_token_free(tok); + tok = isl_stream_next_token(s); + } + if (!tok || tok->type != '{') { + isl_stream_error(s, tok, "expecting '{'"); + if (tok) + isl_stream_push_token(s, tok); + goto error; + } + isl_token_free(tok); + + tok = isl_stream_next_token(s); + if (!tok) + ; + else if (tok->type == ISL_TOKEN_IDENT && !strcmp(tok->u.s, "Sym")) { + isl_token_free(tok); + if (isl_stream_eat(s, '=')) + goto error; + map = read_map_tuple(s, map, isl_dim_param, v, 0, 1); + if (!map) + goto error; + } else if (tok->type == '}') { + obj.type = isl_obj_union_set; + obj.v = isl_union_set_empty(isl_map_get_space(map)); + isl_token_free(tok); + goto done; + } else + isl_stream_push_token(s, tok); + + for (;;) { + struct isl_obj o; + tok = NULL; + o = obj_read_body(s, isl_map_copy(map), v); + if (o.type == isl_obj_none || !o.v) + goto error; + if (!obj.v) + obj = o; + else { + obj = obj_add(s, obj, o); + if (obj.type == isl_obj_none || !obj.v) + goto error; + } + tok = isl_stream_next_token(s); + if (!tok || tok->type != ';') + break; + isl_token_free(tok); + if (isl_stream_next_token_is(s, '}')) { + tok = isl_stream_next_token(s); + break; + } + } + + if (tok && tok->type == '}') { + isl_token_free(tok); + } else { + isl_stream_error(s, tok, "unexpected isl_token"); + if (tok) + isl_token_free(tok); + goto error; + } +done: + vars_free(v); + isl_map_free(map); + + return obj; +error: + isl_map_free(map); + obj.type->free(obj.v); + if (v) + vars_free(v); + obj.v = NULL; + return obj; +} + +struct isl_obj isl_stream_read_obj(__isl_keep isl_stream *s) +{ + return obj_read(s); +} + +__isl_give isl_map *isl_stream_read_map(__isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (obj.v) + isl_assert(s->ctx, obj.type == isl_obj_map || + obj.type == isl_obj_set, goto error); + + if (obj.type == isl_obj_set) + obj.v = isl_map_from_range(obj.v); + + return obj.v; +error: + obj.type->free(obj.v); + return NULL; +} + +__isl_give isl_set *isl_stream_read_set(__isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (obj.v) { + if (obj.type == isl_obj_map && isl_map_may_be_set(obj.v)) { + obj.v = isl_map_range(obj.v); + obj.type = isl_obj_set; + } + isl_assert(s->ctx, obj.type == isl_obj_set, goto error); + } + + return obj.v; +error: + obj.type->free(obj.v); + return NULL; +} + +__isl_give isl_union_map *isl_stream_read_union_map(__isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (obj.type == isl_obj_map) { + obj.type = isl_obj_union_map; + obj.v = isl_union_map_from_map(obj.v); + } + if (obj.type == isl_obj_set) { + obj.type = isl_obj_union_set; + obj.v = isl_union_set_from_set(obj.v); + } + if (obj.v && obj.type == isl_obj_union_set && + isl_union_set_is_empty(obj.v)) + obj.type = isl_obj_union_map; + if (obj.v && obj.type != isl_obj_union_map) + isl_die(s->ctx, isl_error_invalid, "invalid input", goto error); + + return obj.v; +error: + obj.type->free(obj.v); + return NULL; +} + +__isl_give isl_union_set *isl_stream_read_union_set(__isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (obj.type == isl_obj_set) { + obj.type = isl_obj_union_set; + obj.v = isl_union_set_from_set(obj.v); + } + if (obj.v) + isl_assert(s->ctx, obj.type == isl_obj_union_set, goto error); + + return obj.v; +error: + obj.type->free(obj.v); + return NULL; +} + +static __isl_give isl_basic_map *basic_map_read(__isl_keep isl_stream *s) +{ + struct isl_obj obj; + struct isl_map *map; + struct isl_basic_map *bmap; + + obj = obj_read(s); + if (obj.v && (obj.type != isl_obj_map && obj.type != isl_obj_set)) + isl_die(s->ctx, isl_error_invalid, "not a (basic) set or map", + goto error); + map = obj.v; + if (!map) + return NULL; + + if (map->n > 1) + isl_die(s->ctx, isl_error_invalid, + "set or map description involves " + "more than one disjunct", goto error); + + if (map->n == 0) + bmap = isl_basic_map_empty(isl_map_get_space(map)); + else + bmap = isl_basic_map_copy(map->p[0]); + + isl_map_free(map); + + return bmap; +error: + obj.type->free(obj.v); + return NULL; +} + +static __isl_give isl_basic_set *basic_set_read(__isl_keep isl_stream *s) +{ + isl_basic_map *bmap; + bmap = basic_map_read(s); + if (!bmap) + return NULL; + if (!isl_basic_map_may_be_set(bmap)) + isl_die(s->ctx, isl_error_invalid, + "input is not a set", goto error); + return isl_basic_map_range(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_read_from_file(isl_ctx *ctx, + FILE *input) +{ + struct isl_basic_map *bmap; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + bmap = basic_map_read(s); + isl_stream_free(s); + return bmap; +} + +__isl_give isl_basic_set *isl_basic_set_read_from_file(isl_ctx *ctx, + FILE *input) +{ + isl_basic_set *bset; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + bset = basic_set_read(s); + isl_stream_free(s); + return bset; +} + +struct isl_basic_map *isl_basic_map_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + struct isl_basic_map *bmap; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + bmap = basic_map_read(s); + isl_stream_free(s); + return bmap; +} + +struct isl_basic_set *isl_basic_set_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + isl_basic_set *bset; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + bset = basic_set_read(s); + isl_stream_free(s); + return bset; +} + +__isl_give isl_map *isl_map_read_from_file(struct isl_ctx *ctx, + FILE *input) +{ + struct isl_map *map; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + map = isl_stream_read_map(s); + isl_stream_free(s); + return map; +} + +__isl_give isl_map *isl_map_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + struct isl_map *map; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + map = isl_stream_read_map(s); + isl_stream_free(s); + return map; +} + +__isl_give isl_set *isl_set_read_from_file(struct isl_ctx *ctx, + FILE *input) +{ + isl_set *set; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + set = isl_stream_read_set(s); + isl_stream_free(s); + return set; +} + +struct isl_set *isl_set_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + isl_set *set; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + set = isl_stream_read_set(s); + isl_stream_free(s); + return set; +} + +__isl_give isl_union_map *isl_union_map_read_from_file(isl_ctx *ctx, + FILE *input) +{ + isl_union_map *umap; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + umap = isl_stream_read_union_map(s); + isl_stream_free(s); + return umap; +} + +__isl_give isl_union_map *isl_union_map_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + isl_union_map *umap; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + umap = isl_stream_read_union_map(s); + isl_stream_free(s); + return umap; +} + +__isl_give isl_union_set *isl_union_set_read_from_file(isl_ctx *ctx, + FILE *input) +{ + isl_union_set *uset; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + uset = isl_stream_read_union_set(s); + isl_stream_free(s); + return uset; +} + +__isl_give isl_union_set *isl_union_set_read_from_str(struct isl_ctx *ctx, + const char *str) +{ + isl_union_set *uset; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + uset = isl_stream_read_union_set(s); + isl_stream_free(s); + return uset; +} + +static __isl_give isl_vec *isl_vec_read_polylib(__isl_keep isl_stream *s) +{ + struct isl_vec *vec = NULL; + struct isl_token *tok; + unsigned size; + int j; + + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting vector length"); + goto error; + } + + size = isl_int_get_si(tok->u.v); + isl_token_free(tok); + + vec = isl_vec_alloc(s->ctx, size); + + for (j = 0; j < size; ++j) { + tok = isl_stream_next_token(s); + if (!tok || tok->type != ISL_TOKEN_VALUE) { + isl_stream_error(s, tok, "expecting constant value"); + goto error; + } + isl_int_set(vec->el[j], tok->u.v); + isl_token_free(tok); + } + + return vec; +error: + isl_token_free(tok); + isl_vec_free(vec); + return NULL; +} + +static __isl_give isl_vec *vec_read(__isl_keep isl_stream *s) +{ + return isl_vec_read_polylib(s); +} + +__isl_give isl_vec *isl_vec_read_from_file(isl_ctx *ctx, FILE *input) +{ + isl_vec *v; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + v = vec_read(s); + isl_stream_free(s); + return v; +} + +__isl_give isl_pw_qpolynomial *isl_stream_read_pw_qpolynomial( + __isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (obj.v) + isl_assert(s->ctx, obj.type == isl_obj_pw_qpolynomial, + goto error); + + return obj.v; +error: + obj.type->free(obj.v); + return NULL; +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_str(isl_ctx *ctx, + const char *str) +{ + isl_pw_qpolynomial *pwqp; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + pwqp = isl_stream_read_pw_qpolynomial(s); + isl_stream_free(s); + return pwqp; +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_file(isl_ctx *ctx, + FILE *input) +{ + isl_pw_qpolynomial *pwqp; + isl_stream *s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + pwqp = isl_stream_read_pw_qpolynomial(s); + isl_stream_free(s); + return pwqp; +} + +/* Is the next token an identifer not in "v"? + */ +static int next_is_fresh_ident(__isl_keep isl_stream *s, struct vars *v) +{ + int n = v->n; + int fresh; + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + fresh = tok->type == ISL_TOKEN_IDENT && vars_pos(v, tok->u.s, -1) >= n; + isl_stream_push_token(s, tok); + + vars_drop(v, v->n - n); + + return fresh; +} + +/* First read the domain of the affine expression, which may be + * a parameter space or a set. + * The tricky part is that we don't know if the domain is a set or not, + * so when we are trying to read the domain, we may actually be reading + * the affine expression itself (defined on a parameter domains) + * If the tuple we are reading is named, we assume it's the domain. + * Also, if inside the tuple, the first thing we find is a nested tuple + * or a new identifier, we again assume it's the domain. + * Otherwise, we assume we are reading an affine expression. + */ +static __isl_give isl_set *read_aff_domain(__isl_keep isl_stream *s, + __isl_take isl_set *dom, struct vars *v) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (tok && (tok->type == ISL_TOKEN_IDENT || tok->is_keyword)) { + isl_stream_push_token(s, tok); + return read_map_tuple(s, dom, isl_dim_set, v, 1, 0); + } + if (!tok || tok->type != '[') { + isl_stream_error(s, tok, "expecting '['"); + goto error; + } + if (next_is_tuple(s) || next_is_fresh_ident(s, v)) { + isl_stream_push_token(s, tok); + dom = read_map_tuple(s, dom, isl_dim_set, v, 1, 0); + } else + isl_stream_push_token(s, tok); + + return dom; +error: + if (tok) + isl_stream_push_token(s, tok); + isl_set_free(dom); + return NULL; +} + +/* Read an affine expression from "s". + */ +__isl_give isl_aff *isl_stream_read_aff(__isl_keep isl_stream *s) +{ + isl_aff *aff; + isl_multi_aff *ma; + + ma = isl_stream_read_multi_aff(s); + if (!ma) + return NULL; + if (isl_multi_aff_dim(ma, isl_dim_out) != 1) + isl_die(s->ctx, isl_error_invalid, + "expecting single affine expression", + goto error); + + aff = isl_multi_aff_get_aff(ma, 0); + isl_multi_aff_free(ma); + return aff; +error: + isl_multi_aff_free(ma); + return NULL; +} + +/* Read a piecewise affine expression from "s" with domain (space) "dom". + */ +static __isl_give isl_pw_aff *read_pw_aff_with_dom(__isl_keep isl_stream *s, + __isl_take isl_set *dom, struct vars *v) +{ + isl_pw_aff *pwaff = NULL; + + if (!isl_set_is_params(dom) && isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + + if (isl_stream_eat(s, '[')) + goto error; + + pwaff = accept_affine(s, isl_set_get_space(dom), v); + + if (isl_stream_eat(s, ']')) + goto error; + + dom = read_optional_formula(s, dom, v, 0); + pwaff = isl_pw_aff_intersect_domain(pwaff, dom); + + return pwaff; +error: + isl_set_free(dom); + isl_pw_aff_free(pwaff); + return NULL; +} + +__isl_give isl_pw_aff *isl_stream_read_pw_aff(__isl_keep isl_stream *s) +{ + struct vars *v; + isl_set *dom = NULL; + isl_set *aff_dom; + isl_pw_aff *pa = NULL; + int n; + + v = vars_new(s->ctx); + if (!v) + return NULL; + + dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0)); + if (next_is_tuple(s)) { + dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + } + if (isl_stream_eat(s, '{')) + goto error; + + n = v->n; + aff_dom = read_aff_domain(s, isl_set_copy(dom), v); + pa = read_pw_aff_with_dom(s, aff_dom, v); + vars_drop(v, v->n - n); + + while (isl_stream_eat_if_available(s, ';')) { + isl_pw_aff *pa_i; + + n = v->n; + aff_dom = read_aff_domain(s, isl_set_copy(dom), v); + pa_i = read_pw_aff_with_dom(s, aff_dom, v); + vars_drop(v, v->n - n); + + pa = isl_pw_aff_union_add(pa, pa_i); + } + + if (isl_stream_eat(s, '}')) + goto error; + + vars_free(v); + isl_set_free(dom); + return pa; +error: + vars_free(v); + isl_set_free(dom); + isl_pw_aff_free(pa); + return NULL; +} + +__isl_give isl_aff *isl_aff_read_from_str(isl_ctx *ctx, const char *str) +{ + isl_aff *aff; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + aff = isl_stream_read_aff(s); + isl_stream_free(s); + return aff; +} + +__isl_give isl_pw_aff *isl_pw_aff_read_from_str(isl_ctx *ctx, const char *str) +{ + isl_pw_aff *pa; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + pa = isl_stream_read_pw_aff(s); + isl_stream_free(s); + return pa; +} + +/* Read an isl_pw_multi_aff from "s". + * We currently read a generic object and if it turns out to be a set or + * a map, we convert that to an isl_pw_multi_aff. + * It would be more efficient if we were to construct the isl_pw_multi_aff + * directly. + */ +__isl_give isl_pw_multi_aff *isl_stream_read_pw_multi_aff( + __isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (!obj.v) + return NULL; + + if (obj.type == isl_obj_map) + return isl_pw_multi_aff_from_map(obj.v); + if (obj.type == isl_obj_set) + return isl_pw_multi_aff_from_set(obj.v); + + obj.type->free(obj.v); + isl_die(s->ctx, isl_error_invalid, "unexpected object type", + return NULL); +} + +__isl_give isl_pw_multi_aff *isl_pw_multi_aff_read_from_str(isl_ctx *ctx, + const char *str) +{ + isl_pw_multi_aff *pma; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + pma = isl_stream_read_pw_multi_aff(s); + isl_stream_free(s); + return pma; +} + +/* Read an isl_union_pw_multi_aff from "s". + * We currently read a generic object and if it turns out to be a set or + * a map, we convert that to an isl_union_pw_multi_aff. + * It would be more efficient if we were to construct + * the isl_union_pw_multi_aff directly. + */ +__isl_give isl_union_pw_multi_aff *isl_stream_read_union_pw_multi_aff( + __isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (!obj.v) + return NULL; + + if (obj.type == isl_obj_map || obj.type == isl_obj_set) + obj = to_union(s->ctx, obj); + if (obj.type == isl_obj_union_map) + return isl_union_pw_multi_aff_from_union_map(obj.v); + if (obj.type == isl_obj_union_set) + return isl_union_pw_multi_aff_from_union_set(obj.v); + + obj.type->free(obj.v); + isl_die(s->ctx, isl_error_invalid, "unexpected object type", + return NULL); +} + +/* Read an isl_union_pw_multi_aff from "str". + */ +__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_read_from_str( + isl_ctx *ctx, const char *str) +{ + isl_union_pw_multi_aff *upma; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + upma = isl_stream_read_union_pw_multi_aff(s); + isl_stream_free(s); + return upma; +} + +/* Assuming "pa" represents a single affine expression defined on a universe + * domain, extract this affine expression. + */ +static __isl_give isl_aff *aff_from_pw_aff(__isl_take isl_pw_aff *pa) +{ + isl_aff *aff; + + if (!pa) + return NULL; + if (pa->n != 1) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "expecting single affine expression", + goto error); + if (!isl_set_plain_is_universe(pa->p[0].set)) + isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid, + "expecting universe domain", + goto error); + + aff = isl_aff_copy(pa->p[0].aff); + isl_pw_aff_free(pa); + return aff; +error: + isl_pw_aff_free(pa); + return NULL; +} + +/* This function is called for each element in a tuple inside + * isl_stream_read_multi_val. + * Read an isl_val from "s" and add it to *list. + */ +static __isl_give isl_space *read_val_el(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, void *user) +{ + isl_val_list **list = (isl_val_list **) user; + isl_val *val; + + val = isl_stream_read_val(s); + *list = isl_val_list_add(*list, val); + if (!*list) + return isl_space_free(space); + + return space; +} + +/* Read an isl_multi_val from "s". + * + * We first read a tuple space, collecting the element values in a list. + * Then we create an isl_multi_val from the space and the isl_val_list. + */ +__isl_give isl_multi_val *isl_stream_read_multi_val(__isl_keep isl_stream *s) +{ + struct vars *v; + isl_set *dom = NULL; + isl_space *space; + isl_multi_val *mv = NULL; + isl_val_list *list; + + v = vars_new(s->ctx); + if (!v) + return NULL; + + dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0)); + if (next_is_tuple(s)) { + dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + } + if (!isl_set_plain_is_universe(dom)) + isl_die(s->ctx, isl_error_invalid, + "expecting universe parameter domain", goto error); + if (isl_stream_eat(s, '{')) + goto error; + + space = isl_set_get_space(dom); + + list = isl_val_list_alloc(s->ctx, 0); + space = read_tuple_space(s, v, space, 1, 0, &read_val_el, &list); + mv = isl_multi_val_from_val_list(space, list); + + if (isl_stream_eat(s, '}')) + goto error; + + vars_free(v); + isl_set_free(dom); + return mv; +error: + vars_free(v); + isl_set_free(dom); + isl_multi_val_free(mv); + return NULL; +} + +/* Read an isl_multi_val from "str". + */ +__isl_give isl_multi_val *isl_multi_val_read_from_str(isl_ctx *ctx, + const char *str) +{ + isl_multi_val *mv; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + mv = isl_stream_read_multi_val(s); + isl_stream_free(s); + return mv; +} + +/* Read a multi-affine expression from "s". + * If the multi-affine expression has a domain, then the tuple + * representing this domain cannot involve any affine expressions. + * The tuple representing the actual expressions needs to consist + * of only affine expressions. Moreover, these expressions can + * only depend on parameters and input dimensions and not on other + * output dimensions. + */ +__isl_give isl_multi_aff *isl_stream_read_multi_aff(__isl_keep isl_stream *s) +{ + struct vars *v; + isl_set *dom = NULL; + isl_multi_pw_aff *tuple = NULL; + int dim, i, n; + isl_space *space, *dom_space; + isl_multi_aff *ma = NULL; + + v = vars_new(s->ctx); + if (!v) + return NULL; + + dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0)); + if (next_is_tuple(s)) { + dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + } + if (!isl_set_plain_is_universe(dom)) + isl_die(s->ctx, isl_error_invalid, + "expecting universe parameter domain", goto error); + if (isl_stream_eat(s, '{')) + goto error; + + tuple = read_tuple(s, v, 0, 0); + if (!tuple) + goto error; + if (isl_stream_eat_if_available(s, ISL_TOKEN_TO)) { + isl_set *set; + isl_space *space; + int has_expr; + + has_expr = tuple_has_expr(tuple); + if (has_expr < 0) + goto error; + if (has_expr) + isl_die(s->ctx, isl_error_invalid, + "expecting universe domain", goto error); + space = isl_space_range(isl_multi_pw_aff_get_space(tuple)); + set = isl_set_universe(space); + dom = isl_set_intersect_params(set, dom); + isl_multi_pw_aff_free(tuple); + tuple = read_tuple(s, v, 0, 0); + if (!tuple) + goto error; + } + + if (isl_stream_eat(s, '}')) + goto error; + + n = isl_multi_pw_aff_dim(tuple, isl_dim_out); + dim = isl_set_dim(dom, isl_dim_all); + dom_space = isl_set_get_space(dom); + space = isl_space_range(isl_multi_pw_aff_get_space(tuple)); + space = isl_space_align_params(space, isl_space_copy(dom_space)); + if (!isl_space_is_params(dom_space)) + space = isl_space_map_from_domain_and_range( + isl_space_copy(dom_space), space); + isl_space_free(dom_space); + ma = isl_multi_aff_alloc(space); + + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + isl_aff *aff; + pa = isl_multi_pw_aff_get_pw_aff(tuple, i); + aff = aff_from_pw_aff(pa); + if (!aff) + goto error; + if (isl_aff_involves_dims(aff, isl_dim_in, dim, i + 1)) { + isl_aff_free(aff); + isl_die(s->ctx, isl_error_invalid, + "not an affine expression", goto error); + } + aff = isl_aff_drop_dims(aff, isl_dim_in, dim, n); + space = isl_multi_aff_get_domain_space(ma); + aff = isl_aff_reset_domain_space(aff, space); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + + isl_multi_pw_aff_free(tuple); + vars_free(v); + isl_set_free(dom); + return ma; +error: + isl_multi_pw_aff_free(tuple); + vars_free(v); + isl_set_free(dom); + isl_multi_aff_free(ma); + return NULL; +} + +__isl_give isl_multi_aff *isl_multi_aff_read_from_str(isl_ctx *ctx, + const char *str) +{ + isl_multi_aff *maff; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + maff = isl_stream_read_multi_aff(s); + isl_stream_free(s); + return maff; +} + +/* Read an isl_multi_pw_aff from "s". + * + * The input format is similar to that of map, except that any conditions + * on the domains should be specified inside the tuple since each + * piecewise affine expression may have a different domain. + * + * Since we do not know in advance if the isl_multi_pw_aff lives + * in a set or a map space, we first read the first tuple and check + * if it is followed by a "->". If so, we convert the tuple into + * the domain of the isl_multi_pw_aff and read in the next tuple. + * This tuple (or the first tuple if it was not followed by a "->") + * is then converted into the isl_multi_pw_aff. + * + * Note that the function read_tuple accepts tuples where some output or + * set dimensions are defined in terms of other output or set dimensions + * since this function is also used to read maps. As a special case, + * read_tuple also accept dimensions that are defined in terms of themselves + * (i.e., that are not defined). + * These cases are not allowed when reading am isl_multi_pw_aff so we check + * that the definition of the output/set dimensions does not involve any + * output/set dimensions. + * We then drop the output dimensions from the domain of the result + * of read_tuple (which is of the form [input, output] -> [output], + * with anonymous domain) and reset the space. + */ +__isl_give isl_multi_pw_aff *isl_stream_read_multi_pw_aff( + __isl_keep isl_stream *s) +{ + struct vars *v; + isl_set *dom = NULL; + isl_multi_pw_aff *tuple = NULL; + int dim, i, n; + isl_space *space, *dom_space; + isl_multi_pw_aff *mpa = NULL; + + v = vars_new(s->ctx); + if (!v) + return NULL; + + dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0)); + if (next_is_tuple(s)) { + dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + } + if (isl_stream_eat(s, '{')) + goto error; + + tuple = read_tuple(s, v, 0, 0); + if (!tuple) + goto error; + if (isl_stream_eat_if_available(s, ISL_TOKEN_TO)) { + isl_map *map = map_from_tuple(tuple, dom, isl_dim_in, v, 0); + dom = isl_map_domain(map); + tuple = read_tuple(s, v, 0, 0); + if (!tuple) + goto error; + } + + if (isl_stream_eat(s, '}')) + goto error; + + n = isl_multi_pw_aff_dim(tuple, isl_dim_out); + dim = isl_set_dim(dom, isl_dim_all); + dom_space = isl_set_get_space(dom); + space = isl_space_range(isl_multi_pw_aff_get_space(tuple)); + space = isl_space_align_params(space, isl_space_copy(dom_space)); + if (!isl_space_is_params(dom_space)) + space = isl_space_map_from_domain_and_range( + isl_space_copy(dom_space), space); + isl_space_free(dom_space); + mpa = isl_multi_pw_aff_alloc(space); + + for (i = 0; i < n; ++i) { + isl_pw_aff *pa; + pa = isl_multi_pw_aff_get_pw_aff(tuple, i); + if (!pa) + goto error; + if (isl_pw_aff_involves_dims(pa, isl_dim_in, dim, i + 1)) { + isl_pw_aff_free(pa); + isl_die(s->ctx, isl_error_invalid, + "not an affine expression", goto error); + } + pa = isl_pw_aff_drop_dims(pa, isl_dim_in, dim, n); + space = isl_multi_pw_aff_get_domain_space(mpa); + pa = isl_pw_aff_reset_domain_space(pa, space); + mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa); + } + + isl_multi_pw_aff_free(tuple); + vars_free(v); + mpa = isl_multi_pw_aff_intersect_domain(mpa, dom); + return mpa; +error: + isl_multi_pw_aff_free(tuple); + vars_free(v); + isl_set_free(dom); + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Read an isl_multi_pw_aff from "str". + */ +__isl_give isl_multi_pw_aff *isl_multi_pw_aff_read_from_str(isl_ctx *ctx, + const char *str) +{ + isl_multi_pw_aff *mpa; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + mpa = isl_stream_read_multi_pw_aff(s); + isl_stream_free(s); + return mpa; +} + +/* Read the body of an isl_union_pw_aff from "s" with parameter domain "dom". + */ +static __isl_give isl_union_pw_aff *read_union_pw_aff_with_dom( + __isl_keep isl_stream *s, __isl_take isl_set *dom, struct vars *v) +{ + isl_pw_aff *pa; + isl_union_pw_aff *upa = NULL; + isl_set *aff_dom; + int n; + + n = v->n; + aff_dom = read_aff_domain(s, isl_set_copy(dom), v); + pa = read_pw_aff_with_dom(s, aff_dom, v); + vars_drop(v, v->n - n); + + upa = isl_union_pw_aff_from_pw_aff(pa); + + while (isl_stream_eat_if_available(s, ';')) { + isl_pw_aff *pa_i; + isl_union_pw_aff *upa_i; + + n = v->n; + aff_dom = read_aff_domain(s, isl_set_copy(dom), v); + pa_i = read_pw_aff_with_dom(s, aff_dom, v); + vars_drop(v, v->n - n); + + upa_i = isl_union_pw_aff_from_pw_aff(pa_i); + upa = isl_union_pw_aff_union_add(upa, upa_i); + } + + isl_set_free(dom); + return upa; +} + +/* Read an isl_union_pw_aff from "s". + * + * First check if there are any paramters, then read in the opening brace + * and use read_union_pw_aff_with_dom to read in the body of + * the isl_union_pw_aff. Finally, read the closing brace. + */ +__isl_give isl_union_pw_aff *isl_stream_read_union_pw_aff( + __isl_keep isl_stream *s) +{ + struct vars *v; + isl_set *dom; + isl_union_pw_aff *upa = NULL; + + v = vars_new(s->ctx); + if (!v) + return NULL; + + dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0)); + if (next_is_tuple(s)) { + dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + } + if (isl_stream_eat(s, '{')) + goto error; + + upa = read_union_pw_aff_with_dom(s, isl_set_copy(dom), v); + + if (isl_stream_eat(s, '}')) + goto error; + + vars_free(v); + isl_set_free(dom); + return upa; +error: + vars_free(v); + isl_set_free(dom); + isl_union_pw_aff_free(upa); + return NULL; +} + +/* Read an isl_union_pw_aff from "str". + */ +__isl_give isl_union_pw_aff *isl_union_pw_aff_read_from_str(isl_ctx *ctx, + const char *str) +{ + isl_union_pw_aff *upa; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + upa = isl_stream_read_union_pw_aff(s); + isl_stream_free(s); + return upa; +} + +/* This function is called for each element in a tuple inside + * isl_stream_read_multi_union_pw_aff. + * + * Read a '{', the union piecewise affine expression body and a '}' and + * add the isl_union_pw_aff to *list. + */ +static __isl_give isl_space *read_union_pw_aff_el(__isl_keep isl_stream *s, + struct vars *v, __isl_take isl_space *space, int rational, void *user) +{ + isl_set *dom; + isl_union_pw_aff *upa; + isl_union_pw_aff_list **list = (isl_union_pw_aff_list **) user; + + dom = isl_set_universe(isl_space_params(isl_space_copy(space))); + if (isl_stream_eat(s, '{')) + goto error; + upa = read_union_pw_aff_with_dom(s, dom, v); + *list = isl_union_pw_aff_list_add(*list, upa); + if (isl_stream_eat(s, '}')) + return isl_space_free(space); + if (!*list) + return isl_space_free(space); + return space; +error: + isl_set_free(dom); + return isl_space_free(space); +} + +/* Do the next tokens in "s" correspond to an empty tuple? + * In particular, does the stream start with a '[', followed by a ']', + * not followed by a "->"? + */ +static int next_is_empty_tuple(__isl_keep isl_stream *s) +{ + struct isl_token *tok, *tok2, *tok3; + int is_empty_tuple = 0; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type != '[') { + isl_stream_push_token(s, tok); + return 0; + } + + tok2 = isl_stream_next_token(s); + if (tok2 && tok2->type == ']') { + tok3 = isl_stream_next_token(s); + is_empty_tuple = !tok || tok->type != ISL_TOKEN_TO; + if (tok3) + isl_stream_push_token(s, tok3); + } + if (tok2) + isl_stream_push_token(s, tok2); + isl_stream_push_token(s, tok); + + return is_empty_tuple; +} + +/* Do the next tokens in "s" correspond to a tuple of parameters? + * In particular, does the stream start with a '[' that is not + * followed by a '{' or a nested tuple? + */ +static int next_is_param_tuple(__isl_keep isl_stream *s) +{ + struct isl_token *tok, *tok2; + int is_tuple; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type != '[' || next_is_tuple(s)) { + isl_stream_push_token(s, tok); + return 0; + } + + tok2 = isl_stream_next_token(s); + is_tuple = tok2 && tok2->type != '{'; + if (tok2) + isl_stream_push_token(s, tok2); + isl_stream_push_token(s, tok); + + return is_tuple; +} + +/* Read an isl_multi_union_pw_aff from "s". + * + * The input has the form + * + * [{ [..] : ... ; [..] : ... }, { [..] : ... ; [..] : ... }] + * + * or + * + * [..] -> [{ [..] : ... ; [..] : ... }, { [..] : ... ; [..] : ... }] + * + * We first check for the special case of an empty tuple "[]". + * Then we check if there are any parameters. + * Finally, we read the tuple, collecting the individual isl_union_pw_aff + * elements in a list and construct the result from the tuple space and + * the list. + */ +__isl_give isl_multi_union_pw_aff *isl_stream_read_multi_union_pw_aff( + __isl_keep isl_stream *s) +{ + struct vars *v; + isl_set *dom = NULL; + isl_space *space; + isl_multi_union_pw_aff *mupa = NULL; + isl_union_pw_aff_list *list; + + if (next_is_empty_tuple(s)) { + if (isl_stream_eat(s, '[')) + return NULL; + if (isl_stream_eat(s, ']')) + return NULL; + space = isl_space_set_alloc(s->ctx, 0, 0); + return isl_multi_union_pw_aff_zero(space); + } + + v = vars_new(s->ctx); + if (!v) + return NULL; + + dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0)); + if (next_is_param_tuple(s)) { + dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0); + if (isl_stream_eat(s, ISL_TOKEN_TO)) + goto error; + } + space = isl_set_get_space(dom); + isl_set_free(dom); + list = isl_union_pw_aff_list_alloc(s->ctx, 0); + space = read_tuple_space(s, v, space, 1, 0, + &read_union_pw_aff_el, &list); + mupa = isl_multi_union_pw_aff_from_union_pw_aff_list(space, list); + + vars_free(v); + + return mupa; +error: + vars_free(v); + isl_set_free(dom); + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Read an isl_multi_union_pw_aff from "str". + */ +__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_read_from_str( + isl_ctx *ctx, const char *str) +{ + isl_multi_union_pw_aff *mupa; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + mupa = isl_stream_read_multi_union_pw_aff(s); + isl_stream_free(s); + return mupa; +} + +__isl_give isl_union_pw_qpolynomial *isl_stream_read_union_pw_qpolynomial( + __isl_keep isl_stream *s) +{ + struct isl_obj obj; + + obj = obj_read(s); + if (obj.type == isl_obj_pw_qpolynomial) { + obj.type = isl_obj_union_pw_qpolynomial; + obj.v = isl_union_pw_qpolynomial_from_pw_qpolynomial(obj.v); + } + if (obj.v) + isl_assert(s->ctx, obj.type == isl_obj_union_pw_qpolynomial, + goto error); + + return obj.v; +error: + obj.type->free(obj.v); + return NULL; +} + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_read_from_str( + isl_ctx *ctx, const char *str) +{ + isl_union_pw_qpolynomial *upwqp; + isl_stream *s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + upwqp = isl_stream_read_union_pw_qpolynomial(s); + isl_stream_free(s); + return upwqp; +} Index: lib/Analysis/isl/isl_int.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_int.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_INT_H +#define ISL_INT_H +#define ISL_DEPRECATED_INT_H + +#include +#include +#include +#include + +#ifdef USE_GMP_FOR_MP +#include +#endif + +#ifdef USE_IMATH_FOR_MP +#ifdef USE_SMALL_INT_OPT +#include +#else /* USE_SMALL_INT_OPT */ +#include +#endif /* USE_SMALL_INT_OPT */ +#endif /* USE_IMATH_FOR_MP */ + +#define isl_int_is_zero(i) (isl_int_sgn(i) == 0) +#define isl_int_is_one(i) (isl_int_cmp_si(i,1) == 0) +#define isl_int_is_negone(i) (isl_int_cmp_si(i,-1) == 0) +#define isl_int_is_pos(i) (isl_int_sgn(i) > 0) +#define isl_int_is_neg(i) (isl_int_sgn(i) < 0) +#define isl_int_is_nonpos(i) (isl_int_sgn(i) <= 0) +#define isl_int_is_nonneg(i) (isl_int_sgn(i) >= 0) + +#ifndef USE_SMALL_INT_OPT +#define isl_int_print(out,i,width) \ + do { \ + char *s; \ + s = isl_int_get_str(i); \ + fprintf(out, "%*s", width, s); \ + isl_int_free_str(s); \ + } while (0) +#endif /* USE_SMALL_INT_OPT */ + +__isl_give isl_printer *isl_printer_print_isl_int(__isl_take isl_printer *p, + isl_int i); + +#endif /* ISL_INT_H */ Index: lib/Analysis/isl/isl_int_gmp.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_int_gmp.h @@ -0,0 +1,88 @@ +#ifndef ISL_INT_GMP_H +#define ISL_INT_GMP_H + +#include + +/* isl_int is the basic integer type, implemented with GMP's mpz_t. In the + * future, different types such as long long or cln::cl_I will be supported. + */ +typedef mpz_t isl_int; + +#define isl_int_init(i) mpz_init(i) +#define isl_int_clear(i) mpz_clear(i) + +#define isl_int_set(r,i) mpz_set(r,i) +#define isl_int_set_si(r,i) mpz_set_si(r,i) +#define isl_int_set_ui(r,i) mpz_set_ui(r,i) +#define isl_int_fits_slong(r) mpz_fits_slong_p(r) +#define isl_int_get_si(r) mpz_get_si(r) +#define isl_int_fits_ulong(r) mpz_fits_ulong_p(r) +#define isl_int_get_ui(r) mpz_get_ui(r) +#define isl_int_get_d(r) mpz_get_d(r) +#define isl_int_get_str(r) mpz_get_str(0, 10, r) +#define isl_int_abs(r,i) mpz_abs(r,i) +#define isl_int_neg(r,i) mpz_neg(r,i) +#define isl_int_swap(i,j) mpz_swap(i,j) +#define isl_int_swap_or_set(i,j) mpz_swap(i,j) +#define isl_int_add_ui(r,i,j) mpz_add_ui(r,i,j) +#define isl_int_sub_ui(r,i,j) mpz_sub_ui(r,i,j) + +#define isl_int_add(r,i,j) mpz_add(r,i,j) +#define isl_int_sub(r,i,j) mpz_sub(r,i,j) +#define isl_int_mul(r,i,j) mpz_mul(r,i,j) +#define isl_int_mul_2exp(r,i,j) mpz_mul_2exp(r,i,j) +#define isl_int_mul_si(r,i,j) mpz_mul_si(r,i,j) +#define isl_int_mul_ui(r,i,j) mpz_mul_ui(r,i,j) +#define isl_int_pow_ui(r,i,j) mpz_pow_ui(r,i,j) +#define isl_int_addmul(r,i,j) mpz_addmul(r,i,j) +#define isl_int_addmul_ui(r,i,j) mpz_addmul_ui(r,i,j) +#define isl_int_submul(r,i,j) mpz_submul(r,i,j) +#define isl_int_submul_ui(r,i,j) mpz_submul_ui(r,i,j) + +#define isl_int_gcd(r,i,j) mpz_gcd(r,i,j) +#define isl_int_lcm(r,i,j) mpz_lcm(r,i,j) +#define isl_int_divexact(r,i,j) mpz_divexact(r,i,j) +#define isl_int_divexact_ui(r,i,j) mpz_divexact_ui(r,i,j) +#define isl_int_tdiv_q(r,i,j) mpz_tdiv_q(r,i,j) +#define isl_int_cdiv_q(r,i,j) mpz_cdiv_q(r,i,j) +#define isl_int_fdiv_q(r,i,j) mpz_fdiv_q(r,i,j) +#define isl_int_fdiv_r(r,i,j) mpz_fdiv_r(r,i,j) +#define isl_int_fdiv_q_ui(r,i,j) mpz_fdiv_q_ui(r,i,j) + +#define isl_int_read(r,s) mpz_set_str(r,s,10) +#define isl_int_sgn(i) mpz_sgn(i) +#define isl_int_cmp(i,j) mpz_cmp(i,j) +#define isl_int_cmp_si(i,si) mpz_cmp_si(i,si) +#define isl_int_eq(i,j) (mpz_cmp(i,j) == 0) +#define isl_int_ne(i,j) (mpz_cmp(i,j) != 0) +#define isl_int_lt(i,j) (mpz_cmp(i,j) < 0) +#define isl_int_le(i,j) (mpz_cmp(i,j) <= 0) +#define isl_int_gt(i,j) (mpz_cmp(i,j) > 0) +#define isl_int_ge(i,j) (mpz_cmp(i,j) >= 0) +#define isl_int_abs_cmp(i,j) mpz_cmpabs(i,j) +#define isl_int_abs_eq(i,j) (mpz_cmpabs(i,j) == 0) +#define isl_int_abs_ne(i,j) (mpz_cmpabs(i,j) != 0) +#define isl_int_abs_lt(i,j) (mpz_cmpabs(i,j) < 0) +#define isl_int_abs_gt(i,j) (mpz_cmpabs(i,j) > 0) +#define isl_int_abs_ge(i,j) (mpz_cmpabs(i,j) >= 0) +#define isl_int_is_divisible_by(i,j) mpz_divisible_p(i,j) + +uint32_t isl_gmp_hash(mpz_t v, uint32_t hash); +#define isl_int_hash(v,h) isl_gmp_hash(v,h) + +#ifndef mp_get_memory_functions +void mp_get_memory_functions( + void *(**alloc_func_ptr) (size_t), + void *(**realloc_func_ptr) (void *, size_t, size_t), + void (**free_func_ptr) (void *, size_t)); +#endif + +typedef void (*isl_int_print_mp_free_t)(void *, size_t); +#define isl_int_free_str(s) \ + do { \ + isl_int_print_mp_free_t mp_free; \ + mp_get_memory_functions(NULL, NULL, &mp_free); \ + (*mp_free)(s, strlen(s) + 1); \ + } while (0) + +#endif /* ISL_INT_GMP_H */ Index: lib/Analysis/isl/isl_int_imath.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_int_imath.h @@ -0,0 +1,76 @@ +#ifndef ISL_INT_IMATH_H +#define ISL_INT_IMATH_H + +#include "isl_hide_deprecated.h" + +#include + +/* isl_int is the basic integer type, implemented with imath's mp_int. */ +typedef mp_int isl_int; + +#define isl_int_init(i) i = mp_int_alloc() +#define isl_int_clear(i) mp_int_free(i) + +#define isl_int_set(r,i) impz_set(r,i) +#define isl_int_set_si(r,i) impz_set_si(r,i) +#define isl_int_set_ui(r,i) impz_set_ui(r,i) +#define isl_int_fits_slong(r) isl_imath_fits_slong_p(r) +#define isl_int_get_si(r) impz_get_si(r) +#define isl_int_fits_ulong(r) isl_imath_fits_ulong_p(r) +#define isl_int_get_ui(r) impz_get_ui(r) +#define isl_int_get_d(r) impz_get_si(r) +#define isl_int_get_str(r) impz_get_str(0, 10, r) +#define isl_int_abs(r,i) impz_abs(r,i) +#define isl_int_neg(r,i) impz_neg(r,i) +#define isl_int_swap(i,j) impz_swap(i,j) +#define isl_int_swap_or_set(i,j) impz_swap(i,j) +#define isl_int_add_ui(r,i,j) impz_add_ui(r,i,j) +#define isl_int_sub_ui(r,i,j) impz_sub_ui(r,i,j) + +#define isl_int_add(r,i,j) impz_add(r,i,j) +#define isl_int_sub(r,i,j) impz_sub(r,i,j) +#define isl_int_mul(r,i,j) impz_mul(r,i,j) +#define isl_int_mul_2exp(r,i,j) impz_mul_2exp(r,i,j) +#define isl_int_mul_si(r,i,j) mp_int_mul_value(i,j,r) +#define isl_int_mul_ui(r,i,j) impz_mul_ui(r,i,j) +#define isl_int_pow_ui(r,i,j) impz_pow_ui(r,i,j) +#define isl_int_addmul(r,i,j) impz_addmul(r,i,j) +#define isl_int_addmul_ui(r,i,j) isl_imath_addmul_ui(r,i,j) +#define isl_int_submul(r,i,j) impz_submul(r,i,j) +#define isl_int_submul_ui(r,i,j) isl_imath_submul_ui(r,i,j) + +#define isl_int_gcd(r,i,j) impz_gcd(r,i,j) +#define isl_int_lcm(r,i,j) impz_lcm(r,i,j) +#define isl_int_divexact(r,i,j) impz_divexact(r,i,j) +#define isl_int_divexact_ui(r,i,j) impz_divexact_ui(r,i,j) +#define isl_int_tdiv_q(r,i,j) impz_tdiv_q(r,i,j) +#define isl_int_cdiv_q(r,i,j) impz_cdiv_q(r,i,j) +#define isl_int_fdiv_q(r,i,j) impz_fdiv_q(r,i,j) +#define isl_int_fdiv_r(r,i,j) impz_fdiv_r(r,i,j) +#define isl_int_fdiv_q_ui(r,i,j) impz_fdiv_q_ui(r,i,j) + +#define isl_int_read(r,s) impz_set_str(r,s,10) +#define isl_int_sgn(i) impz_sgn(i) +#define isl_int_cmp(i,j) impz_cmp(i,j) +#define isl_int_cmp_si(i,si) impz_cmp_si(i,si) +#define isl_int_eq(i,j) (impz_cmp(i,j) == 0) +#define isl_int_ne(i,j) (impz_cmp(i,j) != 0) +#define isl_int_lt(i,j) (impz_cmp(i,j) < 0) +#define isl_int_le(i,j) (impz_cmp(i,j) <= 0) +#define isl_int_gt(i,j) (impz_cmp(i,j) > 0) +#define isl_int_ge(i,j) (impz_cmp(i,j) >= 0) +#define isl_int_abs_cmp(i,j) impz_cmpabs(i,j) +#define isl_int_abs_eq(i,j) (impz_cmpabs(i,j) == 0) +#define isl_int_abs_ne(i,j) (impz_cmpabs(i,j) != 0) +#define isl_int_abs_lt(i,j) (impz_cmpabs(i,j) < 0) +#define isl_int_abs_gt(i,j) (impz_cmpabs(i,j) > 0) +#define isl_int_abs_ge(i,j) (impz_cmpabs(i,j) >= 0) +#define isl_int_is_divisible_by(i,j) impz_divisible_p(i,j) + +uint32_t isl_imath_hash(mp_int v, uint32_t hash); +#define isl_int_hash(v,h) isl_imath_hash(v,h) + +typedef void (*isl_int_print_mp_free_t)(void *, size_t); +#define isl_int_free_str(s) free(s) + +#endif /* ISL_INT_IMATH_H */ Index: lib/Analysis/isl/isl_int_sioimath.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_int_sioimath.h @@ -0,0 +1,1223 @@ +/* + * Copyright 2015 INRIA Paris-Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Michael Kruse, INRIA Paris-Rocquencourt, + * Domaine de Voluceau, Rocquenqourt, B.P. 105, + * 78153 Le Chesnay Cedex France + */ +#ifndef ISL_INT_SIOIMATH_H +#define ISL_INT_SIOIMATH_H + +#include +#include +#include +#include + +#include +#include + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array)) + +/* Visual Studio before VS2015 does not support the inline keyword when + * compiling in C mode because it was introduced in C99 which it does not + * officially support. Instead, it has a proprietary extension using __inline. + */ +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define inline __inline +#endif + +/* The type to represent integers optimized for small values. It is either a + * pointer to an mp_int ( = mpz_t*; big representation) or an int32_t (small + * represenation) with a discriminator at the least significant bit. In big + * representation it will be always zero because of heap alignment. It is set + * to 1 for small representation and use the 32 most significant bits for the + * int32_t. + * + * Structure on 64 bit machines, with 8-byte aligment (3 bits): + * + * Big representation: + * MSB LSB + * |------------------------------------------------------------000 + * | mpz_t* | + * | != NULL | + * + * Small representation: + * MSB 32 LSB + * |------------------------------|00000000000000000000000000000001 + * | int32_t | + * | 2147483647 ... -2147483647 | + * ^ + * | + * discriminator bit + * + * On 32 bit machines isl_sioimath type is blown up to 8 bytes, i.e. + * isl_sioimath is guaranteed to be at least 8 bytes. This is to ensure the + * int32_t can be hidden in that type without data loss. In the future we might + * optimize this to use 31 hidden bits in a 32 bit pointer. We may also use 63 + * bits on 64 bit machines, but this comes with the cost of additional overflow + * checks because there is no standardized 128 bit integer we could expand to. + * + * We use native integer types and avoid union structures to avoid assumptions + * on the machine's endianness. + * + * This implementation makes the following assumptions: + * - long can represent any int32_t + * - mp_small is signed long + * - mp_usmall is unsigned long + * - adresses returned by malloc are aligned to 2-byte boundaries (leastmost + * bit is zero) + */ +#if UINT64_MAX > UINTPTR_MAX +typedef uint64_t isl_sioimath; +#else +typedef uintptr_t isl_sioimath; +#endif + +/* The negation of the smallest possible number in int32_t, INT32_MIN + * (0x80000000u, -2147483648), cannot be represented in an int32_t, therefore + * every operation that may produce this value needs to special-case it. + * The operations are: + * abs(INT32_MIN) + * -INT32_MIN (negation) + * -1 * INT32_MIN (multiplication) + * INT32_MIN/-1 (any division: divexact, fdiv, cdiv, tdiv) + * To avoid checking these cases, we exclude INT32_MIN from small + * representation. + */ +#define ISL_SIOIMATH_SMALL_MIN (-INT32_MAX) + +/* Largest possible number in small representation */ +#define ISL_SIOIMATH_SMALL_MAX INT32_MAX + +/* Used for function parameters the function modifies. */ +typedef isl_sioimath *isl_sioimath_ptr; + +/* Used for function parameters that are read-only. */ +typedef isl_sioimath isl_sioimath_src; + +/* Return whether the argument is stored in small representation. + */ +inline int isl_sioimath_is_small(isl_sioimath val) +{ + return val & 0x00000001; +} + +/* Return whether the argument is stored in big representation. + */ +inline int isl_sioimath_is_big(isl_sioimath val) +{ + return !isl_sioimath_is_small(val); +} + +/* Get the number of an isl_int in small representation. Result is undefined if + * val is not stored in that format. + */ +inline int32_t isl_sioimath_get_small(isl_sioimath val) +{ + return val >> 32; +} + +/* Get the number of an in isl_int in big representation. Result is undefined if + * val is not stored in that format. + */ +inline mp_int isl_sioimath_get_big(isl_sioimath val) +{ + return (mp_int)(uintptr_t) val; +} + +/* Return 1 if val is stored in small representation and store its value to + * small. We rely on the compiler to optimize the isl_sioimath_get_small such + * that the shift is moved into the branch that executes in case of small + * representation. If there is no such branch, then a single shift is still + * cheaper than introducing branching code. + */ +inline int isl_sioimath_decode_small(isl_sioimath val, int32_t *small) +{ + *small = isl_sioimath_get_small(val); + return isl_sioimath_is_small(val); +} + +/* Return 1 if val is stored in big representation and store its value to big. + */ +inline int isl_sioimath_decode_big(isl_sioimath val, mp_int *big) +{ + *big = isl_sioimath_get_big(val); + return isl_sioimath_is_big(val); +} + +/* Encode a small representation into an isl_int. + */ +inline isl_sioimath isl_sioimath_encode_small(int32_t val) +{ + return ((isl_sioimath) val) << 32 | 0x00000001; +} + +/* Encode a big representation. + */ +inline isl_sioimath isl_sioimath_encode_big(mp_int val) +{ + return (isl_sioimath)(uintptr_t) val; +} + +/* A common situation is to call an IMath function with at least one argument + * that is currently in small representation or an integer parameter, i.e. a big + * representation of the same number is required. Promoting the original + * argument comes with multiple problems, such as modifying a read-only + * argument, the responsibility of deallocation and the execution cost. Instead, + * we make a copy by 'faking' the IMath internal structure. + * + * We reserve the maximum number of required digits on the stack to avoid heap + * allocations. + * + * mp_digit can be uint32_t or uint16_t. This code must work for little and big + * endian digits. The structure for an uint64_t argument and 32-bit mp_digits is + * sketched below. + * + * |----------------------------| + * uint64_t + * + * |-------------||-------------| + * mp_digit mp_digit + * digits[1] digits[0] + * Most sig digit Least sig digit + */ +typedef struct { + mpz_t big; + mp_digit digits[(sizeof(uintmax_t) + sizeof(mp_digit) - 1) / + sizeof(mp_digit)]; +} isl_sioimath_scratchspace_t; + +/* Convert a native integer to IMath's digit representation. A native integer + * might be big- or little endian, but IMath always stores the least significant + * digit in the lowest array indices. memcpy therefore is not possible. + * + * We also have to consider that long and mp_digit can be of different sizes, + * depending on the compiler (LP64, LLP64) and IMath's USE_64BIT_WORDS. This + * macro should work for all of them. + * + * "used" is set to the number of written digits. It must be minimal (IMath + * checks zeroness using the used field), but always at least one. Also note + * that the result of num>>(sizeof(num)*CHAR_BIT) is undefined. + */ +#define ISL_SIOIMATH_TO_DIGITS(num, digits, used) \ + do { \ + int i = 0; \ + do { \ + (digits)[i] = \ + ((num) >> (sizeof(mp_digit) * CHAR_BIT * i)); \ + i += 1; \ + if (i >= (sizeof(num) + sizeof(mp_digit) - 1) / \ + sizeof(mp_digit)) \ + break; \ + if (((num) >> (sizeof(mp_digit) * CHAR_BIT * i)) == 0) \ + break; \ + } while (1); \ + (used) = i; \ + } while (0) + +inline void isl_siomath_uint32_to_digits(uint32_t num, mp_digit *digits, + mp_size *used) +{ + ISL_SIOIMATH_TO_DIGITS(num, digits, *used); +} + +inline void isl_siomath_ulong_to_digits(unsigned long num, mp_digit *digits, + mp_size *used) +{ + ISL_SIOIMATH_TO_DIGITS(num, digits, *used); +} + +inline void isl_siomath_uint64_to_digits(uint64_t num, mp_digit *digits, + mp_size *used) +{ + ISL_SIOIMATH_TO_DIGITS(num, digits, *used); +} + +/* Get the IMath representation of an isl_int without modifying it. + * For the case it is not in big representation yet, pass some scratch space we + * can use to store the big representation in. + * In order to avoid requiring init and free on the scratch space, we directly + * modify the internal representation. + * + * The name derives from its indented use: getting the big representation of an + * input (src) argument. + */ +inline mp_int isl_sioimath_bigarg_src(isl_sioimath arg, + isl_sioimath_scratchspace_t *scratch) +{ + mp_int big; + int32_t small; + uint32_t num; + + if (isl_sioimath_decode_big(arg, &big)) + return big; + + small = isl_sioimath_get_small(arg); + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + if (small >= 0) { + scratch->big.sign = MP_ZPOS; + num = small; + } else { + scratch->big.sign = MP_NEG; + num = -small; + } + + isl_siomath_uint32_to_digits(num, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Create a temporary IMath mp_int for a signed long. + */ +inline mp_int isl_sioimath_siarg_src(signed long arg, + isl_sioimath_scratchspace_t *scratch) +{ + unsigned long num; + + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + if (arg >= 0) { + scratch->big.sign = MP_ZPOS; + num = arg; + } else { + scratch->big.sign = MP_NEG; + num = (arg == LONG_MIN) ? ((unsigned long) LONG_MAX) + 1 : -arg; + } + + isl_siomath_ulong_to_digits(num, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Create a temporary IMath mp_int for an int64_t. + */ +inline mp_int isl_sioimath_si64arg_src(int64_t arg, + isl_sioimath_scratchspace_t *scratch) +{ + uint64_t num; + + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + if (arg >= 0) { + scratch->big.sign = MP_ZPOS; + num = arg; + } else { + scratch->big.sign = MP_NEG; + num = (arg == INT64_MIN) ? ((uint64_t) INT64_MAX) + 1 : -arg; + } + + isl_siomath_uint64_to_digits(num, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Create a temporary IMath mp_int for an unsigned long. + */ +inline mp_int isl_sioimath_uiarg_src(unsigned long arg, + isl_sioimath_scratchspace_t *scratch) +{ + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + scratch->big.sign = MP_ZPOS; + + isl_siomath_ulong_to_digits(arg, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Ensure big representation. Does not preserve the current number. + * Callers may use the fact that the value _is_ preserved if the presentation + * was big before. + */ +inline mp_int isl_sioimath_reinit_big(isl_sioimath_ptr ptr) +{ + if (isl_sioimath_is_small(*ptr)) + *ptr = isl_sioimath_encode_big(mp_int_alloc()); + return isl_sioimath_get_big(*ptr); +} + +/* Set ptr to a number in small representation. + */ +inline void isl_sioimath_set_small(isl_sioimath_ptr ptr, int32_t val) +{ + if (isl_sioimath_is_big(*ptr)) + mp_int_free(isl_sioimath_get_big(*ptr)); + *ptr = isl_sioimath_encode_small(val); +} + +/* Set ptr to val, choosing small representation if possible. + */ +inline void isl_sioimath_set_int32(isl_sioimath_ptr ptr, int32_t val) +{ + if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(ptr, val); + return; + } + + mp_int_init_value(isl_sioimath_reinit_big(ptr), val); +} + +/* Assign an int64_t number using small representation if possible. + */ +inline void isl_sioimath_set_int64(isl_sioimath_ptr ptr, int64_t val) +{ + if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(ptr, val); + return; + } + + isl_sioimath_scratchspace_t scratch; + mp_int_copy(isl_sioimath_si64arg_src(val, &scratch), + isl_sioimath_reinit_big(ptr)); +} + +/* Convert to big representation while preserving the current number. + */ +inline void isl_sioimath_promote(isl_sioimath_ptr dst) +{ + int32_t small; + + if (isl_sioimath_is_big(*dst)) + return; + + small = isl_sioimath_get_small(*dst); + mp_int_set_value(isl_sioimath_reinit_big(dst), small); +} + +/* Convert to small representation while preserving the current number. Does + * nothing if dst doesn't fit small representation. + */ +inline void isl_sioimath_try_demote(isl_sioimath_ptr dst) +{ + mp_small small; + + if (isl_sioimath_is_small(*dst)) + return; + + if (mp_int_to_int(isl_sioimath_get_big(*dst), &small) != MP_OK) + return; + + if (ISL_SIOIMATH_SMALL_MIN <= small && small <= ISL_SIOIMATH_SMALL_MAX) + isl_sioimath_set_small(dst, small); +} + +/* Initialize an isl_int. The implicit value is 0 in small representation. + */ +inline void isl_sioimath_init(isl_sioimath_ptr dst) +{ + *dst = isl_sioimath_encode_small(0); +} + +/* Free the resources taken by an isl_int. + */ +inline void isl_sioimath_clear(isl_sioimath_ptr dst) +{ + if (isl_sioimath_is_small(*dst)) + return; + + mp_int_free(isl_sioimath_get_big(*dst)); +} + +/* Copy the value of one isl_int to another. + */ +inline void isl_sioimath_set(isl_sioimath_ptr dst, isl_sioimath_src val) +{ + if (isl_sioimath_is_small(val)) { + isl_sioimath_set_small(dst, isl_sioimath_get_small(val)); + return; + } + + mp_int_copy(isl_sioimath_get_big(val), isl_sioimath_reinit_big(dst)); +} + +/* Store a signed long into an isl_int. + */ +inline void isl_sioimath_set_si(isl_sioimath_ptr dst, long val) +{ + if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(dst, val); + return; + } + + mp_int_set_value(isl_sioimath_reinit_big(dst), val); +} + +/* Store an unsigned long into an isl_int. + */ +inline void isl_sioimath_set_ui(isl_sioimath_ptr dst, unsigned long val) +{ + if (val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(dst, val); + return; + } + + mp_int_set_uvalue(isl_sioimath_reinit_big(dst), val); +} + +/* Return whether a number can be represented by a signed long. + */ +inline int isl_sioimath_fits_slong(isl_sioimath_src val) +{ + mp_small dummy; + + if (isl_sioimath_is_small(val)) + return 1; + + return mp_int_to_int(isl_sioimath_get_big(val), &dummy) == MP_OK; +} + +/* Return a number as signed long. Result is undefined if the number cannot be + * represented as long. + */ +inline long isl_sioimath_get_si(isl_sioimath_src val) +{ + mp_small result; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val); + + mp_int_to_int(isl_sioimath_get_big(val), &result); + return result; +} + +/* Return whether a number can be represented as unsigned long. + */ +inline int isl_sioimath_fits_ulong(isl_sioimath_src val) +{ + mp_usmall dummy; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val) >= 0; + + return mp_int_to_uint(isl_sioimath_get_big(val), &dummy) == MP_OK; +} + +/* Return a number as unsigned long. Result is undefined if the number cannot be + * represented as unsigned long. + */ +inline unsigned long isl_sioimath_get_ui(isl_sioimath_src val) +{ + mp_usmall result; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val); + + mp_int_to_uint(isl_sioimath_get_big(val), &result); + return result; +} + +/* Return a number as floating point value. + */ +inline double isl_sioimath_get_d(isl_sioimath_src val) +{ + mp_int big; + double result = 0; + int i; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val); + + big = isl_sioimath_get_big(val); + for (i = 0; i < big->used; ++i) + result = result * (double) ((uintmax_t) MP_DIGIT_MAX + 1) + + (double) big->digits[i]; + + if (big->sign == MP_NEG) + result = -result; + + return result; +} + +/* Format a number as decimal string. + * + * The largest possible string from small representation is 12 characters + * ("-2147483647"). + */ +inline char *isl_sioimath_get_str(isl_sioimath_src val) +{ + char *result; + + if (isl_sioimath_is_small(val)) { + result = malloc(12); + snprintf(result, 12, "%" PRIi32, isl_sioimath_get_small(val)); + return result; + } + + return impz_get_str(NULL, 10, isl_sioimath_get_big(val)); +} + +/* Return the absolute value. + */ +inline void isl_sioimath_abs(isl_sioimath_ptr dst, isl_sioimath_src arg) +{ + if (isl_sioimath_is_small(arg)) { + isl_sioimath_set_small(dst, labs(isl_sioimath_get_small(arg))); + return; + } + + mp_int_abs(isl_sioimath_get_big(arg), isl_sioimath_reinit_big(dst)); +} + +/* Return the negation of a number. + */ +inline void isl_sioimath_neg(isl_sioimath_ptr dst, isl_sioimath_src arg) +{ + if (isl_sioimath_is_small(arg)) { + isl_sioimath_set_small(dst, -isl_sioimath_get_small(arg)); + return; + } + + mp_int_neg(isl_sioimath_get_big(arg), isl_sioimath_reinit_big(dst)); +} + +/* Swap two isl_ints. + * + * isl_sioimath can be copied bytewise; nothing depends on its address. It can + * also be stored in a CPU register. + */ +inline void isl_sioimath_swap(isl_sioimath_ptr lhs, isl_sioimath_ptr rhs) +{ + isl_sioimath tmp = *lhs; + *lhs = *rhs; + *rhs = tmp; +} + +/* Add an unsigned long to the number. + * + * On LP64 unsigned long exceeds the range of an int64_t, therefore we check in + * advance whether small representation possibly overflows. + */ +inline void isl_sioimath_add_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + int32_t smalllhs; + isl_sioimath_scratchspace_t lhsscratch; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + (rhs <= (uint64_t) INT64_MAX - (uint64_t) ISL_SIOIMATH_SMALL_MAX)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs + rhs); + return; + } + + impz_add_ui(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs); + isl_sioimath_try_demote(dst); +} + +/* Subtract an unsigned long. + * + * On LP64 unsigned long exceeds the range of an int64_t. If + * ISL_SIOIMATH_SMALL_MIN-rhs>=INT64_MIN we can do the calculation using int64_t + * without risking an overflow. + */ +inline void isl_sioimath_sub_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + int32_t smalllhs; + isl_sioimath_scratchspace_t lhsscratch; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + (rhs < (uint64_t) INT64_MIN - (uint64_t) ISL_SIOIMATH_SMALL_MIN)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs - rhs); + return; + } + + impz_sub_ui(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs); + isl_sioimath_try_demote(dst); +} + +/* Sum of two isl_ints. + */ +inline void isl_sioimath_add(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs, smallrhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + isl_sioimath_decode_small(rhs, &smallrhs)) { + isl_sioimath_set_int64( + dst, (int64_t) smalllhs + (int64_t) smallrhs); + return; + } + + mp_int_add(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Subtract two isl_ints. + */ +inline void isl_sioimath_sub(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs, smallrhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + isl_sioimath_decode_small(rhs, &smallrhs)) { + isl_sioimath_set_int64( + dst, (int64_t) smalllhs - (int64_t) smallrhs); + return; + } + + mp_int_sub(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Multiply two isl_ints. + */ +inline void isl_sioimath_mul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs, smallrhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + isl_sioimath_decode_small(rhs, &smallrhs)) { + isl_sioimath_set_int64( + dst, (int64_t) smalllhs * (int64_t) smallrhs); + return; + } + + mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Shift lhs by rhs bits to the left and store the result in dst. Effectively, + * this operation computes 'lhs * 2^rhs'. + */ +inline void isl_sioimath_mul_2exp(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs; + int32_t smalllhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs <= 32ul)) { + isl_sioimath_set_int64(dst, ((int64_t) smalllhs) << rhs); + return; + } + + mp_int_mul_pow2(isl_sioimath_bigarg_src(lhs, &scratchlhs), rhs, + isl_sioimath_reinit_big(dst)); +} + +/* Multiply an isl_int and a signed long. + */ +inline void isl_sioimath_mul_si(isl_sioimath_ptr dst, isl_sioimath lhs, + signed long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs > LONG_MIN) && + (labs(rhs) <= UINT32_MAX)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs * (int64_t) rhs); + return; + } + + mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_siarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Multiply an isl_int and an unsigned long. + */ +inline void isl_sioimath_mul_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs <= UINT32_MAX)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs * (int64_t) rhs); + return; + } + + mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_uiarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Compute the power of an isl_int to an unsigned long. + * Always let IMath do it; the result is unlikely to be small except in some + * special cases. + * Note: 0^0 == 1 + */ +inline void isl_sioimath_pow_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs; + + switch (rhs) { + case 0: + isl_sioimath_set_small(dst, 1); + return; + case 1: + isl_sioimath_set(dst, lhs); + return; + case 2: + isl_sioimath_mul(dst, lhs, lhs); + return; + } + + if (isl_sioimath_decode_small(lhs, &smalllhs)) { + switch (smalllhs) { + case 0: + isl_sioimath_set_small(dst, 0); + return; + case 1: + isl_sioimath_set_small(dst, 1); + return; + case 2: + isl_sioimath_set_small(dst, 1); + isl_sioimath_mul_2exp(dst, *dst, rhs); + return; + default: + if ((MP_SMALL_MIN <= rhs) && (rhs <= MP_SMALL_MAX)) { + mp_int_expt_value(smalllhs, rhs, + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); + return; + } + } + } + + mp_int_expt_full(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_uiarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Fused multiply-add. + */ +inline void isl_sioimath_addmul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul(&tmp, lhs, rhs); + isl_sioimath_add(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +/* Fused multiply-add with an unsigned long. + */ +inline void isl_sioimath_addmul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul_ui(&tmp, lhs, rhs); + isl_sioimath_add(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +/* Fused multiply-subtract. + */ +inline void isl_sioimath_submul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul(&tmp, lhs, rhs); + isl_sioimath_sub(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +/* Fused multiply-add with an unsigned long. + */ +inline void isl_sioimath_submul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul_ui(&tmp, lhs, rhs); + isl_sioimath_sub(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +void isl_sioimath_gcd(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +void isl_sioimath_lcm(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); + +/* Divide lhs by rhs, rounding to zero (Truncate). + */ +inline void isl_sioimath_tdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + isl_sioimath_set_small(dst, lhssmall / rhssmall); + return; + } + + mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch), + isl_sioimath_reinit_big(dst), NULL); + isl_sioimath_try_demote(dst); + return; +} + +/* Divide lhs by an unsigned long rhs, rounding to zero (Truncate). + */ +inline void isl_sioimath_tdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall; + + if (isl_sioimath_is_small(lhs) && (rhs <= (unsigned long) INT32_MAX)) { + lhssmall = isl_sioimath_get_small(lhs); + isl_sioimath_set_small(dst, lhssmall / (int32_t) rhs); + return; + } + + if (rhs <= MP_SMALL_MAX) { + mp_int_div_value(isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs, + isl_sioimath_reinit_big(dst), NULL); + isl_sioimath_try_demote(dst); + return; + } + + mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_uiarg_src(rhs, &rhsscratch), + isl_sioimath_reinit_big(dst), NULL); + isl_sioimath_try_demote(dst); +} + +/* Divide lhs by rhs, rounding to positive infinity (Ceil). + */ +inline void isl_sioimath_cdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + int32_t lhssmall, rhssmall; + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t q; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + if ((lhssmall >= 0) && (rhssmall >= 0)) + q = ((int64_t) lhssmall + (int64_t) rhssmall - 1) / + rhssmall; + else if ((lhssmall < 0) && (rhssmall < 0)) + q = ((int64_t) lhssmall + (int64_t) rhssmall + 1) / + rhssmall; + else + q = lhssmall / rhssmall; + isl_sioimath_set_small(dst, q); + return; + } + + impz_cdiv_q(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +/* Divide lhs by rhs, rounding to negative infinity (Floor). + */ +inline void isl_sioimath_fdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + int32_t q; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + if ((lhssmall < 0) && (rhssmall >= 0)) + q = ((int64_t) lhssmall - ((int64_t) rhssmall - 1)) / + rhssmall; + else if ((lhssmall >= 0) && (rhssmall < 0)) + q = ((int64_t) lhssmall - ((int64_t) rhssmall + 1)) / + rhssmall; + else + q = lhssmall / rhssmall; + isl_sioimath_set_small(dst, q); + return; + } + + impz_fdiv_q(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +/* Compute the division of lhs by a rhs of type unsigned long, rounding towards + * negative infinity (Floor). + */ +inline void isl_sioimath_fdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, q; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && (rhs <= INT32_MAX)) { + if (lhssmall >= 0) + q = (uint32_t) lhssmall / rhs; + else + q = ((int64_t) lhssmall - ((int64_t) rhs - 1)) / + (int64_t) rhs; + isl_sioimath_set_small(dst, q); + return; + } + + impz_fdiv_q(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_uiarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +/* Get the remainder of: lhs divided by rhs rounded towards negative infinite + * (Floor). + */ +inline void isl_sioimath_fdiv_r(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int64_t lhssmall, rhssmall; + int32_t r; + + if (isl_sioimath_is_small(lhs) && isl_sioimath_is_small(rhs)) { + lhssmall = isl_sioimath_get_small(lhs); + rhssmall = isl_sioimath_get_small(rhs); + r = (rhssmall + lhssmall % rhssmall) % rhssmall; + isl_sioimath_set_small(dst, r); + return; + } + + impz_fdiv_r(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +void isl_sioimath_read(isl_sioimath_ptr dst, const char *str); + +/* Return: + * +1 for a positive number + * -1 for a negative number + * 0 if the number is zero + */ +inline int isl_sioimath_sgn(isl_sioimath_src arg) +{ + int32_t small; + + if (isl_sioimath_decode_small(arg, &small)) + return (small > 0) - (small < 0); + + return mp_int_compare_zero(isl_sioimath_get_big(arg)); +} + +/* Return: + * +1 if lhs > rhs + * -1 if lhs < rhs + * 0 if lhs = rhs + */ +inline int isl_sioimath_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) + return (lhssmall > rhssmall) - (lhssmall < rhssmall); + + if (isl_sioimath_decode_small(rhs, &rhssmall)) + return mp_int_compare_value( + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhssmall); + + if (isl_sioimath_decode_small(lhs, &lhssmall)) + return -mp_int_compare_value( + isl_sioimath_bigarg_src(rhs, &rhsscratch), lhssmall); + + return mp_int_compare( + isl_sioimath_get_big(lhs), isl_sioimath_get_big(rhs)); +} + +/* As isl_sioimath_cmp, but with signed long rhs. + */ +inline int isl_sioimath_cmp_si(isl_sioimath_src lhs, signed long rhs) +{ + int32_t lhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall)) + return (lhssmall > rhs) - (lhssmall < rhs); + + return mp_int_compare_value(isl_sioimath_get_big(lhs), rhs); +} + +/* Return: + * +1 if |lhs| > |rhs| + * -1 if |lhs| < |rhs| + * 0 if |lhs| = |rhs| + */ +inline int isl_sioimath_abs_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + lhssmall = labs(lhssmall); + rhssmall = labs(rhssmall); + return (lhssmall > rhssmall) - (lhssmall < rhssmall); + } + + return mp_int_compare_unsigned( + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); +} + +/* Return whether lhs is divisible by rhs. + */ +inline int isl_sioimath_is_divisible_by(isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + mpz_t rem; + int cmp; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) + return lhssmall % rhssmall == 0; + + if (isl_sioimath_decode_small(rhs, &rhssmall)) + return mp_int_divisible_value( + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhssmall); + + mp_int_init(&rem); + mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch), NULL, &rem); + cmp = mp_int_compare_zero(&rem); + mp_int_clear(&rem); + return cmp == 0; +} + +/* Return a hash code of an isl_sioimath. + * The hash code for a number in small and big representation must be identical + * on the same machine because small representation if not obligatory if fits. + */ +inline uint32_t isl_sioimath_hash(isl_sioimath_src arg, uint32_t hash) +{ + int32_t small; + int i; + uint32_t num; + mp_digit digits[(sizeof(uint32_t) + sizeof(mp_digit) - 1) / + sizeof(mp_digit)]; + mp_size used; + const unsigned char *digitdata = (const unsigned char *) &digits; + + if (isl_sioimath_decode_small(arg, &small)) { + if (small < 0) + isl_hash_byte(hash, 0xFF); + num = labs(small); + + isl_siomath_uint32_to_digits(num, digits, &used); + for (i = 0; i < used * sizeof(mp_digit); i += 1) + isl_hash_byte(hash, digitdata[i]); + return hash; + } + + return isl_imath_hash(isl_sioimath_get_big(arg), hash); +} + +/* Return the number of digits in a number of the given base or more, i.e. the + * string length without sign and null terminator. + * + * Current implementation for small representation returns the maximal number + * of binary digits in that representation, which can be much larger than the + * smallest possible solution. + */ +inline size_t isl_sioimath_sizeinbase(isl_sioimath_src arg, int base) +{ + int32_t small; + + if (isl_sioimath_decode_small(arg, &small)) + return sizeof(int32_t) * CHAR_BIT - 1; + + return impz_sizeinbase(isl_sioimath_get_big(arg), base); +} + +void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width); +void isl_sioimath_dump(isl_sioimath_src arg); + +typedef isl_sioimath isl_int[1]; +#define isl_int_init(i) isl_sioimath_init((i)) +#define isl_int_clear(i) isl_sioimath_clear((i)) + +#define isl_int_set(r, i) isl_sioimath_set((r), *(i)) +#define isl_int_set_si(r, i) isl_sioimath_set_si((r), i) +#define isl_int_set_ui(r, i) isl_sioimath_set_ui((r), i) +#define isl_int_fits_slong(r) isl_sioimath_fits_slong(*(r)) +#define isl_int_get_si(r) isl_sioimath_get_si(*(r)) +#define isl_int_fits_ulong(r) isl_sioimath_fits_ulong(*(r)) +#define isl_int_get_ui(r) isl_sioimath_get_ui(*(r)) +#define isl_int_get_d(r) isl_sioimath_get_d(*(r)) +#define isl_int_get_str(r) isl_sioimath_get_str(*(r)) +#define isl_int_abs(r, i) isl_sioimath_abs((r), *(i)) +#define isl_int_neg(r, i) isl_sioimath_neg((r), *(i)) +#define isl_int_swap(i, j) isl_sioimath_swap((i), (j)) +#define isl_int_swap_or_set(i, j) isl_sioimath_swap((i), (j)) +#define isl_int_add_ui(r, i, j) isl_sioimath_add_ui((r), *(i), j) +#define isl_int_sub_ui(r, i, j) isl_sioimath_sub_ui((r), *(i), j) + +#define isl_int_add(r, i, j) isl_sioimath_add((r), *(i), *(j)) +#define isl_int_sub(r, i, j) isl_sioimath_sub((r), *(i), *(j)) +#define isl_int_mul(r, i, j) isl_sioimath_mul((r), *(i), *(j)) +#define isl_int_mul_2exp(r, i, j) isl_sioimath_mul_2exp((r), *(i), j) +#define isl_int_mul_si(r, i, j) isl_sioimath_mul_si((r), *(i), j) +#define isl_int_mul_ui(r, i, j) isl_sioimath_mul_ui((r), *(i), j) +#define isl_int_pow_ui(r, i, j) isl_sioimath_pow_ui((r), *(i), j) +#define isl_int_addmul(r, i, j) isl_sioimath_addmul((r), *(i), *(j)) +#define isl_int_addmul_ui(r, i, j) isl_sioimath_addmul_ui((r), *(i), j) +#define isl_int_submul(r, i, j) isl_sioimath_submul((r), *(i), *(j)) +#define isl_int_submul_ui(r, i, j) isl_sioimath_submul_ui((r), *(i), j) + +#define isl_int_gcd(r, i, j) isl_sioimath_gcd((r), *(i), *(j)) +#define isl_int_lcm(r, i, j) isl_sioimath_lcm((r), *(i), *(j)) +#define isl_int_divexact(r, i, j) isl_sioimath_tdiv_q((r), *(i), *(j)) +#define isl_int_divexact_ui(r, i, j) isl_sioimath_tdiv_q_ui((r), *(i), j) +#define isl_int_tdiv_q(r, i, j) isl_sioimath_tdiv_q((r), *(i), *(j)) +#define isl_int_cdiv_q(r, i, j) isl_sioimath_cdiv_q((r), *(i), *(j)) +#define isl_int_fdiv_q(r, i, j) isl_sioimath_fdiv_q((r), *(i), *(j)) +#define isl_int_fdiv_r(r, i, j) isl_sioimath_fdiv_r((r), *(i), *(j)) +#define isl_int_fdiv_q_ui(r, i, j) isl_sioimath_fdiv_q_ui((r), *(i), j) + +#define isl_int_read(r, s) isl_sioimath_read((r), s) +#define isl_int_sgn(i) isl_sioimath_sgn(*(i)) +#define isl_int_cmp(i, j) isl_sioimath_cmp(*(i), *(j)) +#define isl_int_cmp_si(i, si) isl_sioimath_cmp_si(*(i), si) +#define isl_int_eq(i, j) (isl_sioimath_cmp(*(i), *(j)) == 0) +#define isl_int_ne(i, j) (isl_sioimath_cmp(*(i), *(j)) != 0) +#define isl_int_lt(i, j) (isl_sioimath_cmp(*(i), *(j)) < 0) +#define isl_int_le(i, j) (isl_sioimath_cmp(*(i), *(j)) <= 0) +#define isl_int_gt(i, j) (isl_sioimath_cmp(*(i), *(j)) > 0) +#define isl_int_ge(i, j) (isl_sioimath_cmp(*(i), *(j)) >= 0) +#define isl_int_abs_cmp(i, j) isl_sioimath_abs_cmp(*(i), *(j)) +#define isl_int_abs_eq(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) == 0) +#define isl_int_abs_ne(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) != 0) +#define isl_int_abs_lt(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) < 0) +#define isl_int_abs_gt(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) > 0) +#define isl_int_abs_ge(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) >= 0) +#define isl_int_is_divisible_by(i, j) isl_sioimath_is_divisible_by(*(i), *(j)) + +#define isl_int_hash(v, h) isl_sioimath_hash(*(v), h) +#define isl_int_free_str(s) free(s) +#define isl_int_print(out, i, width) isl_sioimath_print(out, *(i), width) + +#endif /* ISL_INT_SIOIMATH_H */ Index: lib/Analysis/isl/isl_int_sioimath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_int_sioimath.c @@ -0,0 +1,221 @@ +#include +#include + +#include + +extern int isl_sioimath_decode(isl_sioimath val, int32_t *small, mp_int *big); +extern int isl_sioimath_decode_big(isl_sioimath val, mp_int *big); +extern int isl_sioimath_decode_small(isl_sioimath val, int32_t *small); + +extern isl_sioimath isl_sioimath_encode_small(int32_t val); +extern isl_sioimath isl_sioimath_encode_big(mp_int val); +extern int isl_sioimath_is_small(isl_sioimath val); +extern int isl_sioimath_is_big(isl_sioimath val); +extern int32_t isl_sioimath_get_small(isl_sioimath val); +extern mp_int isl_sioimath_get_big(isl_sioimath val); + +extern void isl_siomath_uint32_to_digits(uint32_t num, mp_digit *digits, + mp_size *used); +extern void isl_siomath_ulong_to_digits(unsigned long num, mp_digit *digits, + mp_size *used); +extern void isl_siomath_uint64_to_digits(uint64_t num, mp_digit *digits, + mp_size *used); + +extern mp_int isl_sioimath_bigarg_src(isl_sioimath arg, + isl_sioimath_scratchspace_t *scratch); +extern mp_int isl_sioimath_siarg_src(signed long arg, + isl_sioimath_scratchspace_t *scratch); +extern mp_int isl_sioimath_si64arg_src(int64_t arg, + isl_sioimath_scratchspace_t *scratch); +extern mp_int isl_sioimath_uiarg_src(unsigned long arg, + isl_sioimath_scratchspace_t *scratch); +extern mp_int isl_sioimath_reinit_big(isl_sioimath_ptr ptr); +extern void isl_sioimath_set_small(isl_sioimath_ptr ptr, int32_t val); +extern void isl_sioimath_set_int32(isl_sioimath_ptr ptr, int32_t val); +extern void isl_sioimath_set_int64(isl_sioimath_ptr ptr, int64_t val); +extern void isl_sioimath_promote(isl_sioimath_ptr dst); +extern void isl_sioimath_try_demote(isl_sioimath_ptr dst); + +extern void isl_sioimath_init(isl_sioimath_ptr dst); +extern void isl_sioimath_clear(isl_sioimath_ptr dst); +extern void isl_sioimath_set(isl_sioimath_ptr dst, isl_sioimath_src val); +extern void isl_sioimath_set_si(isl_sioimath_ptr dst, long val); +extern void isl_sioimath_set_ui(isl_sioimath_ptr dst, unsigned long val); +extern int isl_sioimath_fits_slong(isl_sioimath_src val); +extern long isl_sioimath_get_si(isl_sioimath_src val); +extern int isl_sioimath_fits_ulong(isl_sioimath_src val); +extern unsigned long isl_sioimath_get_ui(isl_sioimath_src val); +extern double isl_sioimath_get_d(isl_sioimath_src val); +extern char *isl_sioimath_get_str(isl_sioimath_src val); +extern void isl_sioimath_abs(isl_sioimath_ptr dst, isl_sioimath_src arg); +extern void isl_sioimath_neg(isl_sioimath_ptr dst, isl_sioimath_src arg); +extern void isl_sioimath_swap(isl_sioimath_ptr lhs, isl_sioimath_ptr rhs); +extern void isl_sioimath_add_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs); +extern void isl_sioimath_sub_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs); + +extern void isl_sioimath_add(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_sub(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_mul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_mul_2exp(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs); +extern void isl_sioimath_mul_si(isl_sioimath_ptr dst, isl_sioimath lhs, + signed long rhs); +extern void isl_sioimath_mul_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs); +extern void isl_sioimath_pow_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs); +extern void isl_sioimath_addmul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_addmul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs); +extern void isl_sioimath_submul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_submul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs); + +/* Implements the Euclidean algorithm to compute the greatest common divisor of + * two values in small representation. + */ +static uint32_t isl_sioimath_smallgcd(int32_t lhs, int32_t rhs) +{ + uint32_t dividend, divisor, remainder; + + dividend = labs(lhs); + divisor = labs(rhs); + while (divisor) { + remainder = dividend % divisor; + dividend = divisor; + divisor = remainder; + } + + return dividend; +} + +/* Compute the greatest common divisor. + * + * Per GMP convention, gcd(0,0)==0 and otherwise always positive. + */ +void isl_sioimath_gcd(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + int32_t lhssmall, rhssmall; + uint32_t smallgcd; + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + smallgcd = isl_sioimath_smallgcd(lhssmall, rhssmall); + isl_sioimath_set_small(dst, smallgcd); + return; + } + + impz_gcd(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs)); + isl_sioimath_try_demote(dst); +} + +/* Compute the lowest common multiple of two numbers. + */ +void isl_sioimath_lcm(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + int32_t lhssmall, rhssmall; + uint32_t smallgcd; + uint64_t multiple; + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + if (lhssmall == 0 || rhssmall == 0) { + isl_sioimath_set_small(dst, 0); + return; + } + smallgcd = isl_sioimath_smallgcd(lhssmall, rhssmall); + multiple = (uint64_t) abs(lhssmall) * (uint64_t) abs(rhssmall); + isl_sioimath_set_int64(dst, multiple / smallgcd); + return; + } + + impz_lcm(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs)); + isl_sioimath_try_demote(dst); +} + +extern void isl_sioimath_tdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_tdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs); +extern void isl_sioimath_cdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_fdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +extern void isl_sioimath_fdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs); +extern void isl_sioimath_fdiv_r(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); + +/* Parse a number from a string. + * If it has less than 10 characters then it will fit into the small + * representation (i.e. strlen("2147483647")). Otherwise, let IMath parse it. + */ +void isl_sioimath_read(isl_sioimath_ptr dst, const char *str) +{ + int32_t small; + + if (strlen(str) < 10) { + small = strtol(str, NULL, 10); + isl_sioimath_set_small(dst, small); + return; + } + + mp_int_read_string(isl_sioimath_reinit_big(dst), 10, str); + isl_sioimath_try_demote(dst); +} + +extern int isl_sioimath_sgn(isl_sioimath_src arg); +extern int isl_sioimath_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs); +extern int isl_sioimath_cmp_si(isl_sioimath_src lhs, signed long rhs); +extern int isl_sioimath_abs_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs); +extern int isl_sioimath_is_divisible_by(isl_sioimath_src lhs, + isl_sioimath_src rhs); + +extern uint32_t isl_sioimath_hash(isl_sioimath_src arg, uint32_t hash); +extern size_t isl_sioimath_sizeinbase(isl_sioimath_src arg, int base); +extern void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width); + +/* Print an isl_int to FILE*. Adds space padding to the left until at least + * width characters are printed. + */ +void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width) +{ + size_t len; + int32_t small; + mp_int big; + char *buf; + + if (isl_sioimath_decode_small(i, &small)) { + fprintf(out, "%*" PRIi32, width, small); + return; + } + + big = isl_sioimath_get_big(i); + len = mp_int_string_len(big, 10); + buf = malloc(len); + mp_int_to_string(big, 10, buf, len); + fprintf(out, "%*s", width, buf); + free(buf); +} + +/* Print a number to stdout. Meant for debugging. + */ +void isl_sioimath_dump(isl_sioimath_src arg) +{ + isl_sioimath_print(stdout, arg, 0); +} Index: lib/Analysis/isl/isl_list_templ.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_list_templ.h @@ -0,0 +1,16 @@ +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) +#define xLIST(EL) EL ## _list +#define LIST(EL) xLIST(EL) + +struct LIST(EL) { + int ref; + isl_ctx *ctx; + + int n; + + size_t size; + struct EL *p[1]; +}; + +__isl_give LIST(EL) *FN(LIST(EL),dup)(__isl_keep LIST(EL) *list); Index: lib/Analysis/isl/isl_list_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_list_templ.c @@ -0,0 +1,522 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2011 INRIA Saclay + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include + +#define xCAT(A,B) A ## B +#define CAT(A,B) xCAT(A,B) +#undef EL +#define EL CAT(isl_,BASE) +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) +#define xLIST(EL) EL ## _list +#define LIST(EL) xLIST(EL) +#define xS(TYPE,NAME) struct TYPE ## _ ## NAME +#define S(TYPE,NAME) xS(TYPE,NAME) + +isl_ctx *FN(LIST(EL),get_ctx)(__isl_keep LIST(EL) *list) +{ + return list ? list->ctx : NULL; +} + +__isl_give LIST(EL) *FN(LIST(EL),alloc)(isl_ctx *ctx, int n) +{ + LIST(EL) *list; + + if (n < 0) + isl_die(ctx, isl_error_invalid, + "cannot create list of negative length", + return NULL); + list = isl_alloc(ctx, LIST(EL), + sizeof(LIST(EL)) + (n - 1) * sizeof(struct EL *)); + if (!list) + return NULL; + + list->ctx = ctx; + isl_ctx_ref(ctx); + list->ref = 1; + list->size = n; + list->n = 0; + return list; +} + +__isl_give LIST(EL) *FN(LIST(EL),copy)(__isl_keep LIST(EL) *list) +{ + if (!list) + return NULL; + + list->ref++; + return list; +} + +__isl_give LIST(EL) *FN(LIST(EL),dup)(__isl_keep LIST(EL) *list) +{ + int i; + LIST(EL) *dup; + + if (!list) + return NULL; + + dup = FN(LIST(EL),alloc)(FN(LIST(EL),get_ctx)(list), list->n); + if (!dup) + return NULL; + for (i = 0; i < list->n; ++i) + dup = FN(LIST(EL),add)(dup, FN(EL,copy)(list->p[i])); + return dup; +} + +__isl_give LIST(EL) *FN(LIST(EL),cow)(__isl_take LIST(EL) *list) +{ + if (!list) + return NULL; + + if (list->ref == 1) + return list; + list->ref--; + return FN(LIST(EL),dup)(list); +} + +/* Make sure "list" has room for at least "n" more pieces. + * Always return a list with a single reference. + * + * If there is only one reference to list, we extend it in place. + * Otherwise, we create a new LIST(EL) and copy the elements. + */ +static __isl_give LIST(EL) *FN(LIST(EL),grow)(__isl_take LIST(EL) *list, int n) +{ + isl_ctx *ctx; + int i, new_size; + LIST(EL) *res; + + if (!list) + return NULL; + if (list->ref == 1 && list->n + n <= list->size) + return list; + + ctx = FN(LIST(EL),get_ctx)(list); + new_size = ((list->n + n + 1) * 3) / 2; + if (list->ref == 1) { + res = isl_realloc(ctx, list, LIST(EL), + sizeof(LIST(EL)) + (new_size - 1) * sizeof(EL *)); + if (!res) + return FN(LIST(EL),free)(list); + res->size = new_size; + return res; + } + + if (list->n + n <= list->size && list->size < new_size) + new_size = list->size; + + res = FN(LIST(EL),alloc)(ctx, new_size); + if (!res) + return FN(LIST(EL),free)(list); + + for (i = 0; i < list->n; ++i) + res = FN(LIST(EL),add)(res, FN(EL,copy)(list->p[i])); + + FN(LIST(EL),free)(list); + return res; +} + +__isl_give LIST(EL) *FN(LIST(EL),add)(__isl_take LIST(EL) *list, + __isl_take struct EL *el) +{ + list = FN(LIST(EL),grow)(list, 1); + if (!list || !el) + goto error; + list->p[list->n] = el; + list->n++; + return list; +error: + FN(EL,free)(el); + FN(LIST(EL),free)(list); + return NULL; +} + +/* Remove the "n" elements starting at "first" from "list". + */ +__isl_give LIST(EL) *FN(LIST(EL),drop)(__isl_take LIST(EL) *list, + unsigned first, unsigned n) +{ + int i; + + if (!list) + return NULL; + if (first + n > list->n || first + n < first) + isl_die(list->ctx, isl_error_invalid, + "index out of bounds", return FN(LIST(EL),free)(list)); + if (n == 0) + return list; + list = FN(LIST(EL),cow)(list); + if (!list) + return NULL; + for (i = 0; i < n; ++i) + FN(EL,free)(list->p[first + i]); + for (i = first; i + n < list->n; ++i) + list->p[i] = list->p[i + n]; + list->n -= n; + return list; +} + +/* Insert "el" at position "pos" in "list". + * + * If there is only one reference to "list" and if it already has space + * for one extra element, we insert it directly into "list". + * Otherwise, we create a new list consisting of "el" and copied + * elements from "list". + */ +__isl_give LIST(EL) *FN(LIST(EL),insert)(__isl_take LIST(EL) *list, + unsigned pos, __isl_take struct EL *el) +{ + int i; + isl_ctx *ctx; + LIST(EL) *res; + + if (!list || !el) + goto error; + ctx = FN(LIST(EL),get_ctx)(list); + if (pos > list->n) + isl_die(ctx, isl_error_invalid, + "index out of bounds", goto error); + + if (list->ref == 1 && list->size > list->n) { + for (i = list->n - 1; i >= pos; --i) + list->p[i + 1] = list->p[i]; + list->n++; + list->p[pos] = el; + return list; + } + + res = FN(LIST(EL),alloc)(ctx, list->n + 1); + for (i = 0; i < pos; ++i) + res = FN(LIST(EL),add)(res, FN(EL,copy)(list->p[i])); + res = FN(LIST(EL),add)(res, el); + for (i = pos; i < list->n; ++i) + res = FN(LIST(EL),add)(res, FN(EL,copy)(list->p[i])); + FN(LIST(EL),free)(list); + + return res; +error: + FN(EL,free)(el); + FN(LIST(EL),free)(list); + return NULL; +} + +__isl_null LIST(EL) *FN(LIST(EL),free)(__isl_take LIST(EL) *list) +{ + int i; + + if (!list) + return NULL; + + if (--list->ref > 0) + return NULL; + + isl_ctx_deref(list->ctx); + for (i = 0; i < list->n; ++i) + FN(EL,free)(list->p[i]); + free(list); + + return NULL; +} + +int FN(FN(LIST(EL),n),BASE)(__isl_keep LIST(EL) *list) +{ + return list ? list->n : 0; +} + +__isl_give EL *FN(FN(LIST(EL),get),BASE)(__isl_keep LIST(EL) *list, int index) +{ + if (!list) + return NULL; + if (index < 0 || index >= list->n) + isl_die(list->ctx, isl_error_invalid, + "index out of bounds", return NULL); + return FN(EL,copy)(list->p[index]); +} + +/* Replace the element at position "index" in "list" by "el". + */ +__isl_give LIST(EL) *FN(FN(LIST(EL),set),BASE)(__isl_take LIST(EL) *list, + int index, __isl_take EL *el) +{ + if (!list || !el) + goto error; + if (index < 0 || index >= list->n) + isl_die(list->ctx, isl_error_invalid, + "index out of bounds", goto error); + if (list->p[index] == el) { + FN(EL,free)(el); + return list; + } + list = FN(LIST(EL),cow)(list); + if (!list) + goto error; + FN(EL,free)(list->p[index]); + list->p[index] = el; + return list; +error: + FN(EL,free)(el); + FN(LIST(EL),free)(list); + return NULL; +} + +isl_stat FN(LIST(EL),foreach)(__isl_keep LIST(EL) *list, + isl_stat (*fn)(__isl_take EL *el, void *user), void *user) +{ + int i; + + if (!list) + return isl_stat_error; + + for (i = 0; i < list->n; ++i) { + EL *el = FN(EL,copy(list->p[i])); + if (!el) + return isl_stat_error; + if (fn(el, user) < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Internal data structure for isl_*_list_sort. + * + * "cmp" is the original comparison function. + * "user" is a user provided pointer that should be passed to "cmp". + */ +S(LIST(EL),sort_data) { + int (*cmp)(__isl_keep EL *a, __isl_keep EL *b, void *user); + void *user; +}; + +/* Compare two entries of an isl_*_list based on the user provided + * comparison function on pairs of isl_* objects. + */ +static int FN(LIST(EL),cmp)(const void *a, const void *b, void *user) +{ + S(LIST(EL),sort_data) *data = user; + EL * const *el1 = a; + EL * const *el2 = b; + + return data->cmp(*el1, *el2, data->user); +} + +/* Sort the elements of "list" in ascending order according to + * comparison function "cmp". + */ +__isl_give LIST(EL) *FN(LIST(EL),sort)(__isl_take LIST(EL) *list, + int (*cmp)(__isl_keep EL *a, __isl_keep EL *b, void *user), void *user) +{ + S(LIST(EL),sort_data) data = { cmp, user }; + + if (!list) + return NULL; + if (list->n <= 1) + return list; + list = FN(LIST(EL),cow)(list); + if (!list) + return NULL; + + if (isl_sort(list->p, list->n, sizeof(list->p[0]), + &FN(LIST(EL),cmp), &data) < 0) + return FN(LIST(EL),free)(list); + + return list; +} + +/* Internal data structure for isl_*_list_foreach_scc. + * + * "list" is the original list. + * "follows" is the user provided callback that defines the edges of the graph. + */ +S(LIST(EL),foreach_scc_data) { + LIST(EL) *list; + isl_bool (*follows)(__isl_keep EL *a, __isl_keep EL *b, void *user); + void *follows_user; +}; + +/* Does element i of data->list follow element j? + * + * Use the user provided callback to find out. + */ +static isl_bool FN(LIST(EL),follows)(int i, int j, void *user) +{ + S(LIST(EL),foreach_scc_data) *data = user; + + return data->follows(data->list->p[i], data->list->p[j], + data->follows_user); +} + +/* Call "fn" on the sublist of "list" that consists of the elements + * with indices specified by the "n" elements of "pos". + */ +static isl_stat FN(LIST(EL),call_on_scc)(__isl_keep LIST(EL) *list, int *pos, + int n, isl_stat (*fn)(__isl_take LIST(EL) *scc, void *user), void *user) +{ + int i; + isl_ctx *ctx; + LIST(EL) *slice; + + ctx = FN(LIST(EL),get_ctx)(list); + slice = FN(LIST(EL),alloc)(ctx, n); + for (i = 0; i < n; ++i) { + EL *el; + + el = FN(EL,copy)(list->p[pos[i]]); + slice = FN(LIST(EL),add)(slice, el); + } + + return fn(slice, user); +} + +/* Call "fn" on each of the strongly connected components (SCCs) of + * the graph with as vertices the elements of "list" and + * a directed edge from node b to node a iff follows(a, b) + * returns 1. follows should return -1 on error. + * + * If SCC a contains a node i that follows a node j in another SCC b + * (i.e., follows(i, j, user) returns 1), then fn will be called on SCC a + * after being called on SCC b. + * + * We simply call isl_tarjan_graph_init, extract the SCCs from the result and + * call fn on each of them. + */ +isl_stat FN(LIST(EL),foreach_scc)(__isl_keep LIST(EL) *list, + isl_bool (*follows)(__isl_keep EL *a, __isl_keep EL *b, void *user), + void *follows_user, + isl_stat (*fn)(__isl_take LIST(EL) *scc, void *user), void *fn_user) +{ + S(LIST(EL),foreach_scc_data) data = { list, follows, follows_user }; + int i, n; + isl_ctx *ctx; + struct isl_tarjan_graph *g; + + if (!list) + return isl_stat_error; + if (list->n == 0) + return isl_stat_ok; + if (list->n == 1) + return fn(FN(LIST(EL),copy)(list), fn_user); + + ctx = FN(LIST(EL),get_ctx)(list); + n = list->n; + g = isl_tarjan_graph_init(ctx, n, &FN(LIST(EL),follows), &data); + if (!g) + return isl_stat_error; + + i = 0; + do { + int first; + + if (g->order[i] == -1) + isl_die(ctx, isl_error_internal, "cannot happen", + break); + first = i; + while (g->order[i] != -1) { + ++i; --n; + } + if (first == 0 && n == 0) { + isl_tarjan_graph_free(g); + return fn(FN(LIST(EL),copy)(list), fn_user); + } + if (FN(LIST(EL),call_on_scc)(list, g->order + first, i - first, + fn, fn_user) < 0) + break; + ++i; + } while (n); + + isl_tarjan_graph_free(g); + + return n > 0 ? isl_stat_error : isl_stat_ok; +} + +__isl_give LIST(EL) *FN(FN(LIST(EL),from),BASE)(__isl_take EL *el) +{ + isl_ctx *ctx; + LIST(EL) *list; + + if (!el) + return NULL; + ctx = FN(EL,get_ctx)(el); + list = FN(LIST(EL),alloc)(ctx, 1); + if (!list) + goto error; + list = FN(LIST(EL),add)(list, el); + return list; +error: + FN(EL,free)(el); + return NULL; +} + +__isl_give LIST(EL) *FN(LIST(EL),concat)(__isl_take LIST(EL) *list1, + __isl_take LIST(EL) *list2) +{ + int i; + isl_ctx *ctx; + LIST(EL) *res; + + if (!list1 || !list2) + goto error; + + ctx = FN(LIST(EL),get_ctx)(list1); + res = FN(LIST(EL),alloc)(ctx, list1->n + list2->n); + for (i = 0; i < list1->n; ++i) + res = FN(LIST(EL),add)(res, FN(EL,copy)(list1->p[i])); + for (i = 0; i < list2->n; ++i) + res = FN(LIST(EL),add)(res, FN(EL,copy)(list2->p[i])); + + FN(LIST(EL),free)(list1); + FN(LIST(EL),free)(list2); + return res; +error: + FN(LIST(EL),free)(list1); + FN(LIST(EL),free)(list2); + return NULL; +} + +__isl_give isl_printer *CAT(isl_printer_print_,LIST(BASE))( + __isl_take isl_printer *p, __isl_keep LIST(EL) *list) +{ + int i; + + if (!p || !list) + goto error; + p = isl_printer_print_str(p, "("); + for (i = 0; i < list->n; ++i) { + if (i) + p = isl_printer_print_str(p, ","); + p = CAT(isl_printer_print_,BASE)(p, list->p[i]); + } + p = isl_printer_print_str(p, ")"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +void FN(LIST(EL),dump)(__isl_keep LIST(EL) *list) +{ + isl_printer *printer; + + if (!list) + return; + + printer = isl_printer_to_file(FN(LIST(EL),get_ctx)(list), stderr); + printer = CAT(isl_printer_print_,LIST(BASE))(printer, list); + printer = isl_printer_end_line(printer); + + isl_printer_free(printer); +} Index: lib/Analysis/isl/isl_local.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_local.h @@ -0,0 +1,9 @@ +#ifndef ISL_LOCAL_H +#define ISL_LOCAL_H + +#include + +isl_bool isl_local_div_is_known(__isl_keep isl_mat *div, int pos); +int isl_local_cmp(__isl_keep isl_mat *div1, __isl_keep isl_mat *div2); + +#endif Index: lib/Analysis/isl/isl_local.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_local.c @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include + +/* Given a matrix "div" representing local variables, + * does the variable at position "pos" have an explicit representation? + */ +isl_bool isl_local_div_is_known(__isl_keep isl_mat *div, int pos) +{ + if (!div) + return isl_bool_error; + if (pos < 0 || pos >= div->n_row) + isl_die(isl_mat_get_ctx(div), isl_error_invalid, + "position out of bounds", return isl_bool_error); + return !isl_int_is_zero(div->row[pos][0]); +} + +/* Compare two matrices representing local variables, defined over + * the same space. + * + * Return -1 if "div1" is "smaller" than "div2", 1 if "div1" is "greater" + * than "div2" and 0 if they are equal. + * + * The order is fairly arbitrary. We do "prefer" divs that only involve + * earlier dimensions in the sense that we consider matrices where + * the first differing div involves earlier dimensions to be smaller. + */ +int isl_local_cmp(__isl_keep isl_mat *div1, __isl_keep isl_mat *div2) +{ + int i; + int cmp; + int known1, known2; + int last1, last2; + int n_col; + + if (div1 == div2) + return 0; + if (!div1) + return -1; + if (!div2) + return 1; + + if (div1->n_row != div2->n_row) + return div1->n_row - div2->n_row; + + n_col = isl_mat_cols(div1); + for (i = 0; i < div1->n_row; ++i) { + known1 = isl_local_div_is_known(div1, i); + known2 = isl_local_div_is_known(div2, i); + if (!known1 && !known2) + continue; + if (!known1) + return 1; + if (!known2) + return -1; + last1 = isl_seq_last_non_zero(div1->row[i] + 1, n_col - 1); + last2 = isl_seq_last_non_zero(div2->row[i] + 1, n_col - 1); + if (last1 != last2) + return last1 - last2; + cmp = isl_seq_cmp(div1->row[i], div2->row[i], n_col); + if (cmp != 0) + return cmp; + } + + return 0; +} Index: lib/Analysis/isl/isl_local_space.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_local_space.c @@ -0,0 +1,1378 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2012-2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +isl_ctx *isl_local_space_get_ctx(__isl_keep isl_local_space *ls) +{ + return ls ? ls->dim->ctx : NULL; +} + +/* Return a hash value that digests "ls". + */ +uint32_t isl_local_space_get_hash(__isl_keep isl_local_space *ls) +{ + uint32_t hash, space_hash, div_hash; + + if (!ls) + return 0; + + hash = isl_hash_init(); + space_hash = isl_space_get_hash(ls->dim); + isl_hash_hash(hash, space_hash); + div_hash = isl_mat_get_hash(ls->div); + isl_hash_hash(hash, div_hash); + + return hash; +} + +__isl_give isl_local_space *isl_local_space_alloc_div(__isl_take isl_space *dim, + __isl_take isl_mat *div) +{ + isl_ctx *ctx; + isl_local_space *ls = NULL; + + if (!dim || !div) + goto error; + + ctx = isl_space_get_ctx(dim); + ls = isl_calloc_type(ctx, struct isl_local_space); + if (!ls) + goto error; + + ls->ref = 1; + ls->dim = dim; + ls->div = div; + + return ls; +error: + isl_mat_free(div); + isl_space_free(dim); + isl_local_space_free(ls); + return NULL; +} + +__isl_give isl_local_space *isl_local_space_alloc(__isl_take isl_space *dim, + unsigned n_div) +{ + isl_ctx *ctx; + isl_mat *div; + unsigned total; + + if (!dim) + return NULL; + + total = isl_space_dim(dim, isl_dim_all); + + ctx = isl_space_get_ctx(dim); + div = isl_mat_alloc(ctx, n_div, 1 + 1 + total + n_div); + return isl_local_space_alloc_div(dim, div); +} + +__isl_give isl_local_space *isl_local_space_from_space(__isl_take isl_space *dim) +{ + return isl_local_space_alloc(dim, 0); +} + +__isl_give isl_local_space *isl_local_space_copy(__isl_keep isl_local_space *ls) +{ + if (!ls) + return NULL; + + ls->ref++; + return ls; +} + +__isl_give isl_local_space *isl_local_space_dup(__isl_keep isl_local_space *ls) +{ + if (!ls) + return NULL; + + return isl_local_space_alloc_div(isl_space_copy(ls->dim), + isl_mat_copy(ls->div)); + +} + +__isl_give isl_local_space *isl_local_space_cow(__isl_take isl_local_space *ls) +{ + if (!ls) + return NULL; + + if (ls->ref == 1) + return ls; + ls->ref--; + return isl_local_space_dup(ls); +} + +__isl_null isl_local_space *isl_local_space_free( + __isl_take isl_local_space *ls) +{ + if (!ls) + return NULL; + + if (--ls->ref > 0) + return NULL; + + isl_space_free(ls->dim); + isl_mat_free(ls->div); + + free(ls); + + return NULL; +} + +/* Is the local space that of a parameter domain? + */ +isl_bool isl_local_space_is_params(__isl_keep isl_local_space *ls) +{ + if (!ls) + return isl_bool_error; + return isl_space_is_params(ls->dim); +} + +/* Is the local space that of a set? + */ +isl_bool isl_local_space_is_set(__isl_keep isl_local_space *ls) +{ + return ls ? isl_space_is_set(ls->dim) : isl_bool_error; +} + +/* Do "ls1" and "ls2" have the same space? + */ +isl_bool isl_local_space_has_equal_space(__isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2) +{ + if (!ls1 || !ls2) + return isl_bool_error; + + return isl_space_is_equal(ls1->dim, ls2->dim); +} + +/* Return true if the two local spaces are identical, with identical + * expressions for the integer divisions. + */ +isl_bool isl_local_space_is_equal(__isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2) +{ + isl_bool equal; + + equal = isl_local_space_has_equal_space(ls1, ls2); + if (equal < 0 || !equal) + return equal; + + if (!isl_local_space_divs_known(ls1)) + return isl_bool_false; + if (!isl_local_space_divs_known(ls2)) + return isl_bool_false; + + return isl_mat_is_equal(ls1->div, ls2->div); +} + +/* Compare two isl_local_spaces. + * + * Return -1 if "ls1" is "smaller" than "ls2", 1 if "ls1" is "greater" + * than "ls2" and 0 if they are equal. + */ +int isl_local_space_cmp(__isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2) +{ + int cmp; + + if (ls1 == ls2) + return 0; + if (!ls1) + return -1; + if (!ls2) + return 1; + + cmp = isl_space_cmp(ls1->dim, ls2->dim); + if (cmp != 0) + return cmp; + + return isl_local_cmp(ls1->div, ls2->div); +} + +int isl_local_space_dim(__isl_keep isl_local_space *ls, + enum isl_dim_type type) +{ + if (!ls) + return 0; + if (type == isl_dim_div) + return ls->div->n_row; + if (type == isl_dim_all) + return isl_space_dim(ls->dim, isl_dim_all) + ls->div->n_row; + return isl_space_dim(ls->dim, type); +} + +unsigned isl_local_space_offset(__isl_keep isl_local_space *ls, + enum isl_dim_type type) +{ + isl_space *dim; + + if (!ls) + return 0; + + dim = ls->dim; + switch (type) { + case isl_dim_cst: return 0; + case isl_dim_param: return 1; + case isl_dim_in: return 1 + dim->nparam; + case isl_dim_out: return 1 + dim->nparam + dim->n_in; + case isl_dim_div: return 1 + dim->nparam + dim->n_in + dim->n_out; + default: return 0; + } +} + +/* Return the position of the dimension of the given type and name + * in "ls". + * Return -1 if no such dimension can be found. + */ +int isl_local_space_find_dim_by_name(__isl_keep isl_local_space *ls, + enum isl_dim_type type, const char *name) +{ + if (!ls) + return -1; + if (type == isl_dim_div) + return -1; + return isl_space_find_dim_by_name(ls->dim, type, name); +} + +/* Does the given dimension have a name? + */ +isl_bool isl_local_space_has_dim_name(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos) +{ + return ls ? isl_space_has_dim_name(ls->dim, type, pos) : isl_bool_error; +} + +const char *isl_local_space_get_dim_name(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos) +{ + return ls ? isl_space_get_dim_name(ls->dim, type, pos) : NULL; +} + +isl_bool isl_local_space_has_dim_id(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos) +{ + return ls ? isl_space_has_dim_id(ls->dim, type, pos) : isl_bool_error; +} + +__isl_give isl_id *isl_local_space_get_dim_id(__isl_keep isl_local_space *ls, + enum isl_dim_type type, unsigned pos) +{ + return ls ? isl_space_get_dim_id(ls->dim, type, pos) : NULL; +} + +__isl_give isl_aff *isl_local_space_get_div(__isl_keep isl_local_space *ls, + int pos) +{ + isl_aff *aff; + + if (!ls) + return NULL; + + if (pos < 0 || pos >= ls->div->n_row) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "index out of bounds", return NULL); + + if (isl_int_is_zero(ls->div->row[pos][0])) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "expression of div unknown", return NULL); + if (!isl_local_space_is_set(ls)) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "cannot represent divs of map spaces", return NULL); + + aff = isl_aff_alloc(isl_local_space_copy(ls)); + if (!aff) + return NULL; + isl_seq_cpy(aff->v->el, ls->div->row[pos], aff->v->size); + return aff; +} + +__isl_give isl_space *isl_local_space_get_space(__isl_keep isl_local_space *ls) +{ + if (!ls) + return NULL; + + return isl_space_copy(ls->dim); +} + +/* Replace the identifier of the tuple of type "type" by "id". + */ +__isl_give isl_local_space *isl_local_space_set_tuple_id( + __isl_take isl_local_space *ls, + enum isl_dim_type type, __isl_take isl_id *id) +{ + ls = isl_local_space_cow(ls); + if (!ls) + goto error; + ls->dim = isl_space_set_tuple_id(ls->dim, type, id); + if (!ls->dim) + return isl_local_space_free(ls); + return ls; +error: + isl_id_free(id); + return NULL; +} + +__isl_give isl_local_space *isl_local_space_set_dim_name( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, const char *s) +{ + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + ls->dim = isl_space_set_dim_name(ls->dim, type, pos, s); + if (!ls->dim) + return isl_local_space_free(ls); + + return ls; +} + +__isl_give isl_local_space *isl_local_space_set_dim_id( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + ls = isl_local_space_cow(ls); + if (!ls) + goto error; + ls->dim = isl_space_set_dim_id(ls->dim, type, pos, id); + if (!ls->dim) + return isl_local_space_free(ls); + + return ls; +error: + isl_id_free(id); + return NULL; +} + +__isl_give isl_local_space *isl_local_space_reset_space( + __isl_take isl_local_space *ls, __isl_take isl_space *dim) +{ + ls = isl_local_space_cow(ls); + if (!ls || !dim) + goto error; + + isl_space_free(ls->dim); + ls->dim = dim; + + return ls; +error: + isl_local_space_free(ls); + isl_space_free(dim); + return NULL; +} + +/* Reorder the columns of the given div definitions according to the + * given reordering. + * The order of the divs themselves is assumed not to change. + */ +static __isl_give isl_mat *reorder_divs(__isl_take isl_mat *div, + __isl_take isl_reordering *r) +{ + int i, j; + isl_mat *mat; + int extra; + + if (!div || !r) + goto error; + + extra = isl_space_dim(r->dim, isl_dim_all) + div->n_row - r->len; + mat = isl_mat_alloc(div->ctx, div->n_row, div->n_col + extra); + if (!mat) + goto error; + + for (i = 0; i < div->n_row; ++i) { + isl_seq_cpy(mat->row[i], div->row[i], 2); + isl_seq_clr(mat->row[i] + 2, mat->n_col - 2); + for (j = 0; j < r->len; ++j) + isl_int_set(mat->row[i][2 + r->pos[j]], + div->row[i][2 + j]); + } + + isl_reordering_free(r); + isl_mat_free(div); + return mat; +error: + isl_reordering_free(r); + isl_mat_free(div); + return NULL; +} + +/* Reorder the dimensions of "ls" according to the given reordering. + * The reordering r is assumed to have been extended with the local + * variables, leaving them in the same order. + */ +__isl_give isl_local_space *isl_local_space_realign( + __isl_take isl_local_space *ls, __isl_take isl_reordering *r) +{ + ls = isl_local_space_cow(ls); + if (!ls || !r) + goto error; + + ls->div = reorder_divs(ls->div, isl_reordering_copy(r)); + if (!ls->div) + goto error; + + ls = isl_local_space_reset_space(ls, isl_space_copy(r->dim)); + + isl_reordering_free(r); + return ls; +error: + isl_local_space_free(ls); + isl_reordering_free(r); + return NULL; +} + +__isl_give isl_local_space *isl_local_space_add_div( + __isl_take isl_local_space *ls, __isl_take isl_vec *div) +{ + ls = isl_local_space_cow(ls); + if (!ls || !div) + goto error; + + if (ls->div->n_col != div->size) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "incompatible dimensions", goto error); + + ls->div = isl_mat_add_zero_cols(ls->div, 1); + ls->div = isl_mat_add_rows(ls->div, 1); + if (!ls->div) + goto error; + + isl_seq_cpy(ls->div->row[ls->div->n_row - 1], div->el, div->size); + isl_int_set_si(ls->div->row[ls->div->n_row - 1][div->size], 0); + + isl_vec_free(div); + return ls; +error: + isl_local_space_free(ls); + isl_vec_free(div); + return NULL; +} + +__isl_give isl_local_space *isl_local_space_replace_divs( + __isl_take isl_local_space *ls, __isl_take isl_mat *div) +{ + ls = isl_local_space_cow(ls); + + if (!ls || !div) + goto error; + + isl_mat_free(ls->div); + ls->div = div; + return ls; +error: + isl_mat_free(div); + isl_local_space_free(ls); + return NULL; +} + +/* Copy row "s" of "src" to row "d" of "dst", applying the expansion + * defined by "exp". + */ +static void expand_row(__isl_keep isl_mat *dst, int d, + __isl_keep isl_mat *src, int s, int *exp) +{ + int i; + unsigned c = src->n_col - src->n_row; + + isl_seq_cpy(dst->row[d], src->row[s], c); + isl_seq_clr(dst->row[d] + c, dst->n_col - c); + + for (i = 0; i < s; ++i) + isl_int_set(dst->row[d][c + exp[i]], src->row[s][c + i]); +} + +/* Compare (known) divs. + * Return non-zero if at least one of the two divs is unknown. + * In particular, if both divs are unknown, we respect their + * current order. Otherwise, we sort the known div after the unknown + * div only if the known div depends on the unknown div. + */ +static int cmp_row(isl_int *row_i, isl_int *row_j, int i, int j, + unsigned n_row, unsigned n_col) +{ + int li, lj; + int unknown_i, unknown_j; + + unknown_i = isl_int_is_zero(row_i[0]); + unknown_j = isl_int_is_zero(row_j[0]); + + if (unknown_i && unknown_j) + return i - j; + + if (unknown_i) + li = n_col - n_row + i; + else + li = isl_seq_last_non_zero(row_i, n_col); + if (unknown_j) + lj = n_col - n_row + j; + else + lj = isl_seq_last_non_zero(row_j, n_col); + + if (li != lj) + return li - lj; + + return isl_seq_cmp(row_i, row_j, n_col); +} + +/* Call cmp_row for divs in a matrix. + */ +int isl_mat_cmp_div(__isl_keep isl_mat *div, int i, int j) +{ + return cmp_row(div->row[i], div->row[j], i, j, div->n_row, div->n_col); +} + +/* Call cmp_row for divs in a basic map. + */ +static int bmap_cmp_row(__isl_keep isl_basic_map *bmap, int i, int j, + unsigned total) +{ + return cmp_row(bmap->div[i], bmap->div[j], i, j, bmap->n_div, total); +} + +/* Sort the divs in "bmap". + * + * We first make sure divs are placed after divs on which they depend. + * Then we perform a simple insertion sort based on the same ordering + * that is used in isl_merge_divs. + */ +__isl_give isl_basic_map *isl_basic_map_sort_divs( + __isl_take isl_basic_map *bmap) +{ + int i, j; + unsigned total; + + bmap = isl_basic_map_order_divs(bmap); + if (!bmap) + return NULL; + if (bmap->n_div <= 1) + return bmap; + + total = 2 + isl_basic_map_total_dim(bmap); + for (i = 1; i < bmap->n_div; ++i) { + for (j = i - 1; j >= 0; --j) { + if (bmap_cmp_row(bmap, j, j + 1, total) <= 0) + break; + isl_basic_map_swap_div(bmap, j, j + 1); + } + } + + return bmap; +} + +/* Sort the divs in the basic maps of "map". + */ +__isl_give isl_map *isl_map_sort_divs(__isl_take isl_map *map) +{ + return isl_map_inline_foreach_basic_map(map, &isl_basic_map_sort_divs); +} + +/* Combine the two lists of divs into a single list. + * For each row i in div1, exp1[i] is set to the position of the corresponding + * row in the result. Similarly for div2 and exp2. + * This function guarantees + * exp1[i] >= i + * exp1[i+1] > exp1[i] + * For optimal merging, the two input list should have been sorted. + */ +__isl_give isl_mat *isl_merge_divs(__isl_keep isl_mat *div1, + __isl_keep isl_mat *div2, int *exp1, int *exp2) +{ + int i, j, k; + isl_mat *div = NULL; + unsigned d; + + if (!div1 || !div2) + return NULL; + + d = div1->n_col - div1->n_row; + div = isl_mat_alloc(div1->ctx, 1 + div1->n_row + div2->n_row, + d + div1->n_row + div2->n_row); + if (!div) + return NULL; + + for (i = 0, j = 0, k = 0; i < div1->n_row && j < div2->n_row; ++k) { + int cmp; + + expand_row(div, k, div1, i, exp1); + expand_row(div, k + 1, div2, j, exp2); + + cmp = isl_mat_cmp_div(div, k, k + 1); + if (cmp == 0) { + exp1[i++] = k; + exp2[j++] = k; + } else if (cmp < 0) { + exp1[i++] = k; + } else { + exp2[j++] = k; + isl_seq_cpy(div->row[k], div->row[k + 1], div->n_col); + } + } + for (; i < div1->n_row; ++i, ++k) { + expand_row(div, k, div1, i, exp1); + exp1[i] = k; + } + for (; j < div2->n_row; ++j, ++k) { + expand_row(div, k, div2, j, exp2); + exp2[j] = k; + } + + div->n_row = k; + div->n_col = d + k; + + return div; +} + +/* Swap divs "a" and "b" in "ls". + */ +__isl_give isl_local_space *isl_local_space_swap_div( + __isl_take isl_local_space *ls, int a, int b) +{ + int offset; + + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + if (a < 0 || a >= ls->div->n_row || b < 0 || b >= ls->div->n_row) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "index out of bounds", return isl_local_space_free(ls)); + offset = ls->div->n_col - ls->div->n_row; + ls->div = isl_mat_swap_cols(ls->div, offset + a, offset + b); + ls->div = isl_mat_swap_rows(ls->div, a, b); + if (!ls->div) + return isl_local_space_free(ls); + return ls; +} + +/* Construct a local space that contains all the divs in either + * "ls1" or "ls2". + */ +__isl_give isl_local_space *isl_local_space_intersect( + __isl_take isl_local_space *ls1, __isl_take isl_local_space *ls2) +{ + isl_ctx *ctx; + int *exp1 = NULL; + int *exp2 = NULL; + isl_mat *div; + int equal; + + if (!ls1 || !ls2) + goto error; + + ctx = isl_local_space_get_ctx(ls1); + if (!isl_space_is_equal(ls1->dim, ls2->dim)) + isl_die(ctx, isl_error_invalid, + "spaces should be identical", goto error); + + if (ls2->div->n_row == 0) { + isl_local_space_free(ls2); + return ls1; + } + + if (ls1->div->n_row == 0) { + isl_local_space_free(ls1); + return ls2; + } + + exp1 = isl_alloc_array(ctx, int, ls1->div->n_row); + exp2 = isl_alloc_array(ctx, int, ls2->div->n_row); + if (!exp1 || !exp2) + goto error; + + div = isl_merge_divs(ls1->div, ls2->div, exp1, exp2); + if (!div) + goto error; + + equal = isl_mat_is_equal(ls1->div, div); + if (equal < 0) + goto error; + if (!equal) + ls1 = isl_local_space_cow(ls1); + if (!ls1) + goto error; + + free(exp1); + free(exp2); + isl_local_space_free(ls2); + isl_mat_free(ls1->div); + ls1->div = div; + + return ls1; +error: + free(exp1); + free(exp2); + isl_local_space_free(ls1); + isl_local_space_free(ls2); + return NULL; +} + +/* Does "ls" have an explicit representation for div "div"? + */ +isl_bool isl_local_space_div_is_known(__isl_keep isl_local_space *ls, int div) +{ + if (!ls) + return isl_bool_error; + return isl_local_div_is_known(ls->div, div); +} + +/* Does "ls" have an explicit representation for all local variables? + */ +isl_bool isl_local_space_divs_known(__isl_keep isl_local_space *ls) +{ + int i; + + if (!ls) + return isl_bool_error; + + for (i = 0; i < ls->div->n_row; ++i) { + isl_bool known = isl_local_space_div_is_known(ls, i); + if (known < 0 || !known) + return known; + } + + return isl_bool_true; +} + +__isl_give isl_local_space *isl_local_space_domain( + __isl_take isl_local_space *ls) +{ + ls = isl_local_space_drop_dims(ls, isl_dim_out, + 0, isl_local_space_dim(ls, isl_dim_out)); + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + ls->dim = isl_space_domain(ls->dim); + if (!ls->dim) + return isl_local_space_free(ls); + return ls; +} + +__isl_give isl_local_space *isl_local_space_range( + __isl_take isl_local_space *ls) +{ + ls = isl_local_space_drop_dims(ls, isl_dim_in, + 0, isl_local_space_dim(ls, isl_dim_in)); + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + ls->dim = isl_space_range(ls->dim); + if (!ls->dim) + return isl_local_space_free(ls); + return ls; +} + +/* Construct a local space for a map that has the given local + * space as domain and that has a zero-dimensional range. + */ +__isl_give isl_local_space *isl_local_space_from_domain( + __isl_take isl_local_space *ls) +{ + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + ls->dim = isl_space_from_domain(ls->dim); + if (!ls->dim) + return isl_local_space_free(ls); + return ls; +} + +__isl_give isl_local_space *isl_local_space_add_dims( + __isl_take isl_local_space *ls, enum isl_dim_type type, unsigned n) +{ + int pos; + + if (!ls) + return NULL; + pos = isl_local_space_dim(ls, type); + return isl_local_space_insert_dims(ls, type, pos, n); +} + +/* Remove common factor of non-constant terms and denominator. + */ +static void normalize_div(__isl_keep isl_local_space *ls, int div) +{ + isl_ctx *ctx = ls->div->ctx; + unsigned total = ls->div->n_col - 2; + + isl_seq_gcd(ls->div->row[div] + 2, total, &ctx->normalize_gcd); + isl_int_gcd(ctx->normalize_gcd, + ctx->normalize_gcd, ls->div->row[div][0]); + if (isl_int_is_one(ctx->normalize_gcd)) + return; + + isl_seq_scale_down(ls->div->row[div] + 2, ls->div->row[div] + 2, + ctx->normalize_gcd, total); + isl_int_divexact(ls->div->row[div][0], ls->div->row[div][0], + ctx->normalize_gcd); + isl_int_fdiv_q(ls->div->row[div][1], ls->div->row[div][1], + ctx->normalize_gcd); +} + +/* Exploit the equalities in "eq" to simplify the expressions of + * the integer divisions in "ls". + * The integer divisions in "ls" are assumed to appear as regular + * dimensions in "eq". + */ +__isl_give isl_local_space *isl_local_space_substitute_equalities( + __isl_take isl_local_space *ls, __isl_take isl_basic_set *eq) +{ + int i, j, k; + unsigned total; + unsigned n_div; + + if (!ls || !eq) + goto error; + + total = isl_space_dim(eq->dim, isl_dim_all); + if (isl_local_space_dim(ls, isl_dim_all) != total) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "spaces don't match", goto error); + total++; + n_div = eq->n_div; + for (i = 0; i < eq->n_eq; ++i) { + j = isl_seq_last_non_zero(eq->eq[i], total + n_div); + if (j < 0 || j == 0 || j >= total) + continue; + + for (k = 0; k < ls->div->n_row; ++k) { + if (isl_int_is_zero(ls->div->row[k][1 + j])) + continue; + ls = isl_local_space_cow(ls); + if (!ls) + goto error; + ls->div = isl_mat_cow(ls->div); + if (!ls->div) + goto error; + isl_seq_elim(ls->div->row[k] + 1, eq->eq[i], j, total, + &ls->div->row[k][0]); + normalize_div(ls, k); + } + } + + isl_basic_set_free(eq); + return ls; +error: + isl_basic_set_free(eq); + isl_local_space_free(ls); + return NULL; +} + +/* Plug in the affine expressions "subs" of length "subs_len" (including + * the denominator and the constant term) into the variable at position "pos" + * of the "n" div expressions starting at "first". + * + * Let i be the dimension to replace and let "subs" be of the form + * + * f/d + * + * Any integer division starting at "first" with a non-zero coefficient for i, + * + * floor((a i + g)/m) + * + * is replaced by + * + * floor((a f + d g)/(m d)) + */ +__isl_give isl_local_space *isl_local_space_substitute_seq( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, isl_int *subs, int subs_len, + int first, int n) +{ + int i; + isl_int v; + + if (n == 0) + return ls; + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + ls->div = isl_mat_cow(ls->div); + if (!ls->div) + return isl_local_space_free(ls); + + if (first + n > ls->div->n_row) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "index out of bounds", return isl_local_space_free(ls)); + + pos += isl_local_space_offset(ls, type); + + isl_int_init(v); + for (i = first; i < first + n; ++i) { + if (isl_int_is_zero(ls->div->row[i][1 + pos])) + continue; + isl_seq_substitute(ls->div->row[i], pos, subs, + ls->div->n_col, subs_len, v); + normalize_div(ls, i); + } + isl_int_clear(v); + + return ls; +} + +/* Plug in "subs" for dimension "type", "pos" in the integer divisions + * of "ls". + * + * Let i be the dimension to replace and let "subs" be of the form + * + * f/d + * + * Any integer division with a non-zero coefficient for i, + * + * floor((a i + g)/m) + * + * is replaced by + * + * floor((a f + d g)/(m d)) + */ +__isl_give isl_local_space *isl_local_space_substitute( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs) +{ + ls = isl_local_space_cow(ls); + if (!ls || !subs) + return isl_local_space_free(ls); + + if (!isl_space_is_equal(ls->dim, subs->ls->dim)) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "spaces don't match", return isl_local_space_free(ls)); + if (isl_local_space_dim(subs->ls, isl_dim_div) != 0) + isl_die(isl_local_space_get_ctx(ls), isl_error_unsupported, + "cannot handle divs yet", + return isl_local_space_free(ls)); + + return isl_local_space_substitute_seq(ls, type, pos, subs->v->el, + subs->v->size, 0, ls->div->n_row); +} + +int isl_local_space_is_named_or_nested(__isl_keep isl_local_space *ls, + enum isl_dim_type type) +{ + if (!ls) + return -1; + return isl_space_is_named_or_nested(ls->dim, type); +} + +__isl_give isl_local_space *isl_local_space_drop_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_ctx *ctx; + + if (!ls) + return NULL; + if (n == 0 && !isl_local_space_is_named_or_nested(ls, type)) + return ls; + + ctx = isl_local_space_get_ctx(ls); + if (first + n > isl_local_space_dim(ls, type)) + isl_die(ctx, isl_error_invalid, "range out of bounds", + return isl_local_space_free(ls)); + + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + if (type == isl_dim_div) { + ls->div = isl_mat_drop_rows(ls->div, first, n); + } else { + ls->dim = isl_space_drop_dims(ls->dim, type, first, n); + if (!ls->dim) + return isl_local_space_free(ls); + } + + first += 1 + isl_local_space_offset(ls, type); + ls->div = isl_mat_drop_cols(ls->div, first, n); + if (!ls->div) + return isl_local_space_free(ls); + + return ls; +} + +__isl_give isl_local_space *isl_local_space_insert_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_ctx *ctx; + + if (!ls) + return NULL; + if (n == 0 && !isl_local_space_is_named_or_nested(ls, type)) + return ls; + + ctx = isl_local_space_get_ctx(ls); + if (first > isl_local_space_dim(ls, type)) + isl_die(ctx, isl_error_invalid, "position out of bounds", + return isl_local_space_free(ls)); + + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + if (type == isl_dim_div) { + ls->div = isl_mat_insert_zero_rows(ls->div, first, n); + } else { + ls->dim = isl_space_insert_dims(ls->dim, type, first, n); + if (!ls->dim) + return isl_local_space_free(ls); + } + + first += 1 + isl_local_space_offset(ls, type); + ls->div = isl_mat_insert_zero_cols(ls->div, first, n); + if (!ls->div) + return isl_local_space_free(ls); + + return ls; +} + +/* Check if the constraints pointed to by "constraint" is a div + * constraint corresponding to div "div" in "ls". + * + * That is, if div = floor(f/m), then check if the constraint is + * + * f - m d >= 0 + * or + * -(f-(m-1)) + m d >= 0 + */ +int isl_local_space_is_div_constraint(__isl_keep isl_local_space *ls, + isl_int *constraint, unsigned div) +{ + unsigned pos; + + if (!ls) + return -1; + + if (isl_int_is_zero(ls->div->row[div][0])) + return 0; + + pos = isl_local_space_offset(ls, isl_dim_div) + div; + + if (isl_int_eq(constraint[pos], ls->div->row[div][0])) { + int neg; + isl_int_sub(ls->div->row[div][1], + ls->div->row[div][1], ls->div->row[div][0]); + isl_int_add_ui(ls->div->row[div][1], ls->div->row[div][1], 1); + neg = isl_seq_is_neg(constraint, ls->div->row[div]+1, pos); + isl_int_sub_ui(ls->div->row[div][1], ls->div->row[div][1], 1); + isl_int_add(ls->div->row[div][1], + ls->div->row[div][1], ls->div->row[div][0]); + if (!neg) + return 0; + if (isl_seq_first_non_zero(constraint+pos+1, + ls->div->n_row-div-1) != -1) + return 0; + } else if (isl_int_abs_eq(constraint[pos], ls->div->row[div][0])) { + if (!isl_seq_eq(constraint, ls->div->row[div]+1, pos)) + return 0; + if (isl_seq_first_non_zero(constraint+pos+1, + ls->div->n_row-div-1) != -1) + return 0; + } else + return 0; + + return 1; +} + +/* + * Set active[i] to 1 if the dimension at position i is involved + * in the linear expression l. + */ +int *isl_local_space_get_active(__isl_keep isl_local_space *ls, isl_int *l) +{ + int i, j; + isl_ctx *ctx; + int *active = NULL; + unsigned total; + unsigned offset; + + ctx = isl_local_space_get_ctx(ls); + total = isl_local_space_dim(ls, isl_dim_all); + active = isl_calloc_array(ctx, int, total); + if (total && !active) + return NULL; + + for (i = 0; i < total; ++i) + active[i] = !isl_int_is_zero(l[i]); + + offset = isl_local_space_offset(ls, isl_dim_div) - 1; + for (i = ls->div->n_row - 1; i >= 0; --i) { + if (!active[offset + i]) + continue; + for (j = 0; j < total; ++j) + active[j] |= !isl_int_is_zero(ls->div->row[i][2 + j]); + } + + return active; +} + +/* Given a local space "ls" of a set, create a local space + * for the lift of the set. In particular, the result + * is of the form [dim -> local[..]], with ls->div->n_row variables in the + * range of the wrapped map. + */ +__isl_give isl_local_space *isl_local_space_lift( + __isl_take isl_local_space *ls) +{ + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + ls->dim = isl_space_lift(ls->dim, ls->div->n_row); + ls->div = isl_mat_drop_rows(ls->div, 0, ls->div->n_row); + if (!ls->dim || !ls->div) + return isl_local_space_free(ls); + + return ls; +} + +/* Construct a basic map that maps a set living in local space "ls" + * to the corresponding lifted local space. + */ +__isl_give isl_basic_map *isl_local_space_lifting( + __isl_take isl_local_space *ls) +{ + isl_basic_map *lifting; + isl_basic_set *bset; + + if (!ls) + return NULL; + if (!isl_local_space_is_set(ls)) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "lifting only defined on set spaces", goto error); + + bset = isl_basic_set_from_local_space(ls); + lifting = isl_basic_set_unwrap(isl_basic_set_lift(bset)); + lifting = isl_basic_map_domain_map(lifting); + lifting = isl_basic_map_reverse(lifting); + + return lifting; +error: + isl_local_space_free(ls); + return NULL; +} + +/* Compute the preimage of "ls" under the function represented by "ma". + * In other words, plug in "ma" in "ls". The result is a local space + * that is part of the domain space of "ma". + * + * If the divs in "ls" are represented as + * + * floor((a_i(p) + b_i x + c_i(divs))/n_i) + * + * and ma is represented by + * + * x = D(p) + F(y) + G(divs') + * + * then the resulting divs are + * + * floor((a_i(p) + b_i D(p) + b_i F(y) + B_i G(divs') + c_i(divs))/n_i) + * + * We first copy over the divs from "ma" and then + * we add the modified divs from "ls". + */ +__isl_give isl_local_space *isl_local_space_preimage_multi_aff( + __isl_take isl_local_space *ls, __isl_take isl_multi_aff *ma) +{ + int i; + isl_space *space; + isl_local_space *res = NULL; + int n_div_ls, n_div_ma; + isl_int f, c1, c2, g; + + ma = isl_multi_aff_align_divs(ma); + if (!ls || !ma) + goto error; + if (!isl_space_is_range_internal(ls->dim, ma->space)) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "spaces don't match", goto error); + + n_div_ls = isl_local_space_dim(ls, isl_dim_div); + n_div_ma = ma->n ? isl_aff_dim(ma->p[0], isl_dim_div) : 0; + + space = isl_space_domain(isl_multi_aff_get_space(ma)); + res = isl_local_space_alloc(space, n_div_ma + n_div_ls); + if (!res) + goto error; + + if (n_div_ma) { + isl_mat_free(res->div); + res->div = isl_mat_copy(ma->p[0]->ls->div); + res->div = isl_mat_add_zero_cols(res->div, n_div_ls); + res->div = isl_mat_add_rows(res->div, n_div_ls); + if (!res->div) + goto error; + } + + isl_int_init(f); + isl_int_init(c1); + isl_int_init(c2); + isl_int_init(g); + + for (i = 0; i < ls->div->n_row; ++i) { + if (isl_int_is_zero(ls->div->row[i][0])) { + isl_int_set_si(res->div->row[n_div_ma + i][0], 0); + continue; + } + isl_seq_preimage(res->div->row[n_div_ma + i], ls->div->row[i], + ma, 0, 0, n_div_ma, n_div_ls, f, c1, c2, g, 1); + normalize_div(res, n_div_ma + i); + } + + isl_int_clear(f); + isl_int_clear(c1); + isl_int_clear(c2); + isl_int_clear(g); + + isl_local_space_free(ls); + isl_multi_aff_free(ma); + return res; +error: + isl_local_space_free(ls); + isl_multi_aff_free(ma); + isl_local_space_free(res); + return NULL; +} + +/* Move the "n" dimensions of "src_type" starting at "src_pos" of "ls" + * to dimensions of "dst_type" at "dst_pos". + * + * Moving to/from local dimensions is not allowed. + * We currently assume that the dimension type changes. + */ +__isl_give isl_local_space *isl_local_space_move_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + unsigned g_dst_pos; + unsigned g_src_pos; + + if (!ls) + return NULL; + if (n == 0 && + !isl_local_space_is_named_or_nested(ls, src_type) && + !isl_local_space_is_named_or_nested(ls, dst_type)) + return ls; + + if (src_pos + n > isl_local_space_dim(ls, src_type)) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "range out of bounds", return isl_local_space_free(ls)); + if (dst_pos > isl_local_space_dim(ls, dst_type)) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "position out of bounds", + return isl_local_space_free(ls)); + if (src_type == isl_dim_div) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "cannot move divs", return isl_local_space_free(ls)); + if (dst_type == isl_dim_div) + isl_die(isl_local_space_get_ctx(ls), isl_error_invalid, + "cannot move to divs", return isl_local_space_free(ls)); + if (dst_type == src_type && dst_pos == src_pos) + return ls; + if (dst_type == src_type) + isl_die(isl_local_space_get_ctx(ls), isl_error_unsupported, + "moving dims within the same type not supported", + return isl_local_space_free(ls)); + + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + g_src_pos = 1 + isl_local_space_offset(ls, src_type) + src_pos; + g_dst_pos = 1 + isl_local_space_offset(ls, dst_type) + dst_pos; + if (dst_type > src_type) + g_dst_pos -= n; + ls->div = isl_mat_move_cols(ls->div, g_dst_pos, g_src_pos, n); + if (!ls->div) + return isl_local_space_free(ls); + ls->dim = isl_space_move_dims(ls->dim, dst_type, dst_pos, + src_type, src_pos, n); + if (!ls->dim) + return isl_local_space_free(ls); + + return ls; +} + +/* Remove any internal structure of the domain of "ls". + * If there is any such internal structure in the input, + * then the name of the corresponding space is also removed. + */ +__isl_give isl_local_space *isl_local_space_flatten_domain( + __isl_take isl_local_space *ls) +{ + if (!ls) + return NULL; + + if (!ls->dim->nested[0]) + return ls; + + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + ls->dim = isl_space_flatten_domain(ls->dim); + if (!ls->dim) + return isl_local_space_free(ls); + + return ls; +} + +/* Remove any internal structure of the range of "ls". + * If there is any such internal structure in the input, + * then the name of the corresponding space is also removed. + */ +__isl_give isl_local_space *isl_local_space_flatten_range( + __isl_take isl_local_space *ls) +{ + if (!ls) + return NULL; + + if (!ls->dim->nested[1]) + return ls; + + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + ls->dim = isl_space_flatten_range(ls->dim); + if (!ls->dim) + return isl_local_space_free(ls); + + return ls; +} + +/* Given the local space "ls" of a map, return the local space of a set + * that lives in a space that wraps the space of "ls" and that has + * the same divs. + */ +__isl_give isl_local_space *isl_local_space_wrap(__isl_take isl_local_space *ls) +{ + ls = isl_local_space_cow(ls); + if (!ls) + return NULL; + + ls->dim = isl_space_wrap(ls->dim); + if (!ls->dim) + return isl_local_space_free(ls); + + return ls; +} Index: lib/Analysis/isl/isl_local_space_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_local_space_private.h @@ -0,0 +1,80 @@ +#ifndef ISL_LOCAL_SPACE_PRIVATE_H +#define ISL_LOCAL_SPACE_PRIVATE_H + +#include +#include +#include + +struct isl_local_space { + int ref; + + isl_space *dim; + isl_mat *div; +}; + +uint32_t isl_local_space_get_hash(__isl_keep isl_local_space *ls); + +__isl_give isl_local_space *isl_local_space_alloc(__isl_take isl_space *dim, + unsigned n_div); +__isl_give isl_local_space *isl_local_space_alloc_div(__isl_take isl_space *dim, + __isl_take isl_mat *div); + +__isl_give isl_local_space *isl_local_space_swap_div( + __isl_take isl_local_space *ls, int a, int b); +__isl_give isl_local_space *isl_local_space_add_div( + __isl_take isl_local_space *ls, __isl_take isl_vec *div); + +int isl_mat_cmp_div(__isl_keep isl_mat *div, int i, int j); +__isl_give isl_mat *isl_merge_divs(__isl_keep isl_mat *div1, + __isl_keep isl_mat *div2, int *exp1, int *exp2); + +unsigned isl_local_space_offset(__isl_keep isl_local_space *ls, + enum isl_dim_type type); + +__isl_give isl_local_space *isl_local_space_replace_divs( + __isl_take isl_local_space *ls, __isl_take isl_mat *div); +isl_bool isl_local_space_div_is_known(__isl_keep isl_local_space *ls, int div); +isl_bool isl_local_space_divs_known(__isl_keep isl_local_space *ls); + +__isl_give isl_local_space *isl_local_space_substitute_equalities( + __isl_take isl_local_space *ls, __isl_take isl_basic_set *eq); + +int isl_local_space_is_named_or_nested(__isl_keep isl_local_space *ls, + enum isl_dim_type type); + +isl_bool isl_local_space_has_equal_space(__isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2); + +__isl_give isl_local_space *isl_local_space_reset_space( + __isl_take isl_local_space *ls, __isl_take isl_space *dim); +__isl_give isl_local_space *isl_local_space_realign( + __isl_take isl_local_space *ls, __isl_take isl_reordering *r); + +int isl_local_space_is_div_constraint(__isl_keep isl_local_space *ls, + isl_int *constraint, unsigned div); + +int *isl_local_space_get_active(__isl_keep isl_local_space *ls, isl_int *l); + +__isl_give isl_local_space *isl_local_space_substitute_seq( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, isl_int *subs, int subs_len, + int first, int n); +__isl_give isl_local_space *isl_local_space_substitute( + __isl_take isl_local_space *ls, + enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs); + +__isl_give isl_local_space *isl_local_space_lift( + __isl_take isl_local_space *ls); + +__isl_give isl_local_space *isl_local_space_preimage_multi_aff( + __isl_take isl_local_space *ls, __isl_take isl_multi_aff *ma); + +__isl_give isl_local_space *isl_local_space_move_dims( + __isl_take isl_local_space *ls, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n); + +int isl_local_space_cmp(__isl_keep isl_local_space *ls1, + __isl_keep isl_local_space *ls2); + +#endif Index: lib/Analysis/isl/isl_lp.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_lp.c @@ -0,0 +1,362 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include "isl_tab.h" +#include +#include +#include +#include +#include +#include + +enum isl_lp_result isl_tab_solve_lp(struct isl_basic_map *bmap, int maximize, + isl_int *f, isl_int denom, isl_int *opt, + isl_int *opt_denom, + struct isl_vec **sol) +{ + struct isl_tab *tab; + enum isl_lp_result res; + unsigned dim = isl_basic_map_total_dim(bmap); + + if (maximize) + isl_seq_neg(f, f, 1 + dim); + + bmap = isl_basic_map_gauss(bmap, NULL); + tab = isl_tab_from_basic_map(bmap, 0); + res = isl_tab_min(tab, f, denom, opt, opt_denom, 0); + if (res == isl_lp_ok && sol) { + *sol = isl_tab_get_sample_value(tab); + if (!*sol) + res = isl_lp_error; + } + isl_tab_free(tab); + + if (maximize) + isl_seq_neg(f, f, 1 + dim); + if (maximize && opt) + isl_int_neg(*opt, *opt); + + return res; +} + +/* Given a basic map "bmap" and an affine combination of the variables "f" + * with denominator "denom", set *opt / *opt_denom to the minimal + * (or maximal if "maximize" is true) value attained by f/d over "bmap", + * assuming the basic map is not empty and the expression cannot attain + * arbitrarily small (or large) values. + * If opt_denom is NULL, then *opt is rounded up (or down) + * to the nearest integer. + * The return value reflects the nature of the result (empty, unbounded, + * minmimal or maximal value returned in *opt). + */ +enum isl_lp_result isl_basic_map_solve_lp(struct isl_basic_map *bmap, int max, + isl_int *f, isl_int d, isl_int *opt, + isl_int *opt_denom, + struct isl_vec **sol) +{ + if (sol) + *sol = NULL; + + if (!bmap) + return isl_lp_error; + + return isl_tab_solve_lp(bmap, max, f, d, opt, opt_denom, sol); +} + +enum isl_lp_result isl_basic_set_solve_lp(struct isl_basic_set *bset, int max, + isl_int *f, isl_int d, isl_int *opt, + isl_int *opt_denom, + struct isl_vec **sol) +{ + return isl_basic_map_solve_lp((struct isl_basic_map *)bset, max, + f, d, opt, opt_denom, sol); +} + +enum isl_lp_result isl_map_solve_lp(__isl_keep isl_map *map, int max, + isl_int *f, isl_int d, isl_int *opt, + isl_int *opt_denom, + struct isl_vec **sol) +{ + int i; + isl_int o; + isl_int t; + isl_int opt_i; + isl_int opt_denom_i; + enum isl_lp_result res; + int max_div; + isl_vec *v = NULL; + + if (!map) + return isl_lp_error; + if (map->n == 0) + return isl_lp_empty; + + max_div = 0; + for (i = 0; i < map->n; ++i) + if (map->p[i]->n_div > max_div) + max_div = map->p[i]->n_div; + if (max_div > 0) { + unsigned total = isl_space_dim(map->dim, isl_dim_all); + v = isl_vec_alloc(map->ctx, 1 + total + max_div); + if (!v) + return isl_lp_error; + isl_seq_cpy(v->el, f, 1 + total); + isl_seq_clr(v->el + 1 + total, max_div); + f = v->el; + } + + if (!opt && map->n > 1 && sol) { + isl_int_init(o); + opt = &o; + } + if (map->n > 0) + isl_int_init(opt_i); + if (map->n > 0 && opt_denom) { + isl_int_init(opt_denom_i); + isl_int_init(t); + } + + res = isl_basic_map_solve_lp(map->p[0], max, f, d, + opt, opt_denom, sol); + if (res == isl_lp_error || res == isl_lp_unbounded) + goto done; + + if (sol) + *sol = NULL; + + for (i = 1; i < map->n; ++i) { + isl_vec *sol_i = NULL; + enum isl_lp_result res_i; + int better; + + res_i = isl_basic_map_solve_lp(map->p[i], max, f, d, + &opt_i, + opt_denom ? &opt_denom_i : NULL, + sol ? &sol_i : NULL); + if (res_i == isl_lp_error || res_i == isl_lp_unbounded) { + res = res_i; + goto done; + } + if (res_i == isl_lp_empty) + continue; + if (res == isl_lp_empty) { + better = 1; + } else if (!opt_denom) { + if (max) + better = isl_int_gt(opt_i, *opt); + else + better = isl_int_lt(opt_i, *opt); + } else { + isl_int_mul(t, opt_i, *opt_denom); + isl_int_submul(t, *opt, opt_denom_i); + if (max) + better = isl_int_is_pos(t); + else + better = isl_int_is_neg(t); + } + if (better) { + res = res_i; + if (opt) + isl_int_set(*opt, opt_i); + if (opt_denom) + isl_int_set(*opt_denom, opt_denom_i); + if (sol) { + isl_vec_free(*sol); + *sol = sol_i; + } + } else + isl_vec_free(sol_i); + } + +done: + isl_vec_free(v); + if (map->n > 0 && opt_denom) { + isl_int_clear(opt_denom_i); + isl_int_clear(t); + } + if (map->n > 0) + isl_int_clear(opt_i); + if (opt == &o) + isl_int_clear(o); + return res; +} + +enum isl_lp_result isl_set_solve_lp(__isl_keep isl_set *set, int max, + isl_int *f, isl_int d, isl_int *opt, + isl_int *opt_denom, + struct isl_vec **sol) +{ + return isl_map_solve_lp((struct isl_map *)set, max, + f, d, opt, opt_denom, sol); +} + +/* Return the optimal (rational) value of "obj" over "bset", assuming + * that "obj" and "bset" have aligned parameters and divs. + * If "max" is set, then the maximal value is computed. + * Otherwise, the minimal value is computed. + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "bset" is empty. + * + * Call isl_basic_set_solve_lp and translate the results. + */ +static __isl_give isl_val *basic_set_opt_lp( + __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj) +{ + isl_ctx *ctx; + isl_val *res; + enum isl_lp_result lp_res; + + if (!bset || !obj) + return NULL; + + ctx = isl_aff_get_ctx(obj); + res = isl_val_alloc(ctx); + if (!res) + return NULL; + lp_res = isl_basic_set_solve_lp(bset, max, obj->v->el + 1, + obj->v->el[0], &res->n, &res->d, NULL); + if (lp_res == isl_lp_ok) + return isl_val_normalize(res); + isl_val_free(res); + if (lp_res == isl_lp_error) + return NULL; + if (lp_res == isl_lp_empty) + return isl_val_nan(ctx); + if (max) + return isl_val_infty(ctx); + else + return isl_val_neginfty(ctx); +} + +/* Return the optimal (rational) value of "obj" over "bset", assuming + * that "obj" and "bset" have aligned parameters. + * If "max" is set, then the maximal value is computed. + * Otherwise, the minimal value is computed. + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "bset" is empty. + * + * Align the divs of "bset" and "obj" and call basic_set_opt_lp. + */ +static __isl_give isl_val *isl_basic_set_opt_lp_val_aligned( + __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj) +{ + int *exp1 = NULL; + int *exp2 = NULL; + isl_ctx *ctx; + isl_mat *bset_div = NULL; + isl_mat *div = NULL; + isl_val *res; + int bset_n_div, obj_n_div; + + if (!bset || !obj) + return NULL; + + ctx = isl_aff_get_ctx(obj); + if (!isl_space_is_equal(bset->dim, obj->ls->dim)) + isl_die(ctx, isl_error_invalid, + "spaces don't match", return NULL); + + bset_n_div = isl_basic_set_dim(bset, isl_dim_div); + obj_n_div = isl_aff_dim(obj, isl_dim_div); + if (bset_n_div == 0 && obj_n_div == 0) + return basic_set_opt_lp(bset, max, obj); + + bset = isl_basic_set_copy(bset); + obj = isl_aff_copy(obj); + + bset_div = isl_basic_set_get_divs(bset); + exp1 = isl_alloc_array(ctx, int, bset_n_div); + exp2 = isl_alloc_array(ctx, int, obj_n_div); + if (!bset_div || (bset_n_div && !exp1) || (obj_n_div && !exp2)) + goto error; + + div = isl_merge_divs(bset_div, obj->ls->div, exp1, exp2); + + bset = isl_basic_set_expand_divs(bset, isl_mat_copy(div), exp1); + obj = isl_aff_expand_divs(obj, isl_mat_copy(div), exp2); + + res = basic_set_opt_lp(bset, max, obj); + + isl_mat_free(bset_div); + isl_mat_free(div); + free(exp1); + free(exp2); + isl_basic_set_free(bset); + isl_aff_free(obj); + + return res; +error: + isl_mat_free(div); + isl_mat_free(bset_div); + free(exp1); + free(exp2); + isl_basic_set_free(bset); + isl_aff_free(obj); + return NULL; +} + +/* Return the optimal (rational) value of "obj" over "bset". + * If "max" is set, then the maximal value is computed. + * Otherwise, the minimal value is computed. + * + * Return infinity or negative infinity if the optimal value is unbounded and + * NaN if "bset" is empty. + */ +static __isl_give isl_val *isl_basic_set_opt_lp_val( + __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj) +{ + isl_val *res; + + if (!bset || !obj) + return NULL; + + if (isl_space_match(bset->dim, isl_dim_param, + obj->ls->dim, isl_dim_param)) + return isl_basic_set_opt_lp_val_aligned(bset, max, obj); + + bset = isl_basic_set_copy(bset); + obj = isl_aff_copy(obj); + bset = isl_basic_set_align_params(bset, isl_aff_get_domain_space(obj)); + obj = isl_aff_align_params(obj, isl_basic_set_get_space(bset)); + + res = isl_basic_set_opt_lp_val_aligned(bset, max, obj); + + isl_basic_set_free(bset); + isl_aff_free(obj); + + return res; +} + +/* Return the minimal (rational) value of "obj" over "bset". + * + * Return negative infinity if the minimal value is unbounded and + * NaN if "bset" is empty. + */ +__isl_give isl_val *isl_basic_set_min_lp_val(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj) +{ + return isl_basic_set_opt_lp_val(bset, 0, obj); +} + +/* Return the maximal (rational) value of "obj" over "bset". + * + * Return infinity if the maximal value is unbounded and + * NaN if "bset" is empty. + */ +__isl_give isl_val *isl_basic_set_max_lp_val(__isl_keep isl_basic_set *bset, + __isl_keep isl_aff *obj) +{ + return isl_basic_set_opt_lp_val(bset, 1, obj); +} Index: lib/Analysis/isl/isl_lp_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_lp_private.h @@ -0,0 +1,21 @@ +#ifndef ISL_LP_PRIVATE_H +#define ISL_LP_PRIVATE_H + +#include +#include +#include + +enum isl_lp_result isl_basic_map_solve_lp(__isl_keep isl_basic_map *bmap, + int max, isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom, + __isl_give isl_vec **sol); +enum isl_lp_result isl_basic_set_solve_lp(__isl_keep isl_basic_set *bset, + int max, isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom, + __isl_give isl_vec **sol); +enum isl_lp_result isl_map_solve_lp(__isl_keep isl_map *map, int max, + isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom, + __isl_give isl_vec **sol); +enum isl_lp_result isl_set_solve_lp(__isl_keep isl_set *set, int max, + isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom, + __isl_give isl_vec **sol); + +#endif Index: lib/Analysis/isl/isl_map.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map.c @@ -0,0 +1,13048 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include "isl_space_private.h" +#include "isl_equalities.h" +#include +#include +#include +#include +#include +#include "isl_sample.h" +#include +#include "isl_tab.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned n(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + switch (type) { + case isl_dim_param: return dim->nparam; + case isl_dim_in: return dim->n_in; + case isl_dim_out: return dim->n_out; + case isl_dim_all: return dim->nparam + dim->n_in + dim->n_out; + default: return 0; + } +} + +static unsigned pos(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + switch (type) { + case isl_dim_param: return 1; + case isl_dim_in: return 1 + dim->nparam; + case isl_dim_out: return 1 + dim->nparam + dim->n_in; + default: return 0; + } +} + +unsigned isl_basic_map_dim(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type) +{ + if (!bmap) + return 0; + switch (type) { + case isl_dim_cst: return 1; + case isl_dim_param: + case isl_dim_in: + case isl_dim_out: return isl_space_dim(bmap->dim, type); + case isl_dim_div: return bmap->n_div; + case isl_dim_all: return isl_basic_map_total_dim(bmap); + default: return 0; + } +} + +unsigned isl_map_dim(__isl_keep isl_map *map, enum isl_dim_type type) +{ + return map ? n(map->dim, type) : 0; +} + +unsigned isl_set_dim(__isl_keep isl_set *set, enum isl_dim_type type) +{ + return set ? n(set->dim, type) : 0; +} + +unsigned isl_basic_map_offset(struct isl_basic_map *bmap, + enum isl_dim_type type) +{ + isl_space *dim = bmap->dim; + switch (type) { + case isl_dim_cst: return 0; + case isl_dim_param: return 1; + case isl_dim_in: return 1 + dim->nparam; + case isl_dim_out: return 1 + dim->nparam + dim->n_in; + case isl_dim_div: return 1 + dim->nparam + dim->n_in + dim->n_out; + default: return 0; + } +} + +unsigned isl_basic_set_offset(struct isl_basic_set *bset, + enum isl_dim_type type) +{ + return isl_basic_map_offset(bset, type); +} + +static unsigned map_offset(struct isl_map *map, enum isl_dim_type type) +{ + return pos(map->dim, type); +} + +unsigned isl_basic_set_dim(__isl_keep isl_basic_set *bset, + enum isl_dim_type type) +{ + return isl_basic_map_dim(bset, type); +} + +unsigned isl_basic_set_n_dim(__isl_keep isl_basic_set *bset) +{ + return isl_basic_set_dim(bset, isl_dim_set); +} + +unsigned isl_basic_set_n_param(__isl_keep isl_basic_set *bset) +{ + return isl_basic_set_dim(bset, isl_dim_param); +} + +unsigned isl_basic_set_total_dim(const struct isl_basic_set *bset) +{ + if (!bset) + return 0; + return isl_space_dim(bset->dim, isl_dim_all) + bset->n_div; +} + +unsigned isl_set_n_dim(__isl_keep isl_set *set) +{ + return isl_set_dim(set, isl_dim_set); +} + +unsigned isl_set_n_param(__isl_keep isl_set *set) +{ + return isl_set_dim(set, isl_dim_param); +} + +unsigned isl_basic_map_n_in(const struct isl_basic_map *bmap) +{ + return bmap ? bmap->dim->n_in : 0; +} + +unsigned isl_basic_map_n_out(const struct isl_basic_map *bmap) +{ + return bmap ? bmap->dim->n_out : 0; +} + +unsigned isl_basic_map_n_param(const struct isl_basic_map *bmap) +{ + return bmap ? bmap->dim->nparam : 0; +} + +unsigned isl_basic_map_n_div(const struct isl_basic_map *bmap) +{ + return bmap ? bmap->n_div : 0; +} + +unsigned isl_basic_map_total_dim(const struct isl_basic_map *bmap) +{ + return bmap ? isl_space_dim(bmap->dim, isl_dim_all) + bmap->n_div : 0; +} + +unsigned isl_map_n_in(const struct isl_map *map) +{ + return map ? map->dim->n_in : 0; +} + +unsigned isl_map_n_out(const struct isl_map *map) +{ + return map ? map->dim->n_out : 0; +} + +unsigned isl_map_n_param(const struct isl_map *map) +{ + return map ? map->dim->nparam : 0; +} + +int isl_map_compatible_domain(struct isl_map *map, struct isl_set *set) +{ + int m; + if (!map || !set) + return -1; + m = isl_space_match(map->dim, isl_dim_param, set->dim, isl_dim_param); + if (m < 0 || !m) + return m; + return isl_space_tuple_is_equal(map->dim, isl_dim_in, + set->dim, isl_dim_set); +} + +int isl_basic_map_compatible_domain(struct isl_basic_map *bmap, + struct isl_basic_set *bset) +{ + int m; + if (!bmap || !bset) + return -1; + m = isl_space_match(bmap->dim, isl_dim_param, bset->dim, isl_dim_param); + if (m < 0 || !m) + return m; + return isl_space_tuple_is_equal(bmap->dim, isl_dim_in, + bset->dim, isl_dim_set); +} + +int isl_map_compatible_range(__isl_keep isl_map *map, __isl_keep isl_set *set) +{ + int m; + if (!map || !set) + return -1; + m = isl_space_match(map->dim, isl_dim_param, set->dim, isl_dim_param); + if (m < 0 || !m) + return m; + return isl_space_tuple_is_equal(map->dim, isl_dim_out, + set->dim, isl_dim_set); +} + +int isl_basic_map_compatible_range(struct isl_basic_map *bmap, + struct isl_basic_set *bset) +{ + int m; + if (!bmap || !bset) + return -1; + m = isl_space_match(bmap->dim, isl_dim_param, bset->dim, isl_dim_param); + if (m < 0 || !m) + return m; + return isl_space_tuple_is_equal(bmap->dim, isl_dim_out, + bset->dim, isl_dim_set); +} + +isl_ctx *isl_basic_map_get_ctx(__isl_keep isl_basic_map *bmap) +{ + return bmap ? bmap->ctx : NULL; +} + +isl_ctx *isl_basic_set_get_ctx(__isl_keep isl_basic_set *bset) +{ + return bset ? bset->ctx : NULL; +} + +isl_ctx *isl_map_get_ctx(__isl_keep isl_map *map) +{ + return map ? map->ctx : NULL; +} + +isl_ctx *isl_set_get_ctx(__isl_keep isl_set *set) +{ + return set ? set->ctx : NULL; +} + +__isl_give isl_space *isl_basic_map_get_space(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + return isl_space_copy(bmap->dim); +} + +__isl_give isl_space *isl_basic_set_get_space(__isl_keep isl_basic_set *bset) +{ + if (!bset) + return NULL; + return isl_space_copy(bset->dim); +} + +/* Extract the divs in "bmap" as a matrix. + */ +__isl_give isl_mat *isl_basic_map_get_divs(__isl_keep isl_basic_map *bmap) +{ + int i; + isl_ctx *ctx; + isl_mat *div; + unsigned total; + unsigned cols; + + if (!bmap) + return NULL; + + ctx = isl_basic_map_get_ctx(bmap); + total = isl_space_dim(bmap->dim, isl_dim_all); + cols = 1 + 1 + total + bmap->n_div; + div = isl_mat_alloc(ctx, bmap->n_div, cols); + if (!div) + return NULL; + + for (i = 0; i < bmap->n_div; ++i) + isl_seq_cpy(div->row[i], bmap->div[i], cols); + + return div; +} + +/* Extract the divs in "bset" as a matrix. + */ +__isl_give isl_mat *isl_basic_set_get_divs(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_get_divs(bset); +} + +__isl_give isl_local_space *isl_basic_map_get_local_space( + __isl_keep isl_basic_map *bmap) +{ + isl_mat *div; + + if (!bmap) + return NULL; + + div = isl_basic_map_get_divs(bmap); + return isl_local_space_alloc_div(isl_space_copy(bmap->dim), div); +} + +__isl_give isl_local_space *isl_basic_set_get_local_space( + __isl_keep isl_basic_set *bset) +{ + return isl_basic_map_get_local_space(bset); +} + +/* For each known div d = floor(f/m), add the constraints + * + * f - m d >= 0 + * -(f-(m-1)) + m d >= 0 + * + * Do not finalize the result. + */ +static __isl_give isl_basic_map *add_known_div_constraints( + __isl_take isl_basic_map *bmap) +{ + int i; + unsigned n_div; + + if (!bmap) + return NULL; + n_div = isl_basic_map_dim(bmap, isl_dim_div); + if (n_div == 0) + return bmap; + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_extend_constraints(bmap, 0, 2 * n_div); + if (!bmap) + return NULL; + for (i = 0; i < n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (isl_basic_map_add_div_constraints(bmap, i) < 0) + return isl_basic_map_free(bmap); + } + + return bmap; +} + +__isl_give isl_basic_map *isl_basic_map_from_local_space( + __isl_take isl_local_space *ls) +{ + int i; + int n_div; + isl_basic_map *bmap; + + if (!ls) + return NULL; + + n_div = isl_local_space_dim(ls, isl_dim_div); + bmap = isl_basic_map_alloc_space(isl_local_space_get_space(ls), + n_div, 0, 2 * n_div); + + for (i = 0; i < n_div; ++i) + if (isl_basic_map_alloc_div(bmap) < 0) + goto error; + + for (i = 0; i < n_div; ++i) + isl_seq_cpy(bmap->div[i], ls->div->row[i], ls->div->n_col); + bmap = add_known_div_constraints(bmap); + + isl_local_space_free(ls); + return bmap; +error: + isl_local_space_free(ls); + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_from_local_space( + __isl_take isl_local_space *ls) +{ + return isl_basic_map_from_local_space(ls); +} + +__isl_give isl_space *isl_map_get_space(__isl_keep isl_map *map) +{ + if (!map) + return NULL; + return isl_space_copy(map->dim); +} + +__isl_give isl_space *isl_set_get_space(__isl_keep isl_set *set) +{ + if (!set) + return NULL; + return isl_space_copy(set->dim); +} + +__isl_give isl_basic_map *isl_basic_map_set_tuple_name( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, const char *s) +{ + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + bmap->dim = isl_space_set_tuple_name(bmap->dim, type, s); + if (!bmap->dim) + goto error; + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_set_tuple_name( + __isl_take isl_basic_set *bset, const char *s) +{ + return isl_basic_map_set_tuple_name(bset, isl_dim_set, s); +} + +const char *isl_basic_map_get_tuple_name(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type) +{ + return bmap ? isl_space_get_tuple_name(bmap->dim, type) : NULL; +} + +__isl_give isl_map *isl_map_set_tuple_name(__isl_take isl_map *map, + enum isl_dim_type type, const char *s) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_set_tuple_name(map->dim, type, s); + if (!map->dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_set_tuple_name(map->p[i], type, s); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Replace the identifier of the tuple of type "type" by "id". + */ +__isl_give isl_basic_map *isl_basic_map_set_tuple_id( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, __isl_take isl_id *id) +{ + bmap = isl_basic_map_cow(bmap); + if (!bmap) + goto error; + bmap->dim = isl_space_set_tuple_id(bmap->dim, type, id); + if (!bmap->dim) + return isl_basic_map_free(bmap); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_id_free(id); + return NULL; +} + +/* Replace the identifier of the tuple by "id". + */ +__isl_give isl_basic_set *isl_basic_set_set_tuple_id( + __isl_take isl_basic_set *bset, __isl_take isl_id *id) +{ + return isl_basic_map_set_tuple_id(bset, isl_dim_set, id); +} + +/* Does the input or output tuple have a name? + */ +isl_bool isl_map_has_tuple_name(__isl_keep isl_map *map, enum isl_dim_type type) +{ + return map ? isl_space_has_tuple_name(map->dim, type) : isl_bool_error; +} + +const char *isl_map_get_tuple_name(__isl_keep isl_map *map, + enum isl_dim_type type) +{ + return map ? isl_space_get_tuple_name(map->dim, type) : NULL; +} + +__isl_give isl_set *isl_set_set_tuple_name(__isl_take isl_set *set, + const char *s) +{ + return (isl_set *)isl_map_set_tuple_name((isl_map *)set, isl_dim_set, s); +} + +__isl_give isl_map *isl_map_set_tuple_id(__isl_take isl_map *map, + enum isl_dim_type type, __isl_take isl_id *id) +{ + map = isl_map_cow(map); + if (!map) + goto error; + + map->dim = isl_space_set_tuple_id(map->dim, type, id); + + return isl_map_reset_space(map, isl_space_copy(map->dim)); +error: + isl_id_free(id); + return NULL; +} + +__isl_give isl_set *isl_set_set_tuple_id(__isl_take isl_set *set, + __isl_take isl_id *id) +{ + return isl_map_set_tuple_id(set, isl_dim_set, id); +} + +__isl_give isl_map *isl_map_reset_tuple_id(__isl_take isl_map *map, + enum isl_dim_type type) +{ + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_reset_tuple_id(map->dim, type); + + return isl_map_reset_space(map, isl_space_copy(map->dim)); +} + +__isl_give isl_set *isl_set_reset_tuple_id(__isl_take isl_set *set) +{ + return isl_map_reset_tuple_id(set, isl_dim_set); +} + +isl_bool isl_map_has_tuple_id(__isl_keep isl_map *map, enum isl_dim_type type) +{ + return map ? isl_space_has_tuple_id(map->dim, type) : isl_bool_error; +} + +__isl_give isl_id *isl_map_get_tuple_id(__isl_keep isl_map *map, + enum isl_dim_type type) +{ + return map ? isl_space_get_tuple_id(map->dim, type) : NULL; +} + +isl_bool isl_set_has_tuple_id(__isl_keep isl_set *set) +{ + return isl_map_has_tuple_id(set, isl_dim_set); +} + +__isl_give isl_id *isl_set_get_tuple_id(__isl_keep isl_set *set) +{ + return isl_map_get_tuple_id(set, isl_dim_set); +} + +/* Does the set tuple have a name? + */ +isl_bool isl_set_has_tuple_name(__isl_keep isl_set *set) +{ + if (!set) + return isl_bool_error; + return isl_space_has_tuple_name(set->dim, isl_dim_set); +} + + +const char *isl_basic_set_get_tuple_name(__isl_keep isl_basic_set *bset) +{ + return bset ? isl_space_get_tuple_name(bset->dim, isl_dim_set) : NULL; +} + +const char *isl_set_get_tuple_name(__isl_keep isl_set *set) +{ + return set ? isl_space_get_tuple_name(set->dim, isl_dim_set) : NULL; +} + +const char *isl_basic_map_get_dim_name(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos) +{ + return bmap ? isl_space_get_dim_name(bmap->dim, type, pos) : NULL; +} + +const char *isl_basic_set_get_dim_name(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos) +{ + return bset ? isl_space_get_dim_name(bset->dim, type, pos) : NULL; +} + +/* Does the given dimension have a name? + */ +isl_bool isl_map_has_dim_name(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos) +{ + if (!map) + return isl_bool_error; + return isl_space_has_dim_name(map->dim, type, pos); +} + +const char *isl_map_get_dim_name(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos) +{ + return map ? isl_space_get_dim_name(map->dim, type, pos) : NULL; +} + +const char *isl_set_get_dim_name(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return set ? isl_space_get_dim_name(set->dim, type, pos) : NULL; +} + +/* Does the given dimension have a name? + */ +isl_bool isl_set_has_dim_name(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + if (!set) + return isl_bool_error; + return isl_space_has_dim_name(set->dim, type, pos); +} + +__isl_give isl_basic_map *isl_basic_map_set_dim_name( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, const char *s) +{ + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + bmap->dim = isl_space_set_dim_name(bmap->dim, type, pos, s); + if (!bmap->dim) + goto error; + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_map *isl_map_set_dim_name(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, const char *s) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_set_dim_name(map->dim, type, pos, s); + if (!map->dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_set_dim_name(map->p[i], type, pos, s); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_set_dim_name( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, const char *s) +{ + return (isl_basic_set *)isl_basic_map_set_dim_name( + (isl_basic_map *)bset, type, pos, s); +} + +__isl_give isl_set *isl_set_set_dim_name(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, const char *s) +{ + return (isl_set *)isl_map_set_dim_name((isl_map *)set, type, pos, s); +} + +isl_bool isl_basic_map_has_dim_id(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos) +{ + if (!bmap) + return isl_bool_error; + return isl_space_has_dim_id(bmap->dim, type, pos); +} + +__isl_give isl_id *isl_basic_set_get_dim_id(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned pos) +{ + return bset ? isl_space_get_dim_id(bset->dim, type, pos) : NULL; +} + +isl_bool isl_map_has_dim_id(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos) +{ + return map ? isl_space_has_dim_id(map->dim, type, pos) : isl_bool_error; +} + +__isl_give isl_id *isl_map_get_dim_id(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos) +{ + return map ? isl_space_get_dim_id(map->dim, type, pos) : NULL; +} + +isl_bool isl_set_has_dim_id(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return isl_map_has_dim_id(set, type, pos); +} + +__isl_give isl_id *isl_set_get_dim_id(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return isl_map_get_dim_id(set, type, pos); +} + +__isl_give isl_map *isl_map_set_dim_id(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + map = isl_map_cow(map); + if (!map) + goto error; + + map->dim = isl_space_set_dim_id(map->dim, type, pos, id); + + return isl_map_reset_space(map, isl_space_copy(map->dim)); +error: + isl_id_free(id); + return NULL; +} + +__isl_give isl_set *isl_set_set_dim_id(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + return isl_map_set_dim_id(set, type, pos, id); +} + +int isl_map_find_dim_by_id(__isl_keep isl_map *map, enum isl_dim_type type, + __isl_keep isl_id *id) +{ + if (!map) + return -1; + return isl_space_find_dim_by_id(map->dim, type, id); +} + +int isl_set_find_dim_by_id(__isl_keep isl_set *set, enum isl_dim_type type, + __isl_keep isl_id *id) +{ + return isl_map_find_dim_by_id(set, type, id); +} + +/* Return the position of the dimension of the given type and name + * in "bmap". + * Return -1 if no such dimension can be found. + */ +int isl_basic_map_find_dim_by_name(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, const char *name) +{ + if (!bmap) + return -1; + return isl_space_find_dim_by_name(bmap->dim, type, name); +} + +int isl_map_find_dim_by_name(__isl_keep isl_map *map, enum isl_dim_type type, + const char *name) +{ + if (!map) + return -1; + return isl_space_find_dim_by_name(map->dim, type, name); +} + +int isl_set_find_dim_by_name(__isl_keep isl_set *set, enum isl_dim_type type, + const char *name) +{ + return isl_map_find_dim_by_name(set, type, name); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the space of "map". + */ +__isl_give isl_map *isl_map_reset_user(__isl_take isl_map *map) +{ + isl_space *space; + + space = isl_map_get_space(map); + space = isl_space_reset_user(space); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the space of "set". + */ +__isl_give isl_set *isl_set_reset_user(__isl_take isl_set *set) +{ + return isl_map_reset_user(set); +} + +int isl_basic_map_is_rational(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return -1; + return ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); +} + +int isl_basic_set_is_rational(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_is_rational(bset); +} + +/* Does "bmap" contain any rational points? + * + * If "bmap" has an equality for each dimension, equating the dimension + * to an integer constant, then it has no rational points, even if it + * is marked as rational. + */ +int isl_basic_map_has_rational(__isl_keep isl_basic_map *bmap) +{ + int has_rational = 1; + unsigned total; + + if (!bmap) + return -1; + if (isl_basic_map_plain_is_empty(bmap)) + return 0; + if (!isl_basic_map_is_rational(bmap)) + return 0; + bmap = isl_basic_map_copy(bmap); + bmap = isl_basic_map_implicit_equalities(bmap); + if (!bmap) + return -1; + total = isl_basic_map_total_dim(bmap); + if (bmap->n_eq == total) { + int i, j; + for (i = 0; i < bmap->n_eq; ++i) { + j = isl_seq_first_non_zero(bmap->eq[i] + 1, total); + if (j < 0) + break; + if (!isl_int_is_one(bmap->eq[i][1 + j]) && + !isl_int_is_negone(bmap->eq[i][1 + j])) + break; + j = isl_seq_first_non_zero(bmap->eq[i] + 1 + j + 1, + total - j - 1); + if (j >= 0) + break; + } + if (i == bmap->n_eq) + has_rational = 0; + } + isl_basic_map_free(bmap); + + return has_rational; +} + +/* Does "map" contain any rational points? + */ +int isl_map_has_rational(__isl_keep isl_map *map) +{ + int i; + int has_rational; + + if (!map) + return -1; + for (i = 0; i < map->n; ++i) { + has_rational = isl_basic_map_has_rational(map->p[i]); + if (has_rational < 0) + return -1; + if (has_rational) + return 1; + } + return 0; +} + +/* Does "set" contain any rational points? + */ +int isl_set_has_rational(__isl_keep isl_set *set) +{ + return isl_map_has_rational(set); +} + +/* Is this basic set a parameter domain? + */ +int isl_basic_set_is_params(__isl_keep isl_basic_set *bset) +{ + if (!bset) + return -1; + return isl_space_is_params(bset->dim); +} + +/* Is this set a parameter domain? + */ +isl_bool isl_set_is_params(__isl_keep isl_set *set) +{ + if (!set) + return isl_bool_error; + return isl_space_is_params(set->dim); +} + +/* Is this map actually a parameter domain? + * Users should never call this function. Outside of isl, + * a map can never be a parameter domain. + */ +int isl_map_is_params(__isl_keep isl_map *map) +{ + if (!map) + return -1; + return isl_space_is_params(map->dim); +} + +static struct isl_basic_map *basic_map_init(struct isl_ctx *ctx, + struct isl_basic_map *bmap, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + int i; + size_t row_size = 1 + isl_space_dim(bmap->dim, isl_dim_all) + extra; + + bmap->ctx = ctx; + isl_ctx_ref(ctx); + + bmap->block = isl_blk_alloc(ctx, (n_ineq + n_eq) * row_size); + if (isl_blk_is_error(bmap->block)) + goto error; + + bmap->ineq = isl_alloc_array(ctx, isl_int *, n_ineq + n_eq); + if ((n_ineq + n_eq) && !bmap->ineq) + goto error; + + if (extra == 0) { + bmap->block2 = isl_blk_empty(); + bmap->div = NULL; + } else { + bmap->block2 = isl_blk_alloc(ctx, extra * (1 + row_size)); + if (isl_blk_is_error(bmap->block2)) + goto error; + + bmap->div = isl_alloc_array(ctx, isl_int *, extra); + if (!bmap->div) + goto error; + } + + for (i = 0; i < n_ineq + n_eq; ++i) + bmap->ineq[i] = bmap->block.data + i * row_size; + + for (i = 0; i < extra; ++i) + bmap->div[i] = bmap->block2.data + i * (1 + row_size); + + bmap->ref = 1; + bmap->flags = 0; + bmap->c_size = n_eq + n_ineq; + bmap->eq = bmap->ineq + n_ineq; + bmap->extra = extra; + bmap->n_eq = 0; + bmap->n_ineq = 0; + bmap->n_div = 0; + bmap->sample = NULL; + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_set *isl_basic_set_alloc(struct isl_ctx *ctx, + unsigned nparam, unsigned dim, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + struct isl_basic_map *bmap; + isl_space *space; + + space = isl_space_set_alloc(ctx, nparam, dim); + if (!space) + return NULL; + + bmap = isl_basic_map_alloc_space(space, extra, n_eq, n_ineq); + return (struct isl_basic_set *)bmap; +} + +struct isl_basic_set *isl_basic_set_alloc_space(__isl_take isl_space *dim, + unsigned extra, unsigned n_eq, unsigned n_ineq) +{ + struct isl_basic_map *bmap; + if (!dim) + return NULL; + isl_assert(dim->ctx, dim->n_in == 0, goto error); + bmap = isl_basic_map_alloc_space(dim, extra, n_eq, n_ineq); + return (struct isl_basic_set *)bmap; +error: + isl_space_free(dim); + return NULL; +} + +struct isl_basic_map *isl_basic_map_alloc_space(__isl_take isl_space *dim, + unsigned extra, unsigned n_eq, unsigned n_ineq) +{ + struct isl_basic_map *bmap; + + if (!dim) + return NULL; + bmap = isl_calloc_type(dim->ctx, struct isl_basic_map); + if (!bmap) + goto error; + bmap->dim = dim; + + return basic_map_init(dim->ctx, bmap, extra, n_eq, n_ineq); +error: + isl_space_free(dim); + return NULL; +} + +struct isl_basic_map *isl_basic_map_alloc(struct isl_ctx *ctx, + unsigned nparam, unsigned in, unsigned out, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + struct isl_basic_map *bmap; + isl_space *dim; + + dim = isl_space_alloc(ctx, nparam, in, out); + if (!dim) + return NULL; + + bmap = isl_basic_map_alloc_space(dim, extra, n_eq, n_ineq); + return bmap; +} + +static void dup_constraints( + struct isl_basic_map *dst, struct isl_basic_map *src) +{ + int i; + unsigned total = isl_basic_map_total_dim(src); + + for (i = 0; i < src->n_eq; ++i) { + int j = isl_basic_map_alloc_equality(dst); + isl_seq_cpy(dst->eq[j], src->eq[i], 1+total); + } + + for (i = 0; i < src->n_ineq; ++i) { + int j = isl_basic_map_alloc_inequality(dst); + isl_seq_cpy(dst->ineq[j], src->ineq[i], 1+total); + } + + for (i = 0; i < src->n_div; ++i) { + int j = isl_basic_map_alloc_div(dst); + isl_seq_cpy(dst->div[j], src->div[i], 1+1+total); + } + ISL_F_SET(dst, ISL_BASIC_SET_FINAL); +} + +struct isl_basic_map *isl_basic_map_dup(struct isl_basic_map *bmap) +{ + struct isl_basic_map *dup; + + if (!bmap) + return NULL; + dup = isl_basic_map_alloc_space(isl_space_copy(bmap->dim), + bmap->n_div, bmap->n_eq, bmap->n_ineq); + if (!dup) + return NULL; + dup_constraints(dup, bmap); + dup->flags = bmap->flags; + dup->sample = isl_vec_copy(bmap->sample); + return dup; +} + +struct isl_basic_set *isl_basic_set_dup(struct isl_basic_set *bset) +{ + struct isl_basic_map *dup; + + dup = isl_basic_map_dup((struct isl_basic_map *)bset); + return (struct isl_basic_set *)dup; +} + +struct isl_basic_set *isl_basic_set_copy(struct isl_basic_set *bset) +{ + if (!bset) + return NULL; + + if (ISL_F_ISSET(bset, ISL_BASIC_SET_FINAL)) { + bset->ref++; + return bset; + } + return isl_basic_set_dup(bset); +} + +struct isl_set *isl_set_copy(struct isl_set *set) +{ + if (!set) + return NULL; + + set->ref++; + return set; +} + +struct isl_basic_map *isl_basic_map_copy(struct isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + if (ISL_F_ISSET(bmap, ISL_BASIC_SET_FINAL)) { + bmap->ref++; + return bmap; + } + bmap = isl_basic_map_dup(bmap); + if (bmap) + ISL_F_SET(bmap, ISL_BASIC_SET_FINAL); + return bmap; +} + +struct isl_map *isl_map_copy(struct isl_map *map) +{ + if (!map) + return NULL; + + map->ref++; + return map; +} + +__isl_null isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + if (--bmap->ref > 0) + return NULL; + + isl_ctx_deref(bmap->ctx); + free(bmap->div); + isl_blk_free(bmap->ctx, bmap->block2); + free(bmap->ineq); + isl_blk_free(bmap->ctx, bmap->block); + isl_vec_free(bmap->sample); + isl_space_free(bmap->dim); + free(bmap); + + return NULL; +} + +__isl_null isl_basic_set *isl_basic_set_free(__isl_take isl_basic_set *bset) +{ + return isl_basic_map_free((struct isl_basic_map *)bset); +} + +static int room_for_con(struct isl_basic_map *bmap, unsigned n) +{ + return bmap->n_eq + bmap->n_ineq + n <= bmap->c_size; +} + +__isl_give isl_map *isl_map_align_params_map_map_and( + __isl_take isl_map *map1, __isl_take isl_map *map2, + __isl_give isl_map *(*fn)(__isl_take isl_map *map1, + __isl_take isl_map *map2)) +{ + if (!map1 || !map2) + goto error; + if (isl_space_match(map1->dim, isl_dim_param, map2->dim, isl_dim_param)) + return fn(map1, map2); + if (!isl_space_has_named_params(map1->dim) || + !isl_space_has_named_params(map2->dim)) + isl_die(map1->ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + map1 = isl_map_align_params(map1, isl_map_get_space(map2)); + map2 = isl_map_align_params(map2, isl_map_get_space(map1)); + return fn(map1, map2); +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +isl_bool isl_map_align_params_map_map_and_test(__isl_keep isl_map *map1, + __isl_keep isl_map *map2, + isl_bool (*fn)(__isl_keep isl_map *map1, __isl_keep isl_map *map2)) +{ + isl_bool r; + + if (!map1 || !map2) + return isl_bool_error; + if (isl_space_match(map1->dim, isl_dim_param, map2->dim, isl_dim_param)) + return fn(map1, map2); + if (!isl_space_has_named_params(map1->dim) || + !isl_space_has_named_params(map2->dim)) + isl_die(map1->ctx, isl_error_invalid, + "unaligned unnamed parameters", return isl_bool_error); + map1 = isl_map_copy(map1); + map2 = isl_map_copy(map2); + map1 = isl_map_align_params(map1, isl_map_get_space(map2)); + map2 = isl_map_align_params(map2, isl_map_get_space(map1)); + r = fn(map1, map2); + isl_map_free(map1); + isl_map_free(map2); + return r; +} + +int isl_basic_map_alloc_equality(struct isl_basic_map *bmap) +{ + struct isl_ctx *ctx; + if (!bmap) + return -1; + ctx = bmap->ctx; + isl_assert(ctx, room_for_con(bmap, 1), return -1); + isl_assert(ctx, (bmap->eq - bmap->ineq) + bmap->n_eq <= bmap->c_size, + return -1); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_IMPLICIT); + ISL_F_CLR(bmap, ISL_BASIC_MAP_ALL_EQUALITIES); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS); + if ((bmap->eq - bmap->ineq) + bmap->n_eq == bmap->c_size) { + isl_int *t; + int j = isl_basic_map_alloc_inequality(bmap); + if (j < 0) + return -1; + t = bmap->ineq[j]; + bmap->ineq[j] = bmap->ineq[bmap->n_ineq - 1]; + bmap->ineq[bmap->n_ineq - 1] = bmap->eq[-1]; + bmap->eq[-1] = t; + bmap->n_eq++; + bmap->n_ineq--; + bmap->eq--; + return 0; + } + isl_seq_clr(bmap->eq[bmap->n_eq] + 1 + isl_basic_map_total_dim(bmap), + bmap->extra - bmap->n_div); + return bmap->n_eq++; +} + +int isl_basic_set_alloc_equality(struct isl_basic_set *bset) +{ + return isl_basic_map_alloc_equality((struct isl_basic_map *)bset); +} + +int isl_basic_map_free_equality(struct isl_basic_map *bmap, unsigned n) +{ + if (!bmap) + return -1; + isl_assert(bmap->ctx, n <= bmap->n_eq, return -1); + bmap->n_eq -= n; + return 0; +} + +int isl_basic_set_free_equality(struct isl_basic_set *bset, unsigned n) +{ + return isl_basic_map_free_equality((struct isl_basic_map *)bset, n); +} + +int isl_basic_map_drop_equality(struct isl_basic_map *bmap, unsigned pos) +{ + isl_int *t; + if (!bmap) + return -1; + isl_assert(bmap->ctx, pos < bmap->n_eq, return -1); + + if (pos != bmap->n_eq - 1) { + t = bmap->eq[pos]; + bmap->eq[pos] = bmap->eq[bmap->n_eq - 1]; + bmap->eq[bmap->n_eq - 1] = t; + } + bmap->n_eq--; + return 0; +} + +int isl_basic_set_drop_equality(struct isl_basic_set *bset, unsigned pos) +{ + return isl_basic_map_drop_equality((struct isl_basic_map *)bset, pos); +} + +/* Turn inequality "pos" of "bmap" into an equality. + * + * In particular, we move the inequality in front of the equalities + * and move the last inequality in the position of the moved inequality. + * Note that isl_tab_make_equalities_explicit depends on this particular + * change in the ordering of the constraints. + */ +void isl_basic_map_inequality_to_equality( + struct isl_basic_map *bmap, unsigned pos) +{ + isl_int *t; + + t = bmap->ineq[pos]; + bmap->ineq[pos] = bmap->ineq[bmap->n_ineq - 1]; + bmap->ineq[bmap->n_ineq - 1] = bmap->eq[-1]; + bmap->eq[-1] = t; + bmap->n_eq++; + bmap->n_ineq--; + bmap->eq--; + ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS); + ISL_F_CLR(bmap, ISL_BASIC_MAP_ALL_EQUALITIES); +} + +static int room_for_ineq(struct isl_basic_map *bmap, unsigned n) +{ + return bmap->n_ineq + n <= bmap->eq - bmap->ineq; +} + +int isl_basic_map_alloc_inequality(struct isl_basic_map *bmap) +{ + struct isl_ctx *ctx; + if (!bmap) + return -1; + ctx = bmap->ctx; + isl_assert(ctx, room_for_ineq(bmap, 1), return -1); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_IMPLICIT); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + ISL_F_CLR(bmap, ISL_BASIC_MAP_ALL_EQUALITIES); + isl_seq_clr(bmap->ineq[bmap->n_ineq] + + 1 + isl_basic_map_total_dim(bmap), + bmap->extra - bmap->n_div); + return bmap->n_ineq++; +} + +int isl_basic_set_alloc_inequality(struct isl_basic_set *bset) +{ + return isl_basic_map_alloc_inequality((struct isl_basic_map *)bset); +} + +int isl_basic_map_free_inequality(struct isl_basic_map *bmap, unsigned n) +{ + if (!bmap) + return -1; + isl_assert(bmap->ctx, n <= bmap->n_ineq, return -1); + bmap->n_ineq -= n; + return 0; +} + +int isl_basic_set_free_inequality(struct isl_basic_set *bset, unsigned n) +{ + return isl_basic_map_free_inequality((struct isl_basic_map *)bset, n); +} + +int isl_basic_map_drop_inequality(struct isl_basic_map *bmap, unsigned pos) +{ + isl_int *t; + if (!bmap) + return -1; + isl_assert(bmap->ctx, pos < bmap->n_ineq, return -1); + + if (pos != bmap->n_ineq - 1) { + t = bmap->ineq[pos]; + bmap->ineq[pos] = bmap->ineq[bmap->n_ineq - 1]; + bmap->ineq[bmap->n_ineq - 1] = t; + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + } + bmap->n_ineq--; + return 0; +} + +int isl_basic_set_drop_inequality(struct isl_basic_set *bset, unsigned pos) +{ + return isl_basic_map_drop_inequality((struct isl_basic_map *)bset, pos); +} + +__isl_give isl_basic_map *isl_basic_map_add_eq(__isl_take isl_basic_map *bmap, + isl_int *eq) +{ + int k; + + bmap = isl_basic_map_extend_constraints(bmap, 1, 0); + if (!bmap) + return NULL; + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->eq[k], eq, 1 + isl_basic_map_total_dim(bmap)); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_add_eq(__isl_take isl_basic_set *bset, + isl_int *eq) +{ + return (isl_basic_set *) + isl_basic_map_add_eq((isl_basic_map *)bset, eq); +} + +__isl_give isl_basic_map *isl_basic_map_add_ineq(__isl_take isl_basic_map *bmap, + isl_int *ineq) +{ + int k; + + bmap = isl_basic_map_extend_constraints(bmap, 0, 1); + if (!bmap) + return NULL; + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->ineq[k], ineq, 1 + isl_basic_map_total_dim(bmap)); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_add_ineq(__isl_take isl_basic_set *bset, + isl_int *ineq) +{ + return (isl_basic_set *) + isl_basic_map_add_ineq((isl_basic_map *)bset, ineq); +} + +int isl_basic_map_alloc_div(struct isl_basic_map *bmap) +{ + if (!bmap) + return -1; + isl_assert(bmap->ctx, bmap->n_div < bmap->extra, return -1); + isl_seq_clr(bmap->div[bmap->n_div] + + 1 + 1 + isl_basic_map_total_dim(bmap), + bmap->extra - bmap->n_div); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS); + return bmap->n_div++; +} + +int isl_basic_set_alloc_div(struct isl_basic_set *bset) +{ + return isl_basic_map_alloc_div((struct isl_basic_map *)bset); +} + +int isl_basic_map_free_div(struct isl_basic_map *bmap, unsigned n) +{ + if (!bmap) + return -1; + isl_assert(bmap->ctx, n <= bmap->n_div, return -1); + bmap->n_div -= n; + return 0; +} + +int isl_basic_set_free_div(struct isl_basic_set *bset, unsigned n) +{ + return isl_basic_map_free_div((struct isl_basic_map *)bset, n); +} + +/* Copy constraint from src to dst, putting the vars of src at offset + * dim_off in dst and the divs of src at offset div_off in dst. + * If both sets are actually map, then dim_off applies to the input + * variables. + */ +static void copy_constraint(struct isl_basic_map *dst_map, isl_int *dst, + struct isl_basic_map *src_map, isl_int *src, + unsigned in_off, unsigned out_off, unsigned div_off) +{ + unsigned src_nparam = isl_basic_map_n_param(src_map); + unsigned dst_nparam = isl_basic_map_n_param(dst_map); + unsigned src_in = isl_basic_map_n_in(src_map); + unsigned dst_in = isl_basic_map_n_in(dst_map); + unsigned src_out = isl_basic_map_n_out(src_map); + unsigned dst_out = isl_basic_map_n_out(dst_map); + isl_int_set(dst[0], src[0]); + isl_seq_cpy(dst+1, src+1, isl_min(dst_nparam, src_nparam)); + if (dst_nparam > src_nparam) + isl_seq_clr(dst+1+src_nparam, + dst_nparam - src_nparam); + isl_seq_clr(dst+1+dst_nparam, in_off); + isl_seq_cpy(dst+1+dst_nparam+in_off, + src+1+src_nparam, + isl_min(dst_in-in_off, src_in)); + if (dst_in-in_off > src_in) + isl_seq_clr(dst+1+dst_nparam+in_off+src_in, + dst_in - in_off - src_in); + isl_seq_clr(dst+1+dst_nparam+dst_in, out_off); + isl_seq_cpy(dst+1+dst_nparam+dst_in+out_off, + src+1+src_nparam+src_in, + isl_min(dst_out-out_off, src_out)); + if (dst_out-out_off > src_out) + isl_seq_clr(dst+1+dst_nparam+dst_in+out_off+src_out, + dst_out - out_off - src_out); + isl_seq_clr(dst+1+dst_nparam+dst_in+dst_out, div_off); + isl_seq_cpy(dst+1+dst_nparam+dst_in+dst_out+div_off, + src+1+src_nparam+src_in+src_out, + isl_min(dst_map->extra-div_off, src_map->n_div)); + if (dst_map->n_div-div_off > src_map->n_div) + isl_seq_clr(dst+1+dst_nparam+dst_in+dst_out+ + div_off+src_map->n_div, + dst_map->n_div - div_off - src_map->n_div); +} + +static void copy_div(struct isl_basic_map *dst_map, isl_int *dst, + struct isl_basic_map *src_map, isl_int *src, + unsigned in_off, unsigned out_off, unsigned div_off) +{ + isl_int_set(dst[0], src[0]); + copy_constraint(dst_map, dst+1, src_map, src+1, in_off, out_off, div_off); +} + +static struct isl_basic_map *add_constraints(struct isl_basic_map *bmap1, + struct isl_basic_map *bmap2, unsigned i_pos, unsigned o_pos) +{ + int i; + unsigned div_off; + + if (!bmap1 || !bmap2) + goto error; + + div_off = bmap1->n_div; + + for (i = 0; i < bmap2->n_eq; ++i) { + int i1 = isl_basic_map_alloc_equality(bmap1); + if (i1 < 0) + goto error; + copy_constraint(bmap1, bmap1->eq[i1], bmap2, bmap2->eq[i], + i_pos, o_pos, div_off); + } + + for (i = 0; i < bmap2->n_ineq; ++i) { + int i1 = isl_basic_map_alloc_inequality(bmap1); + if (i1 < 0) + goto error; + copy_constraint(bmap1, bmap1->ineq[i1], bmap2, bmap2->ineq[i], + i_pos, o_pos, div_off); + } + + for (i = 0; i < bmap2->n_div; ++i) { + int i1 = isl_basic_map_alloc_div(bmap1); + if (i1 < 0) + goto error; + copy_div(bmap1, bmap1->div[i1], bmap2, bmap2->div[i], + i_pos, o_pos, div_off); + } + + isl_basic_map_free(bmap2); + + return bmap1; + +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +struct isl_basic_set *isl_basic_set_add_constraints(struct isl_basic_set *bset1, + struct isl_basic_set *bset2, unsigned pos) +{ + return (struct isl_basic_set *) + add_constraints((struct isl_basic_map *)bset1, + (struct isl_basic_map *)bset2, 0, pos); +} + +struct isl_basic_map *isl_basic_map_extend_space(struct isl_basic_map *base, + __isl_take isl_space *dim, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + struct isl_basic_map *ext; + unsigned flags; + int dims_ok; + + if (!dim) + goto error; + + if (!base) + goto error; + + dims_ok = isl_space_is_equal(base->dim, dim) && + base->extra >= base->n_div + extra; + + if (dims_ok && room_for_con(base, n_eq + n_ineq) && + room_for_ineq(base, n_ineq)) { + isl_space_free(dim); + return base; + } + + isl_assert(base->ctx, base->dim->nparam <= dim->nparam, goto error); + isl_assert(base->ctx, base->dim->n_in <= dim->n_in, goto error); + isl_assert(base->ctx, base->dim->n_out <= dim->n_out, goto error); + extra += base->extra; + n_eq += base->n_eq; + n_ineq += base->n_ineq; + + ext = isl_basic_map_alloc_space(dim, extra, n_eq, n_ineq); + dim = NULL; + if (!ext) + goto error; + + if (dims_ok) + ext->sample = isl_vec_copy(base->sample); + flags = base->flags; + ext = add_constraints(ext, base, 0, 0); + if (ext) { + ext->flags = flags; + ISL_F_CLR(ext, ISL_BASIC_SET_FINAL); + } + + return ext; + +error: + isl_space_free(dim); + isl_basic_map_free(base); + return NULL; +} + +struct isl_basic_set *isl_basic_set_extend_space(struct isl_basic_set *base, + __isl_take isl_space *dim, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + return (struct isl_basic_set *) + isl_basic_map_extend_space((struct isl_basic_map *)base, dim, + extra, n_eq, n_ineq); +} + +struct isl_basic_map *isl_basic_map_extend_constraints( + struct isl_basic_map *base, unsigned n_eq, unsigned n_ineq) +{ + if (!base) + return NULL; + return isl_basic_map_extend_space(base, isl_space_copy(base->dim), + 0, n_eq, n_ineq); +} + +struct isl_basic_map *isl_basic_map_extend(struct isl_basic_map *base, + unsigned nparam, unsigned n_in, unsigned n_out, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + struct isl_basic_map *bmap; + isl_space *dim; + + if (!base) + return NULL; + dim = isl_space_alloc(base->ctx, nparam, n_in, n_out); + if (!dim) + goto error; + + bmap = isl_basic_map_extend_space(base, dim, extra, n_eq, n_ineq); + return bmap; +error: + isl_basic_map_free(base); + return NULL; +} + +struct isl_basic_set *isl_basic_set_extend(struct isl_basic_set *base, + unsigned nparam, unsigned dim, unsigned extra, + unsigned n_eq, unsigned n_ineq) +{ + return (struct isl_basic_set *) + isl_basic_map_extend((struct isl_basic_map *)base, + nparam, 0, dim, extra, n_eq, n_ineq); +} + +struct isl_basic_set *isl_basic_set_extend_constraints( + struct isl_basic_set *base, unsigned n_eq, unsigned n_ineq) +{ + return (struct isl_basic_set *) + isl_basic_map_extend_constraints((struct isl_basic_map *)base, + n_eq, n_ineq); +} + +struct isl_basic_set *isl_basic_set_cow(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_cow((struct isl_basic_map *)bset); +} + +struct isl_basic_map *isl_basic_map_cow(struct isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + if (bmap->ref > 1) { + bmap->ref--; + bmap = isl_basic_map_dup(bmap); + } + if (bmap) { + ISL_F_CLR(bmap, ISL_BASIC_SET_FINAL); + ISL_F_CLR(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS); + } + return bmap; +} + +/* Clear all cached information in "map", either because it is about + * to be modified or because it is being freed. + * Always return the same pointer that is passed in. + * This is needed for the use in isl_map_free. + */ +static __isl_give isl_map *clear_caches(__isl_take isl_map *map) +{ + isl_basic_map_free(map->cached_simple_hull[0]); + isl_basic_map_free(map->cached_simple_hull[1]); + map->cached_simple_hull[0] = NULL; + map->cached_simple_hull[1] = NULL; + return map; +} + +struct isl_set *isl_set_cow(struct isl_set *set) +{ + return isl_map_cow(set); +} + +/* Return an isl_map that is equal to "map" and that has only + * a single reference. + * + * If the original input already has only one reference, then + * simply return it, but clear all cached information, since + * it may be rendered invalid by the operations that will be + * performed on the result. + * + * Otherwise, create a duplicate (without any cached information). + */ +struct isl_map *isl_map_cow(struct isl_map *map) +{ + if (!map) + return NULL; + + if (map->ref == 1) + return clear_caches(map); + map->ref--; + return isl_map_dup(map); +} + +static void swap_vars(struct isl_blk blk, isl_int *a, + unsigned a_len, unsigned b_len) +{ + isl_seq_cpy(blk.data, a+a_len, b_len); + isl_seq_cpy(blk.data+b_len, a, a_len); + isl_seq_cpy(a, blk.data, b_len+a_len); +} + +static __isl_give isl_basic_map *isl_basic_map_swap_vars( + __isl_take isl_basic_map *bmap, unsigned pos, unsigned n1, unsigned n2) +{ + int i; + struct isl_blk blk; + + if (!bmap) + goto error; + + isl_assert(bmap->ctx, + pos + n1 + n2 <= 1 + isl_basic_map_total_dim(bmap), goto error); + + if (n1 == 0 || n2 == 0) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + blk = isl_blk_alloc(bmap->ctx, n1 + n2); + if (isl_blk_is_error(blk)) + goto error; + + for (i = 0; i < bmap->n_eq; ++i) + swap_vars(blk, + bmap->eq[i] + pos, n1, n2); + + for (i = 0; i < bmap->n_ineq; ++i) + swap_vars(blk, + bmap->ineq[i] + pos, n1, n2); + + for (i = 0; i < bmap->n_div; ++i) + swap_vars(blk, + bmap->div[i]+1 + pos, n1, n2); + + isl_blk_free(bmap->ctx, blk); + + ISL_F_CLR(bmap, ISL_BASIC_SET_NORMALIZED); + bmap = isl_basic_map_gauss(bmap, NULL); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_map *isl_basic_map_set_to_empty(struct isl_basic_map *bmap) +{ + int i = 0; + unsigned total; + if (!bmap) + goto error; + total = isl_basic_map_total_dim(bmap); + isl_basic_map_free_div(bmap, bmap->n_div); + isl_basic_map_free_inequality(bmap, bmap->n_ineq); + if (bmap->n_eq > 0) + isl_basic_map_free_equality(bmap, bmap->n_eq-1); + else { + i = isl_basic_map_alloc_equality(bmap); + if (i < 0) + goto error; + } + isl_int_set_si(bmap->eq[i][0], 1); + isl_seq_clr(bmap->eq[i]+1, total); + ISL_F_SET(bmap, ISL_BASIC_MAP_EMPTY); + isl_vec_free(bmap->sample); + bmap->sample = NULL; + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_set *isl_basic_set_set_to_empty(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_set_to_empty((struct isl_basic_map *)bset); +} + +/* Swap divs "a" and "b" in "bmap" (without modifying any of the constraints + * of "bmap"). + */ +static void swap_div(__isl_keep isl_basic_map *bmap, int a, int b) +{ + isl_int *t = bmap->div[a]; + bmap->div[a] = bmap->div[b]; + bmap->div[b] = t; +} + +/* Swap divs "a" and "b" in "bmap" and adjust the constraints and + * div definitions accordingly. + */ +void isl_basic_map_swap_div(struct isl_basic_map *bmap, int a, int b) +{ + int i; + unsigned off = isl_space_dim(bmap->dim, isl_dim_all); + + swap_div(bmap, a, b); + + for (i = 0; i < bmap->n_eq; ++i) + isl_int_swap(bmap->eq[i][1+off+a], bmap->eq[i][1+off+b]); + + for (i = 0; i < bmap->n_ineq; ++i) + isl_int_swap(bmap->ineq[i][1+off+a], bmap->ineq[i][1+off+b]); + + for (i = 0; i < bmap->n_div; ++i) + isl_int_swap(bmap->div[i][1+1+off+a], bmap->div[i][1+1+off+b]); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); +} + +/* Eliminate the specified n dimensions starting at first from the + * constraints, without removing the dimensions from the space. + * If the set is rational, the dimensions are eliminated using Fourier-Motzkin. + */ +__isl_give isl_map *isl_map_eliminate(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!map) + return NULL; + if (n == 0) + return map; + + if (first + n > isl_map_dim(map, type) || first + n < first) + isl_die(map->ctx, isl_error_invalid, + "index out of bounds", goto error); + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_eliminate(map->p[i], type, first, n); + if (!map->p[i]) + goto error; + } + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Eliminate the specified n dimensions starting at first from the + * constraints, without removing the dimensions from the space. + * If the set is rational, the dimensions are eliminated using Fourier-Motzkin. + */ +__isl_give isl_set *isl_set_eliminate(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_set *)isl_map_eliminate((isl_map *)set, type, first, n); +} + +/* Eliminate the specified n dimensions starting at first from the + * constraints, without removing the dimensions from the space. + * If the set is rational, the dimensions are eliminated using Fourier-Motzkin. + */ +__isl_give isl_set *isl_set_eliminate_dims(__isl_take isl_set *set, + unsigned first, unsigned n) +{ + return isl_set_eliminate(set, isl_dim_set, first, n); +} + +__isl_give isl_basic_map *isl_basic_map_remove_divs( + __isl_take isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + bmap = isl_basic_map_eliminate_vars(bmap, + isl_space_dim(bmap->dim, isl_dim_all), bmap->n_div); + if (!bmap) + return NULL; + bmap->n_div = 0; + return isl_basic_map_finalize(bmap); +} + +__isl_give isl_basic_set *isl_basic_set_remove_divs( + __isl_take isl_basic_set *bset) +{ + return (struct isl_basic_set *)isl_basic_map_remove_divs( + (struct isl_basic_map *)bset); +} + +__isl_give isl_map *isl_map_remove_divs(__isl_take isl_map *map) +{ + int i; + + if (!map) + return NULL; + if (map->n == 0) + return map; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_remove_divs(map->p[i]); + if (!map->p[i]) + goto error; + } + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_remove_divs(__isl_take isl_set *set) +{ + return isl_map_remove_divs(set); +} + +struct isl_basic_map *isl_basic_map_remove_dims(struct isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + if (!bmap) + return NULL; + isl_assert(bmap->ctx, first + n <= isl_basic_map_dim(bmap, type), + goto error); + if (n == 0 && !isl_space_is_named_or_nested(bmap->dim, type)) + return bmap; + bmap = isl_basic_map_eliminate_vars(bmap, + isl_basic_map_offset(bmap, type) - 1 + first, n); + if (!bmap) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY) && type == isl_dim_div) + return bmap; + bmap = isl_basic_map_drop(bmap, type, first, n); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Return true if the definition of the given div (recursively) involves + * any of the given variables. + */ +static int div_involves_vars(__isl_keep isl_basic_map *bmap, int div, + unsigned first, unsigned n) +{ + int i; + unsigned div_offset = isl_basic_map_offset(bmap, isl_dim_div); + + if (isl_int_is_zero(bmap->div[div][0])) + return 0; + if (isl_seq_first_non_zero(bmap->div[div] + 1 + first, n) >= 0) + return 1; + + for (i = bmap->n_div - 1; i >= 0; --i) { + if (isl_int_is_zero(bmap->div[div][1 + div_offset + i])) + continue; + if (div_involves_vars(bmap, i, first, n)) + return 1; + } + + return 0; +} + +/* Try and add a lower and/or upper bound on "div" to "bmap" + * based on inequality "i". + * "total" is the total number of variables (excluding the divs). + * "v" is a temporary object that can be used during the calculations. + * If "lb" is set, then a lower bound should be constructed. + * If "ub" is set, then an upper bound should be constructed. + * + * The calling function has already checked that the inequality does not + * reference "div", but we still need to check that the inequality is + * of the right form. We'll consider the case where we want to construct + * a lower bound. The construction of upper bounds is similar. + * + * Let "div" be of the form + * + * q = floor((a + f(x))/d) + * + * We essentially check if constraint "i" is of the form + * + * b + f(x) >= 0 + * + * so that we can use it to derive a lower bound on "div". + * However, we allow a slightly more general form + * + * b + g(x) >= 0 + * + * with the condition that the coefficients of g(x) - f(x) are all + * divisible by d. + * Rewriting this constraint as + * + * 0 >= -b - g(x) + * + * adding a + f(x) to both sides and dividing by d, we obtain + * + * (a + f(x))/d >= (a-b)/d + (f(x)-g(x))/d + * + * Taking the floor on both sides, we obtain + * + * q >= floor((a-b)/d) + (f(x)-g(x))/d + * + * or + * + * (g(x)-f(x))/d + ceil((b-a)/d) + q >= 0 + * + * In the case of an upper bound, we construct the constraint + * + * (g(x)+f(x))/d + floor((b+a)/d) - q >= 0 + * + */ +static __isl_give isl_basic_map *insert_bounds_on_div_from_ineq( + __isl_take isl_basic_map *bmap, int div, int i, + unsigned total, isl_int v, int lb, int ub) +{ + int j; + + for (j = 0; (lb || ub) && j < total + bmap->n_div; ++j) { + if (lb) { + isl_int_sub(v, bmap->ineq[i][1 + j], + bmap->div[div][1 + 1 + j]); + lb = isl_int_is_divisible_by(v, bmap->div[div][0]); + } + if (ub) { + isl_int_add(v, bmap->ineq[i][1 + j], + bmap->div[div][1 + 1 + j]); + ub = isl_int_is_divisible_by(v, bmap->div[div][0]); + } + } + if (!lb && !ub) + return bmap; + + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_extend_constraints(bmap, 0, lb + ub); + if (lb) { + int k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + for (j = 0; j < 1 + total + bmap->n_div; ++j) { + isl_int_sub(bmap->ineq[k][j], bmap->ineq[i][j], + bmap->div[div][1 + j]); + isl_int_cdiv_q(bmap->ineq[k][j], + bmap->ineq[k][j], bmap->div[div][0]); + } + isl_int_set_si(bmap->ineq[k][1 + total + div], 1); + } + if (ub) { + int k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + for (j = 0; j < 1 + total + bmap->n_div; ++j) { + isl_int_add(bmap->ineq[k][j], bmap->ineq[i][j], + bmap->div[div][1 + j]); + isl_int_fdiv_q(bmap->ineq[k][j], + bmap->ineq[k][j], bmap->div[div][0]); + } + isl_int_set_si(bmap->ineq[k][1 + total + div], -1); + } + + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* This function is called right before "div" is eliminated from "bmap" + * using Fourier-Motzkin. + * Look through the constraints of "bmap" for constraints on the argument + * of the integer division and use them to construct constraints on the + * integer division itself. These constraints can then be combined + * during the Fourier-Motzkin elimination. + * Note that it is only useful to introduce lower bounds on "div" + * if "bmap" already contains upper bounds on "div" as the newly + * introduce lower bounds can then be combined with the pre-existing + * upper bounds. Similarly for upper bounds. + * We therefore first check if "bmap" contains any lower and/or upper bounds + * on "div". + * + * It is interesting to note that the introduction of these constraints + * can indeed lead to more accurate results, even when compared to + * deriving constraints on the argument of "div" from constraints on "div". + * Consider, for example, the set + * + * { [i,j,k] : 3 + i + 2j >= 0 and 2 * [(i+2j)/4] <= k } + * + * The second constraint can be rewritten as + * + * 2 * [(-i-2j+3)/4] + k >= 0 + * + * from which we can derive + * + * -i - 2j + 3 >= -2k + * + * or + * + * i + 2j <= 3 + 2k + * + * Combined with the first constraint, we obtain + * + * -3 <= 3 + 2k or k >= -3 + * + * If, on the other hand we derive a constraint on [(i+2j)/4] from + * the first constraint, we obtain + * + * [(i + 2j)/4] >= [-3/4] = -1 + * + * Combining this constraint with the second constraint, we obtain + * + * k >= -2 + */ +static __isl_give isl_basic_map *insert_bounds_on_div( + __isl_take isl_basic_map *bmap, int div) +{ + int i; + int check_lb, check_ub; + isl_int v; + unsigned total; + + if (!bmap) + return NULL; + + if (isl_int_is_zero(bmap->div[div][0])) + return bmap; + + total = isl_space_dim(bmap->dim, isl_dim_all); + + check_lb = 0; + check_ub = 0; + for (i = 0; (!check_lb || !check_ub) && i < bmap->n_ineq; ++i) { + int s = isl_int_sgn(bmap->ineq[i][1 + total + div]); + if (s > 0) + check_ub = 1; + if (s < 0) + check_lb = 1; + } + + if (!check_lb && !check_ub) + return bmap; + + isl_int_init(v); + + for (i = 0; bmap && i < bmap->n_ineq; ++i) { + if (!isl_int_is_zero(bmap->ineq[i][1 + total + div])) + continue; + + bmap = insert_bounds_on_div_from_ineq(bmap, div, i, total, v, + check_lb, check_ub); + } + + isl_int_clear(v); + + return bmap; +} + +/* Remove all divs (recursively) involving any of the given dimensions + * in their definitions. + */ +__isl_give isl_basic_map *isl_basic_map_remove_divs_involving_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!bmap) + return NULL; + isl_assert(bmap->ctx, first + n <= isl_basic_map_dim(bmap, type), + goto error); + first += isl_basic_map_offset(bmap, type); + + for (i = bmap->n_div - 1; i >= 0; --i) { + if (!div_involves_vars(bmap, i, first, n)) + continue; + bmap = insert_bounds_on_div(bmap, i); + bmap = isl_basic_map_remove_dims(bmap, isl_dim_div, i, 1); + if (!bmap) + return NULL; + i = bmap->n_div; + } + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_remove_divs_involving_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_basic_map_remove_divs_involving_dims(bset, type, first, n); +} + +__isl_give isl_map *isl_map_remove_divs_involving_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!map) + return NULL; + if (map->n == 0) + return map; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_remove_divs_involving_dims(map->p[i], + type, first, n); + if (!map->p[i]) + goto error; + } + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_remove_divs_involving_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_set *)isl_map_remove_divs_involving_dims((isl_map *)set, + type, first, n); +} + +/* Does the desciption of "bmap" depend on the specified dimensions? + * We also check whether the dimensions appear in any of the div definitions. + * In principle there is no need for this check. If the dimensions appear + * in a div definition, they also appear in the defining constraints of that + * div. + */ +isl_bool isl_basic_map_involves_dims(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!bmap) + return isl_bool_error; + + if (first + n > isl_basic_map_dim(bmap, type)) + isl_die(bmap->ctx, isl_error_invalid, + "index out of bounds", return isl_bool_error); + + first += isl_basic_map_offset(bmap, type); + for (i = 0; i < bmap->n_eq; ++i) + if (isl_seq_first_non_zero(bmap->eq[i] + first, n) >= 0) + return isl_bool_true; + for (i = 0; i < bmap->n_ineq; ++i) + if (isl_seq_first_non_zero(bmap->ineq[i] + first, n) >= 0) + return isl_bool_true; + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (isl_seq_first_non_zero(bmap->div[i] + 1 + first, n) >= 0) + return isl_bool_true; + } + + return isl_bool_false; +} + +isl_bool isl_map_involves_dims(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!map) + return isl_bool_error; + + if (first + n > isl_map_dim(map, type)) + isl_die(map->ctx, isl_error_invalid, + "index out of bounds", return isl_bool_error); + + for (i = 0; i < map->n; ++i) { + isl_bool involves = isl_basic_map_involves_dims(map->p[i], + type, first, n); + if (involves < 0 || involves) + return involves; + } + + return isl_bool_false; +} + +isl_bool isl_basic_set_involves_dims(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_basic_map_involves_dims(bset, type, first, n); +} + +isl_bool isl_set_involves_dims(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_map_involves_dims(set, type, first, n); +} + +/* Return true if the definition of the given div is unknown or depends + * on unknown divs. + */ +static int div_is_unknown(__isl_keep isl_basic_map *bmap, int div) +{ + int i; + unsigned div_offset = isl_basic_map_offset(bmap, isl_dim_div); + + if (isl_int_is_zero(bmap->div[div][0])) + return 1; + + for (i = bmap->n_div - 1; i >= 0; --i) { + if (isl_int_is_zero(bmap->div[div][1 + div_offset + i])) + continue; + if (div_is_unknown(bmap, i)) + return 1; + } + + return 0; +} + +/* Remove all divs that are unknown or defined in terms of unknown divs. + */ +__isl_give isl_basic_map *isl_basic_map_remove_unknown_divs( + __isl_take isl_basic_map *bmap) +{ + int i; + + if (!bmap) + return NULL; + + for (i = bmap->n_div - 1; i >= 0; --i) { + if (!div_is_unknown(bmap, i)) + continue; + bmap = isl_basic_map_remove_dims(bmap, isl_dim_div, i, 1); + if (!bmap) + return NULL; + i = bmap->n_div; + } + + return bmap; +} + +/* Remove all divs that are unknown or defined in terms of unknown divs. + */ +__isl_give isl_basic_set *isl_basic_set_remove_unknown_divs( + __isl_take isl_basic_set *bset) +{ + return isl_basic_map_remove_unknown_divs(bset); +} + +__isl_give isl_map *isl_map_remove_unknown_divs(__isl_take isl_map *map) +{ + int i; + + if (!map) + return NULL; + if (map->n == 0) + return map; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_remove_unknown_divs(map->p[i]); + if (!map->p[i]) + goto error; + } + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_remove_unknown_divs(__isl_take isl_set *set) +{ + return (isl_set *)isl_map_remove_unknown_divs((isl_map *)set); +} + +__isl_give isl_basic_set *isl_basic_set_remove_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_basic_set *) + isl_basic_map_remove_dims((isl_basic_map *)bset, type, first, n); +} + +struct isl_map *isl_map_remove_dims(struct isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (n == 0) + return map; + + map = isl_map_cow(map); + if (!map) + return NULL; + isl_assert(map->ctx, first + n <= isl_map_dim(map, type), goto error); + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_eliminate_vars(map->p[i], + isl_basic_map_offset(map->p[i], type) - 1 + first, n); + if (!map->p[i]) + goto error; + } + map = isl_map_drop(map, type, first, n); + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_remove_dims(__isl_take isl_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_set *)isl_map_remove_dims((isl_map *)bset, type, first, n); +} + +/* Project out n inputs starting at first using Fourier-Motzkin */ +struct isl_map *isl_map_remove_inputs(struct isl_map *map, + unsigned first, unsigned n) +{ + return isl_map_remove_dims(map, isl_dim_in, first, n); +} + +static void dump_term(struct isl_basic_map *bmap, + isl_int c, int pos, FILE *out) +{ + const char *name; + unsigned in = isl_basic_map_n_in(bmap); + unsigned dim = in + isl_basic_map_n_out(bmap); + unsigned nparam = isl_basic_map_n_param(bmap); + if (!pos) + isl_int_print(out, c, 0); + else { + if (!isl_int_is_one(c)) + isl_int_print(out, c, 0); + if (pos < 1 + nparam) { + name = isl_space_get_dim_name(bmap->dim, + isl_dim_param, pos - 1); + if (name) + fprintf(out, "%s", name); + else + fprintf(out, "p%d", pos - 1); + } else if (pos < 1 + nparam + in) + fprintf(out, "i%d", pos - 1 - nparam); + else if (pos < 1 + nparam + dim) + fprintf(out, "o%d", pos - 1 - nparam - in); + else + fprintf(out, "e%d", pos - 1 - nparam - dim); + } +} + +static void dump_constraint_sign(struct isl_basic_map *bmap, isl_int *c, + int sign, FILE *out) +{ + int i; + int first; + unsigned len = 1 + isl_basic_map_total_dim(bmap); + isl_int v; + + isl_int_init(v); + for (i = 0, first = 1; i < len; ++i) { + if (isl_int_sgn(c[i]) * sign <= 0) + continue; + if (!first) + fprintf(out, " + "); + first = 0; + isl_int_abs(v, c[i]); + dump_term(bmap, v, i, out); + } + isl_int_clear(v); + if (first) + fprintf(out, "0"); +} + +static void dump_constraint(struct isl_basic_map *bmap, isl_int *c, + const char *op, FILE *out, int indent) +{ + int i; + + fprintf(out, "%*s", indent, ""); + + dump_constraint_sign(bmap, c, 1, out); + fprintf(out, " %s ", op); + dump_constraint_sign(bmap, c, -1, out); + + fprintf(out, "\n"); + + for (i = bmap->n_div; i < bmap->extra; ++i) { + if (isl_int_is_zero(c[1+isl_space_dim(bmap->dim, isl_dim_all)+i])) + continue; + fprintf(out, "%*s", indent, ""); + fprintf(out, "ERROR: unused div coefficient not zero\n"); + abort(); + } +} + +static void dump_constraints(struct isl_basic_map *bmap, + isl_int **c, unsigned n, + const char *op, FILE *out, int indent) +{ + int i; + + for (i = 0; i < n; ++i) + dump_constraint(bmap, c[i], op, out, indent); +} + +static void dump_affine(struct isl_basic_map *bmap, isl_int *exp, FILE *out) +{ + int j; + int first = 1; + unsigned total = isl_basic_map_total_dim(bmap); + + for (j = 0; j < 1 + total; ++j) { + if (isl_int_is_zero(exp[j])) + continue; + if (!first && isl_int_is_pos(exp[j])) + fprintf(out, "+"); + dump_term(bmap, exp[j], j, out); + first = 0; + } +} + +static void dump(struct isl_basic_map *bmap, FILE *out, int indent) +{ + int i; + + dump_constraints(bmap, bmap->eq, bmap->n_eq, "=", out, indent); + dump_constraints(bmap, bmap->ineq, bmap->n_ineq, ">=", out, indent); + + for (i = 0; i < bmap->n_div; ++i) { + fprintf(out, "%*s", indent, ""); + fprintf(out, "e%d = [(", i); + dump_affine(bmap, bmap->div[i]+1, out); + fprintf(out, ")/"); + isl_int_print(out, bmap->div[i][0], 0); + fprintf(out, "]\n"); + } +} + +void isl_basic_set_print_internal(struct isl_basic_set *bset, + FILE *out, int indent) +{ + if (!bset) { + fprintf(out, "null basic set\n"); + return; + } + + fprintf(out, "%*s", indent, ""); + fprintf(out, "ref: %d, nparam: %d, dim: %d, extra: %d, flags: %x\n", + bset->ref, bset->dim->nparam, bset->dim->n_out, + bset->extra, bset->flags); + dump((struct isl_basic_map *)bset, out, indent); +} + +void isl_basic_map_print_internal(struct isl_basic_map *bmap, + FILE *out, int indent) +{ + if (!bmap) { + fprintf(out, "null basic map\n"); + return; + } + + fprintf(out, "%*s", indent, ""); + fprintf(out, "ref: %d, nparam: %d, in: %d, out: %d, extra: %d, " + "flags: %x, n_name: %d\n", + bmap->ref, + bmap->dim->nparam, bmap->dim->n_in, bmap->dim->n_out, + bmap->extra, bmap->flags, bmap->dim->n_id); + dump(bmap, out, indent); +} + +int isl_inequality_negate(struct isl_basic_map *bmap, unsigned pos) +{ + unsigned total; + if (!bmap) + return -1; + total = isl_basic_map_total_dim(bmap); + isl_assert(bmap->ctx, pos < bmap->n_ineq, return -1); + isl_seq_neg(bmap->ineq[pos], bmap->ineq[pos], 1 + total); + isl_int_sub_ui(bmap->ineq[pos][0], bmap->ineq[pos][0], 1); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + return 0; +} + +__isl_give isl_set *isl_set_alloc_space(__isl_take isl_space *space, int n, + unsigned flags) +{ + if (!space) + return NULL; + if (isl_space_dim(space, isl_dim_in) != 0) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "set cannot have input dimensions", goto error); + return isl_map_alloc_space(space, n, flags); +error: + isl_space_free(space); + return NULL; +} + +struct isl_set *isl_set_alloc(struct isl_ctx *ctx, + unsigned nparam, unsigned dim, int n, unsigned flags) +{ + struct isl_set *set; + isl_space *dims; + + dims = isl_space_alloc(ctx, nparam, 0, dim); + if (!dims) + return NULL; + + set = isl_set_alloc_space(dims, n, flags); + return set; +} + +/* Make sure "map" has room for at least "n" more basic maps. + */ +struct isl_map *isl_map_grow(struct isl_map *map, int n) +{ + int i; + struct isl_map *grown = NULL; + + if (!map) + return NULL; + isl_assert(map->ctx, n >= 0, goto error); + if (map->n + n <= map->size) + return map; + grown = isl_map_alloc_space(isl_map_get_space(map), map->n + n, map->flags); + if (!grown) + goto error; + for (i = 0; i < map->n; ++i) { + grown->p[i] = isl_basic_map_copy(map->p[i]); + if (!grown->p[i]) + goto error; + grown->n++; + } + isl_map_free(map); + return grown; +error: + isl_map_free(grown); + isl_map_free(map); + return NULL; +} + +/* Make sure "set" has room for at least "n" more basic sets. + */ +struct isl_set *isl_set_grow(struct isl_set *set, int n) +{ + return (struct isl_set *)isl_map_grow((struct isl_map *)set, n); +} + +struct isl_set *isl_set_dup(struct isl_set *set) +{ + int i; + struct isl_set *dup; + + if (!set) + return NULL; + + dup = isl_set_alloc_space(isl_space_copy(set->dim), set->n, set->flags); + if (!dup) + return NULL; + for (i = 0; i < set->n; ++i) + dup = isl_set_add_basic_set(dup, isl_basic_set_copy(set->p[i])); + return dup; +} + +struct isl_set *isl_set_from_basic_set(struct isl_basic_set *bset) +{ + return isl_map_from_basic_map(bset); +} + +struct isl_map *isl_map_from_basic_map(struct isl_basic_map *bmap) +{ + struct isl_map *map; + + if (!bmap) + return NULL; + + map = isl_map_alloc_space(isl_space_copy(bmap->dim), 1, ISL_MAP_DISJOINT); + return isl_map_add_basic_map(map, bmap); +} + +__isl_give isl_set *isl_set_add_basic_set(__isl_take isl_set *set, + __isl_take isl_basic_set *bset) +{ + return (struct isl_set *)isl_map_add_basic_map((struct isl_map *)set, + (struct isl_basic_map *)bset); +} + +__isl_null isl_set *isl_set_free(__isl_take isl_set *set) +{ + return isl_map_free(set); +} + +void isl_set_print_internal(struct isl_set *set, FILE *out, int indent) +{ + int i; + + if (!set) { + fprintf(out, "null set\n"); + return; + } + + fprintf(out, "%*s", indent, ""); + fprintf(out, "ref: %d, n: %d, nparam: %d, dim: %d, flags: %x\n", + set->ref, set->n, set->dim->nparam, set->dim->n_out, + set->flags); + for (i = 0; i < set->n; ++i) { + fprintf(out, "%*s", indent, ""); + fprintf(out, "basic set %d:\n", i); + isl_basic_set_print_internal(set->p[i], out, indent+4); + } +} + +void isl_map_print_internal(struct isl_map *map, FILE *out, int indent) +{ + int i; + + if (!map) { + fprintf(out, "null map\n"); + return; + } + + fprintf(out, "%*s", indent, ""); + fprintf(out, "ref: %d, n: %d, nparam: %d, in: %d, out: %d, " + "flags: %x, n_name: %d\n", + map->ref, map->n, map->dim->nparam, map->dim->n_in, + map->dim->n_out, map->flags, map->dim->n_id); + for (i = 0; i < map->n; ++i) { + fprintf(out, "%*s", indent, ""); + fprintf(out, "basic map %d:\n", i); + isl_basic_map_print_internal(map->p[i], out, indent+4); + } +} + +struct isl_basic_map *isl_basic_map_intersect_domain( + struct isl_basic_map *bmap, struct isl_basic_set *bset) +{ + struct isl_basic_map *bmap_domain; + + if (!bmap || !bset) + goto error; + + isl_assert(bset->ctx, isl_space_match(bmap->dim, isl_dim_param, + bset->dim, isl_dim_param), goto error); + + if (isl_space_dim(bset->dim, isl_dim_set) != 0) + isl_assert(bset->ctx, + isl_basic_map_compatible_domain(bmap, bset), goto error); + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + goto error; + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), + bset->n_div, bset->n_eq, bset->n_ineq); + bmap_domain = isl_basic_map_from_domain(bset); + bmap = add_constraints(bmap, bmap_domain, 0, 0); + + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + isl_basic_set_free(bset); + return NULL; +} + +struct isl_basic_map *isl_basic_map_intersect_range( + struct isl_basic_map *bmap, struct isl_basic_set *bset) +{ + struct isl_basic_map *bmap_range; + + if (!bmap || !bset) + goto error; + + isl_assert(bset->ctx, isl_space_match(bmap->dim, isl_dim_param, + bset->dim, isl_dim_param), goto error); + + if (isl_space_dim(bset->dim, isl_dim_set) != 0) + isl_assert(bset->ctx, + isl_basic_map_compatible_range(bmap, bset), goto error); + + if (isl_basic_set_plain_is_universe(bset)) { + isl_basic_set_free(bset); + return bmap; + } + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + goto error; + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), + bset->n_div, bset->n_eq, bset->n_ineq); + bmap_range = isl_basic_map_from_basic_set(bset, isl_space_copy(bset->dim)); + bmap = add_constraints(bmap, bmap_range, 0, 0); + + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + isl_basic_set_free(bset); + return NULL; +} + +isl_bool isl_basic_map_contains(__isl_keep isl_basic_map *bmap, + __isl_keep isl_vec *vec) +{ + int i; + unsigned total; + isl_int s; + + if (!bmap || !vec) + return isl_bool_error; + + total = 1 + isl_basic_map_total_dim(bmap); + if (total != vec->size) + return isl_bool_error; + + isl_int_init(s); + + for (i = 0; i < bmap->n_eq; ++i) { + isl_seq_inner_product(vec->el, bmap->eq[i], total, &s); + if (!isl_int_is_zero(s)) { + isl_int_clear(s); + return isl_bool_false; + } + } + + for (i = 0; i < bmap->n_ineq; ++i) { + isl_seq_inner_product(vec->el, bmap->ineq[i], total, &s); + if (isl_int_is_neg(s)) { + isl_int_clear(s); + return isl_bool_false; + } + } + + isl_int_clear(s); + + return isl_bool_true; +} + +isl_bool isl_basic_set_contains(__isl_keep isl_basic_set *bset, + __isl_keep isl_vec *vec) +{ + return isl_basic_map_contains((struct isl_basic_map *)bset, vec); +} + +struct isl_basic_map *isl_basic_map_intersect( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + struct isl_vec *sample = NULL; + + if (!bmap1 || !bmap2) + goto error; + + isl_assert(bmap1->ctx, isl_space_match(bmap1->dim, isl_dim_param, + bmap2->dim, isl_dim_param), goto error); + if (isl_space_dim(bmap1->dim, isl_dim_all) == + isl_space_dim(bmap1->dim, isl_dim_param) && + isl_space_dim(bmap2->dim, isl_dim_all) != + isl_space_dim(bmap2->dim, isl_dim_param)) + return isl_basic_map_intersect(bmap2, bmap1); + + if (isl_space_dim(bmap2->dim, isl_dim_all) != + isl_space_dim(bmap2->dim, isl_dim_param)) + isl_assert(bmap1->ctx, + isl_space_is_equal(bmap1->dim, bmap2->dim), goto error); + + if (isl_basic_map_plain_is_empty(bmap1)) { + isl_basic_map_free(bmap2); + return bmap1; + } + if (isl_basic_map_plain_is_empty(bmap2)) { + isl_basic_map_free(bmap1); + return bmap2; + } + + if (bmap1->sample && + isl_basic_map_contains(bmap1, bmap1->sample) > 0 && + isl_basic_map_contains(bmap2, bmap1->sample) > 0) + sample = isl_vec_copy(bmap1->sample); + else if (bmap2->sample && + isl_basic_map_contains(bmap1, bmap2->sample) > 0 && + isl_basic_map_contains(bmap2, bmap2->sample) > 0) + sample = isl_vec_copy(bmap2->sample); + + bmap1 = isl_basic_map_cow(bmap1); + if (!bmap1) + goto error; + bmap1 = isl_basic_map_extend_space(bmap1, isl_space_copy(bmap1->dim), + bmap2->n_div, bmap2->n_eq, bmap2->n_ineq); + bmap1 = add_constraints(bmap1, bmap2, 0, 0); + + if (!bmap1) + isl_vec_free(sample); + else if (sample) { + isl_vec_free(bmap1->sample); + bmap1->sample = sample; + } + + bmap1 = isl_basic_map_simplify(bmap1); + return isl_basic_map_finalize(bmap1); +error: + if (sample) + isl_vec_free(sample); + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +struct isl_basic_set *isl_basic_set_intersect( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + return (struct isl_basic_set *) + isl_basic_map_intersect( + (struct isl_basic_map *)bset1, + (struct isl_basic_map *)bset2); +} + +__isl_give isl_basic_set *isl_basic_set_intersect_params( + __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2) +{ + return isl_basic_set_intersect(bset1, bset2); +} + +/* Special case of isl_map_intersect, where both map1 and map2 + * are convex, without any divs and such that either map1 or map2 + * contains a single constraint. This constraint is then simply + * added to the other map. + */ +static __isl_give isl_map *map_intersect_add_constraint( + __isl_take isl_map *map1, __isl_take isl_map *map2) +{ + isl_assert(map1->ctx, map1->n == 1, goto error); + isl_assert(map2->ctx, map1->n == 1, goto error); + isl_assert(map1->ctx, map1->p[0]->n_div == 0, goto error); + isl_assert(map2->ctx, map1->p[0]->n_div == 0, goto error); + + if (map2->p[0]->n_eq + map2->p[0]->n_ineq != 1) + return isl_map_intersect(map2, map1); + + isl_assert(map2->ctx, + map2->p[0]->n_eq + map2->p[0]->n_ineq == 1, goto error); + + map1 = isl_map_cow(map1); + if (!map1) + goto error; + if (isl_map_plain_is_empty(map1)) { + isl_map_free(map2); + return map1; + } + map1->p[0] = isl_basic_map_cow(map1->p[0]); + if (map2->p[0]->n_eq == 1) + map1->p[0] = isl_basic_map_add_eq(map1->p[0], map2->p[0]->eq[0]); + else + map1->p[0] = isl_basic_map_add_ineq(map1->p[0], + map2->p[0]->ineq[0]); + + map1->p[0] = isl_basic_map_simplify(map1->p[0]); + map1->p[0] = isl_basic_map_finalize(map1->p[0]); + if (!map1->p[0]) + goto error; + + if (isl_basic_map_plain_is_empty(map1->p[0])) { + isl_basic_map_free(map1->p[0]); + map1->n = 0; + } + + isl_map_free(map2); + + return map1; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +/* map2 may be either a parameter domain or a map living in the same + * space as map1. + */ +static __isl_give isl_map *map_intersect_internal(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + unsigned flags = 0; + isl_map *result; + int i, j; + + if (!map1 || !map2) + goto error; + + if ((isl_map_plain_is_empty(map1) || + isl_map_plain_is_universe(map2)) && + isl_space_is_equal(map1->dim, map2->dim)) { + isl_map_free(map2); + return map1; + } + if ((isl_map_plain_is_empty(map2) || + isl_map_plain_is_universe(map1)) && + isl_space_is_equal(map1->dim, map2->dim)) { + isl_map_free(map1); + return map2; + } + + if (map1->n == 1 && map2->n == 1 && + map1->p[0]->n_div == 0 && map2->p[0]->n_div == 0 && + isl_space_is_equal(map1->dim, map2->dim) && + (map1->p[0]->n_eq + map1->p[0]->n_ineq == 1 || + map2->p[0]->n_eq + map2->p[0]->n_ineq == 1)) + return map_intersect_add_constraint(map1, map2); + + if (isl_space_dim(map2->dim, isl_dim_all) != + isl_space_dim(map2->dim, isl_dim_param)) + isl_assert(map1->ctx, + isl_space_is_equal(map1->dim, map2->dim), goto error); + + if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT) && + ISL_F_ISSET(map2, ISL_MAP_DISJOINT)) + ISL_FL_SET(flags, ISL_MAP_DISJOINT); + + result = isl_map_alloc_space(isl_space_copy(map1->dim), + map1->n * map2->n, flags); + if (!result) + goto error; + for (i = 0; i < map1->n; ++i) + for (j = 0; j < map2->n; ++j) { + struct isl_basic_map *part; + part = isl_basic_map_intersect( + isl_basic_map_copy(map1->p[i]), + isl_basic_map_copy(map2->p[j])); + if (isl_basic_map_is_empty(part) < 0) + part = isl_basic_map_free(part); + result = isl_map_add_basic_map(result, part); + if (!result) + goto error; + } + isl_map_free(map1); + isl_map_free(map2); + return result; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +static __isl_give isl_map *map_intersect(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + if (!map1 || !map2) + goto error; + if (!isl_space_is_equal(map1->dim, map2->dim)) + isl_die(isl_map_get_ctx(map1), isl_error_invalid, + "spaces don't match", goto error); + return map_intersect_internal(map1, map2); +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +__isl_give isl_map *isl_map_intersect(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_intersect); +} + +struct isl_set *isl_set_intersect(struct isl_set *set1, struct isl_set *set2) +{ + return (struct isl_set *) + isl_map_intersect((struct isl_map *)set1, + (struct isl_map *)set2); +} + +/* map_intersect_internal accepts intersections + * with parameter domains, so we can just call that function. + */ +static __isl_give isl_map *map_intersect_params(__isl_take isl_map *map, + __isl_take isl_set *params) +{ + return map_intersect_internal(map, params); +} + +__isl_give isl_map *isl_map_intersect_params(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_intersect_params); +} + +__isl_give isl_set *isl_set_intersect_params(__isl_take isl_set *set, + __isl_take isl_set *params) +{ + return isl_map_intersect_params(set, params); +} + +struct isl_basic_map *isl_basic_map_reverse(struct isl_basic_map *bmap) +{ + isl_space *space; + unsigned pos, n1, n2; + + if (!bmap) + return NULL; + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + space = isl_space_reverse(isl_space_copy(bmap->dim)); + pos = isl_basic_map_offset(bmap, isl_dim_in); + n1 = isl_basic_map_dim(bmap, isl_dim_in); + n2 = isl_basic_map_dim(bmap, isl_dim_out); + bmap = isl_basic_map_swap_vars(bmap, pos, n1, n2); + return isl_basic_map_reset_space(bmap, space); +} + +static __isl_give isl_basic_map *basic_map_space_reset( + __isl_take isl_basic_map *bmap, enum isl_dim_type type) +{ + isl_space *space; + + if (!bmap) + return NULL; + if (!isl_space_is_named_or_nested(bmap->dim, type)) + return bmap; + + space = isl_basic_map_get_space(bmap); + space = isl_space_reset(space, type); + bmap = isl_basic_map_reset_space(bmap, space); + return bmap; +} + +__isl_give isl_basic_map *isl_basic_map_insert_dims( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, + unsigned pos, unsigned n) +{ + isl_space *res_dim; + struct isl_basic_map *res; + struct isl_dim_map *dim_map; + unsigned total, off; + enum isl_dim_type t; + + if (n == 0) + return basic_map_space_reset(bmap, type); + + if (!bmap) + return NULL; + + res_dim = isl_space_insert_dims(isl_basic_map_get_space(bmap), type, pos, n); + + total = isl_basic_map_total_dim(bmap) + n; + dim_map = isl_dim_map_alloc(bmap->ctx, total); + off = 0; + for (t = isl_dim_param; t <= isl_dim_out; ++t) { + if (t != type) { + isl_dim_map_dim(dim_map, bmap->dim, t, off); + } else { + unsigned size = isl_basic_map_dim(bmap, t); + isl_dim_map_dim_range(dim_map, bmap->dim, t, + 0, pos, off); + isl_dim_map_dim_range(dim_map, bmap->dim, t, + pos, size - pos, off + pos + n); + } + off += isl_space_dim(res_dim, t); + } + isl_dim_map_div(dim_map, bmap, off); + + res = isl_basic_map_alloc_space(res_dim, + bmap->n_div, bmap->n_eq, bmap->n_ineq); + if (isl_basic_map_is_rational(bmap)) + res = isl_basic_map_set_rational(res); + if (isl_basic_map_plain_is_empty(bmap)) { + isl_basic_map_free(bmap); + free(dim_map); + return isl_basic_map_set_to_empty(res); + } + res = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map); + return isl_basic_map_finalize(res); +} + +__isl_give isl_basic_set *isl_basic_set_insert_dims( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, unsigned n) +{ + return isl_basic_map_insert_dims(bset, type, pos, n); +} + +__isl_give isl_basic_map *isl_basic_map_add_dims(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned n) +{ + if (!bmap) + return NULL; + return isl_basic_map_insert_dims(bmap, type, + isl_basic_map_dim(bmap, type), n); +} + +__isl_give isl_basic_set *isl_basic_set_add_dims(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned n) +{ + if (!bset) + return NULL; + isl_assert(bset->ctx, type != isl_dim_in, goto error); + return isl_basic_map_add_dims(bset, type, n); +error: + isl_basic_set_free(bset); + return NULL; +} + +static __isl_give isl_map *map_space_reset(__isl_take isl_map *map, + enum isl_dim_type type) +{ + isl_space *space; + + if (!map || !isl_space_is_named_or_nested(map->dim, type)) + return map; + + space = isl_map_get_space(map); + space = isl_space_reset(space, type); + map = isl_map_reset_space(map, space); + return map; +} + +__isl_give isl_map *isl_map_insert_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, unsigned n) +{ + int i; + + if (n == 0) + return map_space_reset(map, type); + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_insert_dims(map->dim, type, pos, n); + if (!map->dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_insert_dims(map->p[i], type, pos, n); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_insert_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, unsigned n) +{ + return isl_map_insert_dims(set, type, pos, n); +} + +__isl_give isl_map *isl_map_add_dims(__isl_take isl_map *map, + enum isl_dim_type type, unsigned n) +{ + if (!map) + return NULL; + return isl_map_insert_dims(map, type, isl_map_dim(map, type), n); +} + +__isl_give isl_set *isl_set_add_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned n) +{ + if (!set) + return NULL; + isl_assert(set->ctx, type != isl_dim_in, goto error); + return (isl_set *)isl_map_add_dims((isl_map *)set, type, n); +error: + isl_set_free(set); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_move_dims( + __isl_take isl_basic_map *bmap, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + struct isl_dim_map *dim_map; + struct isl_basic_map *res; + enum isl_dim_type t; + unsigned total, off; + + if (!bmap) + return NULL; + if (n == 0) + return bmap; + + isl_assert(bmap->ctx, src_pos + n <= isl_basic_map_dim(bmap, src_type), + goto error); + + if (dst_type == src_type && dst_pos == src_pos) + return bmap; + + isl_assert(bmap->ctx, dst_type != src_type, goto error); + + if (pos(bmap->dim, dst_type) + dst_pos == + pos(bmap->dim, src_type) + src_pos + + ((src_type < dst_type) ? n : 0)) { + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + bmap->dim = isl_space_move_dims(bmap->dim, dst_type, dst_pos, + src_type, src_pos, n); + if (!bmap->dim) + goto error; + + bmap = isl_basic_map_finalize(bmap); + + return bmap; + } + + total = isl_basic_map_total_dim(bmap); + dim_map = isl_dim_map_alloc(bmap->ctx, total); + + off = 0; + for (t = isl_dim_param; t <= isl_dim_out; ++t) { + unsigned size = isl_space_dim(bmap->dim, t); + if (t == dst_type) { + isl_dim_map_dim_range(dim_map, bmap->dim, t, + 0, dst_pos, off); + off += dst_pos; + isl_dim_map_dim_range(dim_map, bmap->dim, src_type, + src_pos, n, off); + off += n; + isl_dim_map_dim_range(dim_map, bmap->dim, t, + dst_pos, size - dst_pos, off); + off += size - dst_pos; + } else if (t == src_type) { + isl_dim_map_dim_range(dim_map, bmap->dim, t, + 0, src_pos, off); + off += src_pos; + isl_dim_map_dim_range(dim_map, bmap->dim, t, + src_pos + n, size - src_pos - n, off); + off += size - src_pos - n; + } else { + isl_dim_map_dim(dim_map, bmap->dim, t, off); + off += size; + } + } + isl_dim_map_div(dim_map, bmap, off); + + res = isl_basic_map_alloc_space(isl_basic_map_get_space(bmap), + bmap->n_div, bmap->n_eq, bmap->n_ineq); + bmap = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map); + if (!bmap) + goto error; + + bmap->dim = isl_space_move_dims(bmap->dim, dst_type, dst_pos, + src_type, src_pos, n); + if (!bmap->dim) + goto error; + + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + bmap = isl_basic_map_gauss(bmap, NULL); + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_move_dims(__isl_take isl_basic_set *bset, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + return (isl_basic_set *)isl_basic_map_move_dims( + (isl_basic_map *)bset, dst_type, dst_pos, src_type, src_pos, n); +} + +__isl_give isl_set *isl_set_move_dims(__isl_take isl_set *set, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + if (!set) + return NULL; + isl_assert(set->ctx, dst_type != isl_dim_in, goto error); + return (isl_set *)isl_map_move_dims((isl_map *)set, dst_type, dst_pos, + src_type, src_pos, n); +error: + isl_set_free(set); + return NULL; +} + +__isl_give isl_map *isl_map_move_dims(__isl_take isl_map *map, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + int i; + + if (!map) + return NULL; + if (n == 0) + return map; + + isl_assert(map->ctx, src_pos + n <= isl_map_dim(map, src_type), + goto error); + + if (dst_type == src_type && dst_pos == src_pos) + return map; + + isl_assert(map->ctx, dst_type != src_type, goto error); + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_move_dims(map->dim, dst_type, dst_pos, src_type, src_pos, n); + if (!map->dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_move_dims(map->p[i], + dst_type, dst_pos, + src_type, src_pos, n); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Move the specified dimensions to the last columns right before + * the divs. Don't change the dimension specification of bmap. + * That's the responsibility of the caller. + */ +static __isl_give isl_basic_map *move_last(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + struct isl_dim_map *dim_map; + struct isl_basic_map *res; + enum isl_dim_type t; + unsigned total, off; + + if (!bmap) + return NULL; + if (pos(bmap->dim, type) + first + n == + 1 + isl_space_dim(bmap->dim, isl_dim_all)) + return bmap; + + total = isl_basic_map_total_dim(bmap); + dim_map = isl_dim_map_alloc(bmap->ctx, total); + + off = 0; + for (t = isl_dim_param; t <= isl_dim_out; ++t) { + unsigned size = isl_space_dim(bmap->dim, t); + if (t == type) { + isl_dim_map_dim_range(dim_map, bmap->dim, t, + 0, first, off); + off += first; + isl_dim_map_dim_range(dim_map, bmap->dim, t, + first, n, total - bmap->n_div - n); + isl_dim_map_dim_range(dim_map, bmap->dim, t, + first + n, size - (first + n), off); + off += size - (first + n); + } else { + isl_dim_map_dim(dim_map, bmap->dim, t, off); + off += size; + } + } + isl_dim_map_div(dim_map, bmap, off + n); + + res = isl_basic_map_alloc_space(isl_basic_map_get_space(bmap), + bmap->n_div, bmap->n_eq, bmap->n_ineq); + res = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map); + return res; +} + +/* Insert "n" rows in the divs of "bmap". + * + * The number of columns is not changed, which means that the last + * dimensions of "bmap" are being reintepreted as the new divs. + * The space of "bmap" is not adjusted, however, which means + * that "bmap" is left in an inconsistent state. Removing "n" dimensions + * from the space of "bmap" is the responsibility of the caller. + */ +static __isl_give isl_basic_map *insert_div_rows(__isl_take isl_basic_map *bmap, + int n) +{ + int i; + size_t row_size; + isl_int **new_div; + isl_int *old; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + row_size = 1 + isl_space_dim(bmap->dim, isl_dim_all) + bmap->extra; + old = bmap->block2.data; + bmap->block2 = isl_blk_extend(bmap->ctx, bmap->block2, + (bmap->extra + n) * (1 + row_size)); + if (!bmap->block2.data) + return isl_basic_map_free(bmap); + new_div = isl_alloc_array(bmap->ctx, isl_int *, bmap->extra + n); + if (!new_div) + return isl_basic_map_free(bmap); + for (i = 0; i < n; ++i) { + new_div[i] = bmap->block2.data + + (bmap->extra + i) * (1 + row_size); + isl_seq_clr(new_div[i], 1 + row_size); + } + for (i = 0; i < bmap->extra; ++i) + new_div[n + i] = bmap->block2.data + (bmap->div[i] - old); + free(bmap->div); + bmap->div = new_div; + bmap->n_div += n; + bmap->extra += n; + + return bmap; +} + +/* Drop constraints from "bmap" that only involve the variables + * of "type" in the range [first, first + n] that are not related + * to any of the variables outside that interval. + * These constraints cannot influence the values for the variables + * outside the interval, except in case they cause "bmap" to be empty. + * Only drop the constraints if "bmap" is known to be non-empty. + */ +static __isl_give isl_basic_map *drop_irrelevant_constraints( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, + unsigned first, unsigned n) +{ + int i; + int *groups; + unsigned dim, n_div; + isl_bool non_empty; + + non_empty = isl_basic_map_plain_is_non_empty(bmap); + if (non_empty < 0) + return isl_basic_map_free(bmap); + if (!non_empty) + return bmap; + + dim = isl_basic_map_dim(bmap, isl_dim_all); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + groups = isl_calloc_array(isl_basic_map_get_ctx(bmap), int, dim); + if (!groups) + return isl_basic_map_free(bmap); + first += isl_basic_map_offset(bmap, type) - 1; + for (i = 0; i < first; ++i) + groups[i] = -1; + for (i = first + n; i < dim - n_div; ++i) + groups[i] = -1; + + bmap = isl_basic_map_drop_unrelated_constraints(bmap, groups); + + return bmap; +} + +/* Turn the n dimensions of type type, starting at first + * into existentially quantified variables. + * + * If a subset of the projected out variables are unrelated + * to any of the variables that remain, then the constraints + * involving this subset are simply dropped first. + */ +__isl_give isl_basic_map *isl_basic_map_project_out( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + if (n == 0) + return basic_map_space_reset(bmap, type); + if (type == isl_dim_div) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "cannot project out existentially quantified variables", + return isl_basic_map_free(bmap)); + + bmap = drop_irrelevant_constraints(bmap, type, first, n); + if (!bmap) + return NULL; + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) + return isl_basic_map_remove_dims(bmap, type, first, n); + + isl_assert(bmap->ctx, first + n <= isl_basic_map_dim(bmap, type), + goto error); + + bmap = move_last(bmap, type, first, n); + bmap = isl_basic_map_cow(bmap); + bmap = insert_div_rows(bmap, n); + if (!bmap) + return NULL; + + bmap->dim = isl_space_drop_dims(bmap->dim, type, first, n); + if (!bmap->dim) + goto error; + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_drop_redundant_divs(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Turn the n dimensions of type type, starting at first + * into existentially quantified variables. + */ +struct isl_basic_set *isl_basic_set_project_out(struct isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_basic_set *)isl_basic_map_project_out( + (isl_basic_map *)bset, type, first, n); +} + +/* Turn the n dimensions of type type, starting at first + * into existentially quantified variables. + */ +__isl_give isl_map *isl_map_project_out(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!map) + return NULL; + + if (n == 0) + return map_space_reset(map, type); + + isl_assert(map->ctx, first + n <= isl_map_dim(map, type), goto error); + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_drop_dims(map->dim, type, first, n); + if (!map->dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_project_out(map->p[i], type, first, n); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Turn the n dimensions of type type, starting at first + * into existentially quantified variables. + */ +__isl_give isl_set *isl_set_project_out(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_set *)isl_map_project_out((isl_map *)set, type, first, n); +} + +/* Return a map that projects the elements in "set" onto their + * "n" set dimensions starting at "first". + * "type" should be equal to isl_dim_set. + */ +__isl_give isl_map *isl_set_project_onto_map(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + int dim; + isl_map *map; + + if (!set) + return NULL; + if (type != isl_dim_set) + isl_die(isl_set_get_ctx(set), isl_error_invalid, + "only set dimensions can be projected out", goto error); + dim = isl_set_dim(set, isl_dim_set); + if (first + n > dim || first + n < first) + isl_die(isl_set_get_ctx(set), isl_error_invalid, + "index out of bounds", goto error); + + map = isl_map_from_domain(set); + map = isl_map_add_dims(map, isl_dim_out, n); + for (i = 0; i < n; ++i) + map = isl_map_equate(map, isl_dim_in, first + i, + isl_dim_out, i); + return map; +error: + isl_set_free(set); + return NULL; +} + +static struct isl_basic_map *add_divs(struct isl_basic_map *bmap, unsigned n) +{ + int i, j; + + for (i = 0; i < n; ++i) { + j = isl_basic_map_alloc_div(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->div[j], 1+1+isl_basic_map_total_dim(bmap)); + } + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_map *isl_basic_map_apply_range( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + isl_space *dim_result = NULL; + struct isl_basic_map *bmap; + unsigned n_in, n_out, n, nparam, total, pos; + struct isl_dim_map *dim_map1, *dim_map2; + + if (!bmap1 || !bmap2) + goto error; + if (!isl_space_match(bmap1->dim, isl_dim_param, + bmap2->dim, isl_dim_param)) + isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid, + "parameters don't match", goto error); + if (!isl_space_tuple_is_equal(bmap1->dim, isl_dim_out, + bmap2->dim, isl_dim_in)) + isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid, + "spaces don't match", goto error); + + dim_result = isl_space_join(isl_space_copy(bmap1->dim), + isl_space_copy(bmap2->dim)); + + n_in = isl_basic_map_n_in(bmap1); + n_out = isl_basic_map_n_out(bmap2); + n = isl_basic_map_n_out(bmap1); + nparam = isl_basic_map_n_param(bmap1); + + total = nparam + n_in + n_out + bmap1->n_div + bmap2->n_div + n; + dim_map1 = isl_dim_map_alloc(bmap1->ctx, total); + dim_map2 = isl_dim_map_alloc(bmap1->ctx, total); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += n_in); + isl_dim_map_div(dim_map1, bmap1, pos += n_out); + isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += bmap2->n_div); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos); + + bmap = isl_basic_map_alloc_space(dim_result, + bmap1->n_div + bmap2->n_div + n, + bmap1->n_eq + bmap2->n_eq, + bmap1->n_ineq + bmap2->n_ineq); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2); + bmap = add_divs(bmap, n); + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_drop_redundant_divs(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +struct isl_basic_set *isl_basic_set_apply( + struct isl_basic_set *bset, struct isl_basic_map *bmap) +{ + if (!bset || !bmap) + goto error; + + isl_assert(bset->ctx, isl_basic_map_compatible_domain(bmap, bset), + goto error); + + return (struct isl_basic_set *) + isl_basic_map_apply_range((struct isl_basic_map *)bset, bmap); +error: + isl_basic_set_free(bset); + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_map *isl_basic_map_apply_domain( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + if (!bmap1 || !bmap2) + goto error; + + isl_assert(bmap1->ctx, + isl_basic_map_n_in(bmap1) == isl_basic_map_n_in(bmap2), goto error); + isl_assert(bmap1->ctx, + isl_basic_map_n_param(bmap1) == isl_basic_map_n_param(bmap2), + goto error); + + bmap1 = isl_basic_map_reverse(bmap1); + bmap1 = isl_basic_map_apply_range(bmap1, bmap2); + return isl_basic_map_reverse(bmap1); +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +/* Given two basic maps A -> f(A) and B -> g(B), construct a basic map + * A \cap B -> f(A) + f(B) + */ +struct isl_basic_map *isl_basic_map_sum( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + unsigned n_in, n_out, nparam, total, pos; + struct isl_basic_map *bmap = NULL; + struct isl_dim_map *dim_map1, *dim_map2; + int i; + + if (!bmap1 || !bmap2) + goto error; + + isl_assert(bmap1->ctx, isl_space_is_equal(bmap1->dim, bmap2->dim), + goto error); + + nparam = isl_basic_map_n_param(bmap1); + n_in = isl_basic_map_n_in(bmap1); + n_out = isl_basic_map_n_out(bmap1); + + total = nparam + n_in + n_out + bmap1->n_div + bmap2->n_div + 2 * n_out; + dim_map1 = isl_dim_map_alloc(bmap1->ctx, total); + dim_map2 = isl_dim_map_alloc(bmap2->ctx, total); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos); + isl_dim_map_div(dim_map1, bmap1, pos += n_in + n_out); + isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += bmap2->n_div); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += n_out); + + bmap = isl_basic_map_alloc_space(isl_space_copy(bmap1->dim), + bmap1->n_div + bmap2->n_div + 2 * n_out, + bmap1->n_eq + bmap2->n_eq + n_out, + bmap1->n_ineq + bmap2->n_ineq); + for (i = 0; i < n_out; ++i) { + int j = isl_basic_map_alloc_equality(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->eq[j], 1+total); + isl_int_set_si(bmap->eq[j][1+nparam+n_in+i], -1); + isl_int_set_si(bmap->eq[j][1+pos+i], 1); + isl_int_set_si(bmap->eq[j][1+pos-n_out+i], 1); + } + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2); + bmap = add_divs(bmap, 2 * n_out); + + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +/* Given two maps A -> f(A) and B -> g(B), construct a map + * A \cap B -> f(A) + f(B) + */ +struct isl_map *isl_map_sum(struct isl_map *map1, struct isl_map *map2) +{ + struct isl_map *result; + int i, j; + + if (!map1 || !map2) + goto error; + + isl_assert(map1->ctx, isl_space_is_equal(map1->dim, map2->dim), goto error); + + result = isl_map_alloc_space(isl_space_copy(map1->dim), + map1->n * map2->n, 0); + if (!result) + goto error; + for (i = 0; i < map1->n; ++i) + for (j = 0; j < map2->n; ++j) { + struct isl_basic_map *part; + part = isl_basic_map_sum( + isl_basic_map_copy(map1->p[i]), + isl_basic_map_copy(map2->p[j])); + if (isl_basic_map_is_empty(part)) + isl_basic_map_free(part); + else + result = isl_map_add_basic_map(result, part); + if (!result) + goto error; + } + isl_map_free(map1); + isl_map_free(map2); + return result; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +__isl_give isl_set *isl_set_sum(__isl_take isl_set *set1, + __isl_take isl_set *set2) +{ + return (isl_set *)isl_map_sum((isl_map *)set1, (isl_map *)set2); +} + +/* Given a basic map A -> f(A), construct A -> -f(A). + */ +struct isl_basic_map *isl_basic_map_neg(struct isl_basic_map *bmap) +{ + int i, j; + unsigned off, n; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + n = isl_basic_map_dim(bmap, isl_dim_out); + off = isl_basic_map_offset(bmap, isl_dim_out); + for (i = 0; i < bmap->n_eq; ++i) + for (j = 0; j < n; ++j) + isl_int_neg(bmap->eq[i][off+j], bmap->eq[i][off+j]); + for (i = 0; i < bmap->n_ineq; ++i) + for (j = 0; j < n; ++j) + isl_int_neg(bmap->ineq[i][off+j], bmap->ineq[i][off+j]); + for (i = 0; i < bmap->n_div; ++i) + for (j = 0; j < n; ++j) + isl_int_neg(bmap->div[i][1+off+j], bmap->div[i][1+off+j]); + bmap = isl_basic_map_gauss(bmap, NULL); + return isl_basic_map_finalize(bmap); +} + +__isl_give isl_basic_set *isl_basic_set_neg(__isl_take isl_basic_set *bset) +{ + return isl_basic_map_neg(bset); +} + +/* Given a map A -> f(A), construct A -> -f(A). + */ +struct isl_map *isl_map_neg(struct isl_map *map) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_neg(map->p[i]); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_neg(__isl_take isl_set *set) +{ + return (isl_set *)isl_map_neg((isl_map *)set); +} + +/* Given a basic map A -> f(A) and an integer d, construct a basic map + * A -> floor(f(A)/d). + */ +struct isl_basic_map *isl_basic_map_floordiv(struct isl_basic_map *bmap, + isl_int d) +{ + unsigned n_in, n_out, nparam, total, pos; + struct isl_basic_map *result = NULL; + struct isl_dim_map *dim_map; + int i; + + if (!bmap) + return NULL; + + nparam = isl_basic_map_n_param(bmap); + n_in = isl_basic_map_n_in(bmap); + n_out = isl_basic_map_n_out(bmap); + + total = nparam + n_in + n_out + bmap->n_div + n_out; + dim_map = isl_dim_map_alloc(bmap->ctx, total); + isl_dim_map_dim(dim_map, bmap->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map, bmap->dim, isl_dim_in, pos += nparam); + isl_dim_map_div(dim_map, bmap, pos += n_in + n_out); + isl_dim_map_dim(dim_map, bmap->dim, isl_dim_out, pos += bmap->n_div); + + result = isl_basic_map_alloc_space(isl_space_copy(bmap->dim), + bmap->n_div + n_out, + bmap->n_eq, bmap->n_ineq + 2 * n_out); + result = isl_basic_map_add_constraints_dim_map(result, bmap, dim_map); + result = add_divs(result, n_out); + for (i = 0; i < n_out; ++i) { + int j; + j = isl_basic_map_alloc_inequality(result); + if (j < 0) + goto error; + isl_seq_clr(result->ineq[j], 1+total); + isl_int_neg(result->ineq[j][1+nparam+n_in+i], d); + isl_int_set_si(result->ineq[j][1+pos+i], 1); + j = isl_basic_map_alloc_inequality(result); + if (j < 0) + goto error; + isl_seq_clr(result->ineq[j], 1+total); + isl_int_set(result->ineq[j][1+nparam+n_in+i], d); + isl_int_set_si(result->ineq[j][1+pos+i], -1); + isl_int_sub_ui(result->ineq[j][0], d, 1); + } + + result = isl_basic_map_simplify(result); + return isl_basic_map_finalize(result); +error: + isl_basic_map_free(result); + return NULL; +} + +/* Given a map A -> f(A) and an integer d, construct a map + * A -> floor(f(A)/d). + */ +struct isl_map *isl_map_floordiv(struct isl_map *map, isl_int d) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + ISL_F_CLR(map, ISL_MAP_DISJOINT); + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_floordiv(map->p[i], d); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Given a map A -> f(A) and an integer d, construct a map + * A -> floor(f(A)/d). + */ +__isl_give isl_map *isl_map_floordiv_val(__isl_take isl_map *map, + __isl_take isl_val *d) +{ + if (!map || !d) + goto error; + if (!isl_val_is_int(d)) + isl_die(isl_val_get_ctx(d), isl_error_invalid, + "expecting integer denominator", goto error); + map = isl_map_floordiv(map, d->n); + isl_val_free(d); + return map; +error: + isl_map_free(map); + isl_val_free(d); + return NULL; +} + +static struct isl_basic_map *var_equal(struct isl_basic_map *bmap, unsigned pos) +{ + int i; + unsigned nparam; + unsigned n_in; + + i = isl_basic_map_alloc_equality(bmap); + if (i < 0) + goto error; + nparam = isl_basic_map_n_param(bmap); + n_in = isl_basic_map_n_in(bmap); + isl_seq_clr(bmap->eq[i], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[i][1+nparam+pos], -1); + isl_int_set_si(bmap->eq[i][1+nparam+n_in+pos], 1); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Add a constraints to "bmap" expressing i_pos < o_pos + */ +static struct isl_basic_map *var_less(struct isl_basic_map *bmap, unsigned pos) +{ + int i; + unsigned nparam; + unsigned n_in; + + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + goto error; + nparam = isl_basic_map_n_param(bmap); + n_in = isl_basic_map_n_in(bmap); + isl_seq_clr(bmap->ineq[i], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->ineq[i][0], -1); + isl_int_set_si(bmap->ineq[i][1+nparam+pos], -1); + isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], 1); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Add a constraint to "bmap" expressing i_pos <= o_pos + */ +static __isl_give isl_basic_map *var_less_or_equal( + __isl_take isl_basic_map *bmap, unsigned pos) +{ + int i; + unsigned nparam; + unsigned n_in; + + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + goto error; + nparam = isl_basic_map_n_param(bmap); + n_in = isl_basic_map_n_in(bmap); + isl_seq_clr(bmap->ineq[i], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->ineq[i][1+nparam+pos], -1); + isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], 1); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Add a constraints to "bmap" expressing i_pos > o_pos + */ +static struct isl_basic_map *var_more(struct isl_basic_map *bmap, unsigned pos) +{ + int i; + unsigned nparam; + unsigned n_in; + + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + goto error; + nparam = isl_basic_map_n_param(bmap); + n_in = isl_basic_map_n_in(bmap); + isl_seq_clr(bmap->ineq[i], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->ineq[i][0], -1); + isl_int_set_si(bmap->ineq[i][1+nparam+pos], 1); + isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], -1); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Add a constraint to "bmap" expressing i_pos >= o_pos + */ +static __isl_give isl_basic_map *var_more_or_equal( + __isl_take isl_basic_map *bmap, unsigned pos) +{ + int i; + unsigned nparam; + unsigned n_in; + + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + goto error; + nparam = isl_basic_map_n_param(bmap); + n_in = isl_basic_map_n_in(bmap); + isl_seq_clr(bmap->ineq[i], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->ineq[i][1+nparam+pos], 1); + isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], -1); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_equal( + __isl_take isl_space *dim, unsigned n_equal) +{ + int i; + struct isl_basic_map *bmap; + bmap = isl_basic_map_alloc_space(dim, 0, n_equal, 0); + if (!bmap) + return NULL; + for (i = 0; i < n_equal && bmap; ++i) + bmap = var_equal(bmap, i); + return isl_basic_map_finalize(bmap); +} + +/* Return a relation on of dimension "dim" expressing i_[0..pos] << o_[0..pos] + */ +__isl_give isl_basic_map *isl_basic_map_less_at(__isl_take isl_space *dim, + unsigned pos) +{ + int i; + struct isl_basic_map *bmap; + bmap = isl_basic_map_alloc_space(dim, 0, pos, 1); + if (!bmap) + return NULL; + for (i = 0; i < pos && bmap; ++i) + bmap = var_equal(bmap, i); + if (bmap) + bmap = var_less(bmap, pos); + return isl_basic_map_finalize(bmap); +} + +/* Return a relation on of dimension "dim" expressing i_[0..pos] <<= o_[0..pos] + */ +__isl_give isl_basic_map *isl_basic_map_less_or_equal_at( + __isl_take isl_space *dim, unsigned pos) +{ + int i; + isl_basic_map *bmap; + + bmap = isl_basic_map_alloc_space(dim, 0, pos, 1); + for (i = 0; i < pos; ++i) + bmap = var_equal(bmap, i); + bmap = var_less_or_equal(bmap, pos); + return isl_basic_map_finalize(bmap); +} + +/* Return a relation on pairs of sets of dimension "dim" expressing i_pos > o_pos + */ +__isl_give isl_basic_map *isl_basic_map_more_at(__isl_take isl_space *dim, + unsigned pos) +{ + int i; + struct isl_basic_map *bmap; + bmap = isl_basic_map_alloc_space(dim, 0, pos, 1); + if (!bmap) + return NULL; + for (i = 0; i < pos && bmap; ++i) + bmap = var_equal(bmap, i); + if (bmap) + bmap = var_more(bmap, pos); + return isl_basic_map_finalize(bmap); +} + +/* Return a relation on of dimension "dim" expressing i_[0..pos] >>= o_[0..pos] + */ +__isl_give isl_basic_map *isl_basic_map_more_or_equal_at( + __isl_take isl_space *dim, unsigned pos) +{ + int i; + isl_basic_map *bmap; + + bmap = isl_basic_map_alloc_space(dim, 0, pos, 1); + for (i = 0; i < pos; ++i) + bmap = var_equal(bmap, i); + bmap = var_more_or_equal(bmap, pos); + return isl_basic_map_finalize(bmap); +} + +static __isl_give isl_map *map_lex_lte_first(__isl_take isl_space *dims, + unsigned n, int equal) +{ + struct isl_map *map; + int i; + + if (n == 0 && equal) + return isl_map_universe(dims); + + map = isl_map_alloc_space(isl_space_copy(dims), n, ISL_MAP_DISJOINT); + + for (i = 0; i + 1 < n; ++i) + map = isl_map_add_basic_map(map, + isl_basic_map_less_at(isl_space_copy(dims), i)); + if (n > 0) { + if (equal) + map = isl_map_add_basic_map(map, + isl_basic_map_less_or_equal_at(dims, n - 1)); + else + map = isl_map_add_basic_map(map, + isl_basic_map_less_at(dims, n - 1)); + } else + isl_space_free(dims); + + return map; +} + +static __isl_give isl_map *map_lex_lte(__isl_take isl_space *dims, int equal) +{ + if (!dims) + return NULL; + return map_lex_lte_first(dims, dims->n_out, equal); +} + +__isl_give isl_map *isl_map_lex_lt_first(__isl_take isl_space *dim, unsigned n) +{ + return map_lex_lte_first(dim, n, 0); +} + +__isl_give isl_map *isl_map_lex_le_first(__isl_take isl_space *dim, unsigned n) +{ + return map_lex_lte_first(dim, n, 1); +} + +__isl_give isl_map *isl_map_lex_lt(__isl_take isl_space *set_dim) +{ + return map_lex_lte(isl_space_map_from_set(set_dim), 0); +} + +__isl_give isl_map *isl_map_lex_le(__isl_take isl_space *set_dim) +{ + return map_lex_lte(isl_space_map_from_set(set_dim), 1); +} + +static __isl_give isl_map *map_lex_gte_first(__isl_take isl_space *dims, + unsigned n, int equal) +{ + struct isl_map *map; + int i; + + if (n == 0 && equal) + return isl_map_universe(dims); + + map = isl_map_alloc_space(isl_space_copy(dims), n, ISL_MAP_DISJOINT); + + for (i = 0; i + 1 < n; ++i) + map = isl_map_add_basic_map(map, + isl_basic_map_more_at(isl_space_copy(dims), i)); + if (n > 0) { + if (equal) + map = isl_map_add_basic_map(map, + isl_basic_map_more_or_equal_at(dims, n - 1)); + else + map = isl_map_add_basic_map(map, + isl_basic_map_more_at(dims, n - 1)); + } else + isl_space_free(dims); + + return map; +} + +static __isl_give isl_map *map_lex_gte(__isl_take isl_space *dims, int equal) +{ + if (!dims) + return NULL; + return map_lex_gte_first(dims, dims->n_out, equal); +} + +__isl_give isl_map *isl_map_lex_gt_first(__isl_take isl_space *dim, unsigned n) +{ + return map_lex_gte_first(dim, n, 0); +} + +__isl_give isl_map *isl_map_lex_ge_first(__isl_take isl_space *dim, unsigned n) +{ + return map_lex_gte_first(dim, n, 1); +} + +__isl_give isl_map *isl_map_lex_gt(__isl_take isl_space *set_dim) +{ + return map_lex_gte(isl_space_map_from_set(set_dim), 0); +} + +__isl_give isl_map *isl_map_lex_ge(__isl_take isl_space *set_dim) +{ + return map_lex_gte(isl_space_map_from_set(set_dim), 1); +} + +__isl_give isl_map *isl_set_lex_le_set(__isl_take isl_set *set1, + __isl_take isl_set *set2) +{ + isl_map *map; + map = isl_map_lex_le(isl_set_get_space(set1)); + map = isl_map_intersect_domain(map, set1); + map = isl_map_intersect_range(map, set2); + return map; +} + +__isl_give isl_map *isl_set_lex_lt_set(__isl_take isl_set *set1, + __isl_take isl_set *set2) +{ + isl_map *map; + map = isl_map_lex_lt(isl_set_get_space(set1)); + map = isl_map_intersect_domain(map, set1); + map = isl_map_intersect_range(map, set2); + return map; +} + +__isl_give isl_map *isl_set_lex_ge_set(__isl_take isl_set *set1, + __isl_take isl_set *set2) +{ + isl_map *map; + map = isl_map_lex_ge(isl_set_get_space(set1)); + map = isl_map_intersect_domain(map, set1); + map = isl_map_intersect_range(map, set2); + return map; +} + +__isl_give isl_map *isl_set_lex_gt_set(__isl_take isl_set *set1, + __isl_take isl_set *set2) +{ + isl_map *map; + map = isl_map_lex_gt(isl_set_get_space(set1)); + map = isl_map_intersect_domain(map, set1); + map = isl_map_intersect_range(map, set2); + return map; +} + +__isl_give isl_map *isl_map_lex_le_map(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *map; + map = isl_map_lex_le(isl_space_range(isl_map_get_space(map1))); + map = isl_map_apply_domain(map, isl_map_reverse(map1)); + map = isl_map_apply_range(map, isl_map_reverse(map2)); + return map; +} + +__isl_give isl_map *isl_map_lex_lt_map(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *map; + map = isl_map_lex_lt(isl_space_range(isl_map_get_space(map1))); + map = isl_map_apply_domain(map, isl_map_reverse(map1)); + map = isl_map_apply_range(map, isl_map_reverse(map2)); + return map; +} + +__isl_give isl_map *isl_map_lex_ge_map(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *map; + map = isl_map_lex_ge(isl_space_range(isl_map_get_space(map1))); + map = isl_map_apply_domain(map, isl_map_reverse(map1)); + map = isl_map_apply_range(map, isl_map_reverse(map2)); + return map; +} + +__isl_give isl_map *isl_map_lex_gt_map(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *map; + map = isl_map_lex_gt(isl_space_range(isl_map_get_space(map1))); + map = isl_map_apply_domain(map, isl_map_reverse(map1)); + map = isl_map_apply_range(map, isl_map_reverse(map2)); + return map; +} + +__isl_give isl_basic_map *isl_basic_map_from_basic_set( + __isl_take isl_basic_set *bset, __isl_take isl_space *dim) +{ + struct isl_basic_map *bmap; + + bset = isl_basic_set_cow(bset); + if (!bset || !dim) + goto error; + + isl_assert(bset->ctx, isl_space_compatible(bset->dim, dim), goto error); + isl_space_free(bset->dim); + bmap = (struct isl_basic_map *) bset; + bmap->dim = dim; + return isl_basic_map_finalize(bmap); +error: + isl_basic_set_free(bset); + isl_space_free(dim); + return NULL; +} + +/* For a div d = floor(f/m), add the constraint + * + * f - m d >= 0 + */ +static int add_upper_div_constraint(__isl_keep isl_basic_map *bmap, + unsigned pos, isl_int *div) +{ + int i; + unsigned total = isl_basic_map_total_dim(bmap); + + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + return -1; + isl_seq_cpy(bmap->ineq[i], div + 1, 1 + total); + isl_int_neg(bmap->ineq[i][1 + pos], div[0]); + + return 0; +} + +/* For a div d = floor(f/m), add the constraint + * + * -(f-(m-1)) + m d >= 0 + */ +static int add_lower_div_constraint(__isl_keep isl_basic_map *bmap, + unsigned pos, isl_int *div) +{ + int i; + unsigned total = isl_basic_map_total_dim(bmap); + + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + return -1; + isl_seq_neg(bmap->ineq[i], div + 1, 1 + total); + isl_int_set(bmap->ineq[i][1 + pos], div[0]); + isl_int_add(bmap->ineq[i][0], bmap->ineq[i][0], bmap->ineq[i][1 + pos]); + isl_int_sub_ui(bmap->ineq[i][0], bmap->ineq[i][0], 1); + + return 0; +} + +/* For a div d = floor(f/m), add the constraints + * + * f - m d >= 0 + * -(f-(m-1)) + m d >= 0 + * + * Note that the second constraint is the negation of + * + * f - m d >= m + */ +int isl_basic_map_add_div_constraints_var(__isl_keep isl_basic_map *bmap, + unsigned pos, isl_int *div) +{ + if (add_upper_div_constraint(bmap, pos, div) < 0) + return -1; + if (add_lower_div_constraint(bmap, pos, div) < 0) + return -1; + return 0; +} + +int isl_basic_set_add_div_constraints_var(__isl_keep isl_basic_set *bset, + unsigned pos, isl_int *div) +{ + return isl_basic_map_add_div_constraints_var((isl_basic_map *)bset, + pos, div); +} + +int isl_basic_map_add_div_constraints(struct isl_basic_map *bmap, unsigned div) +{ + unsigned total = isl_basic_map_total_dim(bmap); + unsigned div_pos = total - bmap->n_div + div; + + return isl_basic_map_add_div_constraints_var(bmap, div_pos, + bmap->div[div]); +} + +/* For each known div d = floor(f/m), add the constraints + * + * f - m d >= 0 + * -(f-(m-1)) + m d >= 0 + * + * Remove duplicate constraints in case of some these div constraints + * already appear in "bmap". + */ +__isl_give isl_basic_map *isl_basic_map_add_known_div_constraints( + __isl_take isl_basic_map *bmap) +{ + unsigned n_div; + + if (!bmap) + return NULL; + n_div = isl_basic_map_dim(bmap, isl_dim_div); + if (n_div == 0) + return bmap; + + bmap = add_known_div_constraints(bmap); + bmap = isl_basic_map_remove_duplicate_constraints(bmap, NULL, 0); + bmap = isl_basic_map_finalize(bmap); + return bmap; +} + +/* Add the div constraint of sign "sign" for div "div" of "bmap". + * + * In particular, if this div is of the form d = floor(f/m), + * then add the constraint + * + * f - m d >= 0 + * + * if sign < 0 or the constraint + * + * -(f-(m-1)) + m d >= 0 + * + * if sign > 0. + */ +int isl_basic_map_add_div_constraint(__isl_keep isl_basic_map *bmap, + unsigned div, int sign) +{ + unsigned total; + unsigned div_pos; + + if (!bmap) + return -1; + + total = isl_basic_map_total_dim(bmap); + div_pos = total - bmap->n_div + div; + + if (sign < 0) + return add_upper_div_constraint(bmap, div_pos, bmap->div[div]); + else + return add_lower_div_constraint(bmap, div_pos, bmap->div[div]); +} + +int isl_basic_set_add_div_constraints(struct isl_basic_set *bset, unsigned div) +{ + return isl_basic_map_add_div_constraints(bset, div); +} + +struct isl_basic_set *isl_basic_map_underlying_set( + struct isl_basic_map *bmap) +{ + if (!bmap) + goto error; + if (bmap->dim->nparam == 0 && bmap->dim->n_in == 0 && + bmap->n_div == 0 && + !isl_space_is_named_or_nested(bmap->dim, isl_dim_in) && + !isl_space_is_named_or_nested(bmap->dim, isl_dim_out)) + return (struct isl_basic_set *)bmap; + bmap = isl_basic_map_cow(bmap); + if (!bmap) + goto error; + bmap->dim = isl_space_underlying(bmap->dim, bmap->n_div); + if (!bmap->dim) + goto error; + bmap->extra -= bmap->n_div; + bmap->n_div = 0; + bmap = isl_basic_map_finalize(bmap); + return (struct isl_basic_set *)bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_underlying_set( + __isl_take isl_basic_set *bset) +{ + return isl_basic_map_underlying_set((isl_basic_map *)bset); +} + +/* Replace each element in "list" by the result of applying + * isl_basic_map_underlying_set to the element. + */ +__isl_give isl_basic_set_list *isl_basic_map_list_underlying_set( + __isl_take isl_basic_map_list *list) +{ + int i, n; + + if (!list) + return NULL; + + n = isl_basic_map_list_n_basic_map(list); + for (i = 0; i < n; ++i) { + isl_basic_map *bmap; + isl_basic_set *bset; + + bmap = isl_basic_map_list_get_basic_map(list, i); + bset = isl_basic_set_underlying_set(bmap); + list = isl_basic_set_list_set_basic_set(list, i, bset); + } + + return list; +} + +struct isl_basic_map *isl_basic_map_overlying_set( + struct isl_basic_set *bset, struct isl_basic_map *like) +{ + struct isl_basic_map *bmap; + struct isl_ctx *ctx; + unsigned total; + int i; + + if (!bset || !like) + goto error; + ctx = bset->ctx; + isl_assert(ctx, bset->n_div == 0, goto error); + isl_assert(ctx, isl_basic_set_n_param(bset) == 0, goto error); + isl_assert(ctx, bset->dim->n_out == isl_basic_map_total_dim(like), + goto error); + if (like->n_div == 0) { + isl_space *space = isl_basic_map_get_space(like); + isl_basic_map_free(like); + return isl_basic_map_reset_space(bset, space); + } + bset = isl_basic_set_cow(bset); + if (!bset) + goto error; + total = bset->dim->n_out + bset->extra; + bmap = (struct isl_basic_map *)bset; + isl_space_free(bmap->dim); + bmap->dim = isl_space_copy(like->dim); + if (!bmap->dim) + goto error; + bmap->n_div = like->n_div; + bmap->extra += like->n_div; + if (bmap->extra) { + unsigned ltotal; + isl_int **div; + ltotal = total - bmap->extra + like->extra; + if (ltotal > total) + ltotal = total; + bmap->block2 = isl_blk_extend(ctx, bmap->block2, + bmap->extra * (1 + 1 + total)); + if (isl_blk_is_error(bmap->block2)) + goto error; + div = isl_realloc_array(ctx, bmap->div, isl_int *, bmap->extra); + if (!div) + goto error; + bmap->div = div; + for (i = 0; i < bmap->extra; ++i) + bmap->div[i] = bmap->block2.data + i * (1 + 1 + total); + for (i = 0; i < like->n_div; ++i) { + isl_seq_cpy(bmap->div[i], like->div[i], 1 + 1 + ltotal); + isl_seq_clr(bmap->div[i]+1+1+ltotal, total - ltotal); + } + bmap = isl_basic_map_add_known_div_constraints(bmap); + } + isl_basic_map_free(like); + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_basic_map_free(like); + isl_basic_set_free(bset); + return NULL; +} + +struct isl_basic_set *isl_basic_set_from_underlying_set( + struct isl_basic_set *bset, struct isl_basic_set *like) +{ + return (struct isl_basic_set *) + isl_basic_map_overlying_set(bset, (struct isl_basic_map *)like); +} + +struct isl_set *isl_set_from_underlying_set( + struct isl_set *set, struct isl_basic_set *like) +{ + int i; + + if (!set || !like) + goto error; + isl_assert(set->ctx, set->dim->n_out == isl_basic_set_total_dim(like), + goto error); + if (isl_space_is_equal(set->dim, like->dim) && like->n_div == 0) { + isl_basic_set_free(like); + return set; + } + set = isl_set_cow(set); + if (!set) + goto error; + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_from_underlying_set(set->p[i], + isl_basic_set_copy(like)); + if (!set->p[i]) + goto error; + } + isl_space_free(set->dim); + set->dim = isl_space_copy(like->dim); + if (!set->dim) + goto error; + isl_basic_set_free(like); + return set; +error: + isl_basic_set_free(like); + isl_set_free(set); + return NULL; +} + +struct isl_set *isl_map_underlying_set(struct isl_map *map) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + map->dim = isl_space_cow(map->dim); + if (!map->dim) + goto error; + + for (i = 1; i < map->n; ++i) + isl_assert(map->ctx, map->p[0]->n_div == map->p[i]->n_div, + goto error); + for (i = 0; i < map->n; ++i) { + map->p[i] = (struct isl_basic_map *) + isl_basic_map_underlying_set(map->p[i]); + if (!map->p[i]) + goto error; + } + if (map->n == 0) + map->dim = isl_space_underlying(map->dim, 0); + else { + isl_space_free(map->dim); + map->dim = isl_space_copy(map->p[0]->dim); + } + if (!map->dim) + goto error; + return (struct isl_set *)map; +error: + isl_map_free(map); + return NULL; +} + +struct isl_set *isl_set_to_underlying_set(struct isl_set *set) +{ + return (struct isl_set *)isl_map_underlying_set((struct isl_map *)set); +} + +/* Replace the space of "bmap" by "space". + * + * If the space of "bmap" is identical to "space" (including the identifiers + * of the input and output dimensions), then simply return the original input. + */ +__isl_give isl_basic_map *isl_basic_map_reset_space( + __isl_take isl_basic_map *bmap, __isl_take isl_space *space) +{ + isl_bool equal; + + if (!bmap) + goto error; + equal = isl_space_is_equal(bmap->dim, space); + if (equal >= 0 && equal) + equal = isl_space_match(bmap->dim, isl_dim_in, + space, isl_dim_in); + if (equal >= 0 && equal) + equal = isl_space_match(bmap->dim, isl_dim_out, + space, isl_dim_out); + if (equal < 0) + goto error; + if (equal) { + isl_space_free(space); + return bmap; + } + bmap = isl_basic_map_cow(bmap); + if (!bmap || !space) + goto error; + + isl_space_free(bmap->dim); + bmap->dim = space; + + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + isl_space_free(space); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_reset_space( + __isl_take isl_basic_set *bset, __isl_take isl_space *dim) +{ + return (isl_basic_set *)isl_basic_map_reset_space((isl_basic_map *)bset, + dim); +} + +__isl_give isl_map *isl_map_reset_space(__isl_take isl_map *map, + __isl_take isl_space *dim) +{ + int i; + + map = isl_map_cow(map); + if (!map || !dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_reset_space(map->p[i], + isl_space_copy(dim)); + if (!map->p[i]) + goto error; + } + isl_space_free(map->dim); + map->dim = dim; + + return map; +error: + isl_map_free(map); + isl_space_free(dim); + return NULL; +} + +__isl_give isl_set *isl_set_reset_space(__isl_take isl_set *set, + __isl_take isl_space *dim) +{ + return (struct isl_set *) isl_map_reset_space((struct isl_map *)set, dim); +} + +/* Compute the parameter domain of the given basic set. + */ +__isl_give isl_basic_set *isl_basic_set_params(__isl_take isl_basic_set *bset) +{ + isl_space *space; + unsigned n; + + if (isl_basic_set_is_params(bset)) + return bset; + + n = isl_basic_set_dim(bset, isl_dim_set); + bset = isl_basic_set_project_out(bset, isl_dim_set, 0, n); + space = isl_basic_set_get_space(bset); + space = isl_space_params(space); + bset = isl_basic_set_reset_space(bset, space); + return bset; +} + +/* Construct a zero-dimensional basic set with the given parameter domain. + */ +__isl_give isl_basic_set *isl_basic_set_from_params( + __isl_take isl_basic_set *bset) +{ + isl_space *space; + space = isl_basic_set_get_space(bset); + space = isl_space_set_from_params(space); + bset = isl_basic_set_reset_space(bset, space); + return bset; +} + +/* Compute the parameter domain of the given set. + */ +__isl_give isl_set *isl_set_params(__isl_take isl_set *set) +{ + isl_space *space; + unsigned n; + + if (isl_set_is_params(set)) + return set; + + n = isl_set_dim(set, isl_dim_set); + set = isl_set_project_out(set, isl_dim_set, 0, n); + space = isl_set_get_space(set); + space = isl_space_params(space); + set = isl_set_reset_space(set, space); + return set; +} + +/* Construct a zero-dimensional set with the given parameter domain. + */ +__isl_give isl_set *isl_set_from_params(__isl_take isl_set *set) +{ + isl_space *space; + space = isl_set_get_space(set); + space = isl_space_set_from_params(space); + set = isl_set_reset_space(set, space); + return set; +} + +/* Compute the parameter domain of the given map. + */ +__isl_give isl_set *isl_map_params(__isl_take isl_map *map) +{ + isl_space *space; + unsigned n; + + n = isl_map_dim(map, isl_dim_in); + map = isl_map_project_out(map, isl_dim_in, 0, n); + n = isl_map_dim(map, isl_dim_out); + map = isl_map_project_out(map, isl_dim_out, 0, n); + space = isl_map_get_space(map); + space = isl_space_params(space); + map = isl_map_reset_space(map, space); + return map; +} + +struct isl_basic_set *isl_basic_map_domain(struct isl_basic_map *bmap) +{ + isl_space *space; + unsigned n_out; + + if (!bmap) + return NULL; + space = isl_space_domain(isl_basic_map_get_space(bmap)); + + n_out = isl_basic_map_n_out(bmap); + bmap = isl_basic_map_project_out(bmap, isl_dim_out, 0, n_out); + + return isl_basic_map_reset_space(bmap, space); +} + +int isl_basic_map_may_be_set(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return -1; + return isl_space_may_be_set(bmap->dim); +} + +/* Is this basic map actually a set? + * Users should never call this function. Outside of isl, + * the type should indicate whether something is a set or a map. + */ +int isl_basic_map_is_set(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return -1; + return isl_space_is_set(bmap->dim); +} + +struct isl_basic_set *isl_basic_map_range(struct isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + if (isl_basic_map_is_set(bmap)) + return bmap; + return isl_basic_map_domain(isl_basic_map_reverse(bmap)); +} + +__isl_give isl_basic_map *isl_basic_map_domain_map( + __isl_take isl_basic_map *bmap) +{ + int i, k; + isl_space *dim; + isl_basic_map *domain; + int nparam, n_in, n_out; + unsigned total; + + nparam = isl_basic_map_dim(bmap, isl_dim_param); + n_in = isl_basic_map_dim(bmap, isl_dim_in); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + + dim = isl_space_from_range(isl_space_domain(isl_basic_map_get_space(bmap))); + domain = isl_basic_map_universe(dim); + + bmap = isl_basic_map_from_domain(isl_basic_map_wrap(bmap)); + bmap = isl_basic_map_apply_range(bmap, domain); + bmap = isl_basic_map_extend_constraints(bmap, n_in, 0); + + total = isl_basic_map_total_dim(bmap); + + for (i = 0; i < n_in; ++i) { + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->eq[k], 1 + total); + isl_int_set_si(bmap->eq[k][1 + nparam + i], -1); + isl_int_set_si(bmap->eq[k][1 + nparam + n_in + n_out + i], 1); + } + + bmap = isl_basic_map_gauss(bmap, NULL); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_range_map( + __isl_take isl_basic_map *bmap) +{ + int i, k; + isl_space *dim; + isl_basic_map *range; + int nparam, n_in, n_out; + unsigned total; + + nparam = isl_basic_map_dim(bmap, isl_dim_param); + n_in = isl_basic_map_dim(bmap, isl_dim_in); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + + dim = isl_space_from_range(isl_space_range(isl_basic_map_get_space(bmap))); + range = isl_basic_map_universe(dim); + + bmap = isl_basic_map_from_domain(isl_basic_map_wrap(bmap)); + bmap = isl_basic_map_apply_range(bmap, range); + bmap = isl_basic_map_extend_constraints(bmap, n_out, 0); + + total = isl_basic_map_total_dim(bmap); + + for (i = 0; i < n_out; ++i) { + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->eq[k], 1 + total); + isl_int_set_si(bmap->eq[k][1 + nparam + n_in + i], -1); + isl_int_set_si(bmap->eq[k][1 + nparam + n_in + n_out + i], 1); + } + + bmap = isl_basic_map_gauss(bmap, NULL); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +int isl_map_may_be_set(__isl_keep isl_map *map) +{ + if (!map) + return -1; + return isl_space_may_be_set(map->dim); +} + +/* Is this map actually a set? + * Users should never call this function. Outside of isl, + * the type should indicate whether something is a set or a map. + */ +int isl_map_is_set(__isl_keep isl_map *map) +{ + if (!map) + return -1; + return isl_space_is_set(map->dim); +} + +struct isl_set *isl_map_range(struct isl_map *map) +{ + int i; + struct isl_set *set; + + if (!map) + goto error; + if (isl_map_is_set(map)) + return (isl_set *)map; + + map = isl_map_cow(map); + if (!map) + goto error; + + set = (struct isl_set *) map; + set->dim = isl_space_range(set->dim); + if (!set->dim) + goto error; + for (i = 0; i < map->n; ++i) { + set->p[i] = isl_basic_map_range(map->p[i]); + if (!set->p[i]) + goto error; + } + ISL_F_CLR(set, ISL_MAP_DISJOINT); + ISL_F_CLR(set, ISL_SET_NORMALIZED); + return set; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_map *isl_map_domain_map(__isl_take isl_map *map) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_domain_map(map->dim); + if (!map->dim) + goto error; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_domain_map(map->p[i]); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_DISJOINT); + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_map *isl_map_range_map(__isl_take isl_map *map) +{ + int i; + isl_space *range_dim; + + map = isl_map_cow(map); + if (!map) + return NULL; + + range_dim = isl_space_range(isl_map_get_space(map)); + range_dim = isl_space_from_range(range_dim); + map->dim = isl_space_from_domain(isl_space_wrap(map->dim)); + map->dim = isl_space_join(map->dim, range_dim); + if (!map->dim) + goto error; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_range_map(map->p[i]); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_DISJOINT); + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Given a wrapped map of the form A[B -> C], + * return the map A[B -> C] -> B. + */ +__isl_give isl_map *isl_set_wrapped_domain_map(__isl_take isl_set *set) +{ + isl_id *id; + isl_map *map; + + if (!set) + return NULL; + if (!isl_set_has_tuple_id(set)) + return isl_map_domain_map(isl_set_unwrap(set)); + + id = isl_set_get_tuple_id(set); + map = isl_map_domain_map(isl_set_unwrap(set)); + map = isl_map_set_tuple_id(map, isl_dim_in, id); + + return map; +} + +__isl_give isl_map *isl_map_from_set(__isl_take isl_set *set, + __isl_take isl_space *dim) +{ + int i; + struct isl_map *map = NULL; + + set = isl_set_cow(set); + if (!set || !dim) + goto error; + isl_assert(set->ctx, isl_space_compatible(set->dim, dim), goto error); + map = (struct isl_map *)set; + for (i = 0; i < set->n; ++i) { + map->p[i] = isl_basic_map_from_basic_set( + set->p[i], isl_space_copy(dim)); + if (!map->p[i]) + goto error; + } + isl_space_free(map->dim); + map->dim = dim; + return map; +error: + isl_space_free(dim); + isl_set_free(set); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_from_domain( + __isl_take isl_basic_set *bset) +{ + return isl_basic_map_reverse(isl_basic_map_from_range(bset)); +} + +__isl_give isl_basic_map *isl_basic_map_from_range( + __isl_take isl_basic_set *bset) +{ + isl_space *space; + space = isl_basic_set_get_space(bset); + space = isl_space_from_range(space); + bset = isl_basic_set_reset_space(bset, space); + return (isl_basic_map *)bset; +} + +/* Create a relation with the given set as range. + * The domain of the created relation is a zero-dimensional + * flat anonymous space. + */ +__isl_give isl_map *isl_map_from_range(__isl_take isl_set *set) +{ + isl_space *space; + space = isl_set_get_space(set); + space = isl_space_from_range(space); + set = isl_set_reset_space(set, space); + return (struct isl_map *)set; +} + +/* Create a relation with the given set as domain. + * The range of the created relation is a zero-dimensional + * flat anonymous space. + */ +__isl_give isl_map *isl_map_from_domain(__isl_take isl_set *set) +{ + return isl_map_reverse(isl_map_from_range(set)); +} + +__isl_give isl_basic_map *isl_basic_map_from_domain_and_range( + __isl_take isl_basic_set *domain, __isl_take isl_basic_set *range) +{ + return isl_basic_map_apply_range(isl_basic_map_reverse(domain), range); +} + +__isl_give isl_map *isl_map_from_domain_and_range(__isl_take isl_set *domain, + __isl_take isl_set *range) +{ + return isl_map_apply_range(isl_map_reverse(domain), range); +} + +/* Return a newly allocated isl_map with given space and flags and + * room for "n" basic maps. + * Make sure that all cached information is cleared. + */ +__isl_give isl_map *isl_map_alloc_space(__isl_take isl_space *space, int n, + unsigned flags) +{ + struct isl_map *map; + + if (!space) + return NULL; + if (n < 0) + isl_die(space->ctx, isl_error_internal, + "negative number of basic maps", goto error); + map = isl_calloc(space->ctx, struct isl_map, + sizeof(struct isl_map) + + (n - 1) * sizeof(struct isl_basic_map *)); + if (!map) + goto error; + + map->ctx = space->ctx; + isl_ctx_ref(map->ctx); + map->ref = 1; + map->size = n; + map->n = 0; + map->dim = space; + map->flags = flags; + return map; +error: + isl_space_free(space); + return NULL; +} + +struct isl_map *isl_map_alloc(struct isl_ctx *ctx, + unsigned nparam, unsigned in, unsigned out, int n, + unsigned flags) +{ + struct isl_map *map; + isl_space *dims; + + dims = isl_space_alloc(ctx, nparam, in, out); + if (!dims) + return NULL; + + map = isl_map_alloc_space(dims, n, flags); + return map; +} + +__isl_give isl_basic_map *isl_basic_map_empty(__isl_take isl_space *dim) +{ + struct isl_basic_map *bmap; + bmap = isl_basic_map_alloc_space(dim, 0, 1, 0); + bmap = isl_basic_map_set_to_empty(bmap); + return bmap; +} + +__isl_give isl_basic_set *isl_basic_set_empty(__isl_take isl_space *dim) +{ + struct isl_basic_set *bset; + bset = isl_basic_set_alloc_space(dim, 0, 1, 0); + bset = isl_basic_set_set_to_empty(bset); + return bset; +} + +__isl_give isl_basic_map *isl_basic_map_universe(__isl_take isl_space *dim) +{ + struct isl_basic_map *bmap; + bmap = isl_basic_map_alloc_space(dim, 0, 0, 0); + bmap = isl_basic_map_finalize(bmap); + return bmap; +} + +__isl_give isl_basic_set *isl_basic_set_universe(__isl_take isl_space *dim) +{ + struct isl_basic_set *bset; + bset = isl_basic_set_alloc_space(dim, 0, 0, 0); + bset = isl_basic_set_finalize(bset); + return bset; +} + +__isl_give isl_basic_map *isl_basic_map_nat_universe(__isl_take isl_space *dim) +{ + int i; + unsigned total = isl_space_dim(dim, isl_dim_all); + isl_basic_map *bmap; + + bmap= isl_basic_map_alloc_space(dim, 0, 0, total); + for (i = 0; i < total; ++i) { + int k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->ineq[k], 1 + total); + isl_int_set_si(bmap->ineq[k][1 + i], 1); + } + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_nat_universe(__isl_take isl_space *dim) +{ + return isl_basic_map_nat_universe(dim); +} + +__isl_give isl_map *isl_map_nat_universe(__isl_take isl_space *dim) +{ + return isl_map_from_basic_map(isl_basic_map_nat_universe(dim)); +} + +__isl_give isl_set *isl_set_nat_universe(__isl_take isl_space *dim) +{ + return isl_map_nat_universe(dim); +} + +__isl_give isl_map *isl_map_empty(__isl_take isl_space *dim) +{ + return isl_map_alloc_space(dim, 0, ISL_MAP_DISJOINT); +} + +__isl_give isl_set *isl_set_empty(__isl_take isl_space *dim) +{ + return isl_set_alloc_space(dim, 0, ISL_MAP_DISJOINT); +} + +__isl_give isl_map *isl_map_universe(__isl_take isl_space *dim) +{ + struct isl_map *map; + if (!dim) + return NULL; + map = isl_map_alloc_space(isl_space_copy(dim), 1, ISL_MAP_DISJOINT); + map = isl_map_add_basic_map(map, isl_basic_map_universe(dim)); + return map; +} + +__isl_give isl_set *isl_set_universe(__isl_take isl_space *dim) +{ + struct isl_set *set; + if (!dim) + return NULL; + set = isl_set_alloc_space(isl_space_copy(dim), 1, ISL_MAP_DISJOINT); + set = isl_set_add_basic_set(set, isl_basic_set_universe(dim)); + return set; +} + +struct isl_map *isl_map_dup(struct isl_map *map) +{ + int i; + struct isl_map *dup; + + if (!map) + return NULL; + dup = isl_map_alloc_space(isl_space_copy(map->dim), map->n, map->flags); + for (i = 0; i < map->n; ++i) + dup = isl_map_add_basic_map(dup, isl_basic_map_copy(map->p[i])); + return dup; +} + +__isl_give isl_map *isl_map_add_basic_map(__isl_take isl_map *map, + __isl_take isl_basic_map *bmap) +{ + if (!bmap || !map) + goto error; + if (isl_basic_map_plain_is_empty(bmap)) { + isl_basic_map_free(bmap); + return map; + } + isl_assert(map->ctx, isl_space_is_equal(map->dim, bmap->dim), goto error); + isl_assert(map->ctx, map->n < map->size, goto error); + map->p[map->n] = bmap; + map->n++; + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + if (map) + isl_map_free(map); + if (bmap) + isl_basic_map_free(bmap); + return NULL; +} + +__isl_null isl_map *isl_map_free(__isl_take isl_map *map) +{ + int i; + + if (!map) + return NULL; + + if (--map->ref > 0) + return NULL; + + clear_caches(map); + isl_ctx_deref(map->ctx); + for (i = 0; i < map->n; ++i) + isl_basic_map_free(map->p[i]); + isl_space_free(map->dim); + free(map); + + return NULL; +} + +static struct isl_basic_map *isl_basic_map_fix_pos_si( + struct isl_basic_map *bmap, unsigned pos, int value) +{ + int j; + + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_extend_constraints(bmap, 1, 0); + j = isl_basic_map_alloc_equality(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->eq[j] + 1, isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[j][pos], -1); + isl_int_set_si(bmap->eq[j][0], value); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +static __isl_give isl_basic_map *isl_basic_map_fix_pos( + __isl_take isl_basic_map *bmap, unsigned pos, isl_int value) +{ + int j; + + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_extend_constraints(bmap, 1, 0); + j = isl_basic_map_alloc_equality(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->eq[j] + 1, isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[j][pos], -1); + isl_int_set(bmap->eq[j][0], value); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_map *isl_basic_map_fix_si(struct isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value) +{ + if (!bmap) + return NULL; + isl_assert(bmap->ctx, pos < isl_basic_map_dim(bmap, type), goto error); + return isl_basic_map_fix_pos_si(bmap, + isl_basic_map_offset(bmap, type) + pos, value); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_fix(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + if (!bmap) + return NULL; + isl_assert(bmap->ctx, pos < isl_basic_map_dim(bmap, type), goto error); + return isl_basic_map_fix_pos(bmap, + isl_basic_map_offset(bmap, type) + pos, value); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Fix the value of the variable at position "pos" of type "type" of "bmap" + * to be equal to "v". + */ +__isl_give isl_basic_map *isl_basic_map_fix_val(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v) +{ + if (!bmap || !v) + goto error; + if (!isl_val_is_int(v)) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "expecting integer value", goto error); + if (pos >= isl_basic_map_dim(bmap, type)) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "index out of bounds", goto error); + pos += isl_basic_map_offset(bmap, type); + bmap = isl_basic_map_fix_pos(bmap, pos, v->n); + isl_val_free(v); + return bmap; +error: + isl_basic_map_free(bmap); + isl_val_free(v); + return NULL; +} + +/* Fix the value of the variable at position "pos" of type "type" of "bset" + * to be equal to "v". + */ +__isl_give isl_basic_set *isl_basic_set_fix_val(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v) +{ + return isl_basic_map_fix_val(bset, type, pos, v); +} + +struct isl_basic_set *isl_basic_set_fix_si(struct isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, int value) +{ + return (struct isl_basic_set *) + isl_basic_map_fix_si((struct isl_basic_map *)bset, + type, pos, value); +} + +__isl_give isl_basic_set *isl_basic_set_fix(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + return (struct isl_basic_set *) + isl_basic_map_fix((struct isl_basic_map *)bset, + type, pos, value); +} + +struct isl_basic_map *isl_basic_map_fix_input_si(struct isl_basic_map *bmap, + unsigned input, int value) +{ + return isl_basic_map_fix_si(bmap, isl_dim_in, input, value); +} + +struct isl_basic_set *isl_basic_set_fix_dim_si(struct isl_basic_set *bset, + unsigned dim, int value) +{ + return (struct isl_basic_set *) + isl_basic_map_fix_si((struct isl_basic_map *)bset, + isl_dim_set, dim, value); +} + +static int remove_if_empty(__isl_keep isl_map *map, int i) +{ + int empty = isl_basic_map_plain_is_empty(map->p[i]); + + if (empty < 0) + return -1; + if (!empty) + return 0; + + isl_basic_map_free(map->p[i]); + if (i != map->n - 1) { + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + map->p[i] = map->p[map->n - 1]; + } + map->n--; + + return 0; +} + +/* Perform "fn" on each basic map of "map", where we may not be holding + * the only reference to "map". + * In particular, "fn" should be a semantics preserving operation + * that we want to apply to all copies of "map". We therefore need + * to be careful not to modify "map" in a way that breaks "map" + * in case anything goes wrong. + */ +__isl_give isl_map *isl_map_inline_foreach_basic_map(__isl_take isl_map *map, + __isl_give isl_basic_map *(*fn)(__isl_take isl_basic_map *bmap)) +{ + struct isl_basic_map *bmap; + int i; + + if (!map) + return NULL; + + for (i = map->n - 1; i >= 0; --i) { + bmap = isl_basic_map_copy(map->p[i]); + bmap = fn(bmap); + if (!bmap) + goto error; + isl_basic_map_free(map->p[i]); + map->p[i] = bmap; + if (remove_if_empty(map, i) < 0) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +struct isl_map *isl_map_fix_si(struct isl_map *map, + enum isl_dim_type type, unsigned pos, int value) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + isl_assert(map->ctx, pos < isl_map_dim(map, type), goto error); + for (i = map->n - 1; i >= 0; --i) { + map->p[i] = isl_basic_map_fix_si(map->p[i], type, pos, value); + if (remove_if_empty(map, i) < 0) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_fix_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value) +{ + return (struct isl_set *) + isl_map_fix_si((struct isl_map *)set, type, pos, value); +} + +__isl_give isl_map *isl_map_fix(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + isl_assert(map->ctx, pos < isl_map_dim(map, type), goto error); + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_fix(map->p[i], type, pos, value); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_fix(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + return (struct isl_set *)isl_map_fix((isl_map *)set, type, pos, value); +} + +/* Fix the value of the variable at position "pos" of type "type" of "map" + * to be equal to "v". + */ +__isl_give isl_map *isl_map_fix_val(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v) +{ + int i; + + map = isl_map_cow(map); + if (!map || !v) + goto error; + + if (!isl_val_is_int(v)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "expecting integer value", goto error); + if (pos >= isl_map_dim(map, type)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "index out of bounds", goto error); + for (i = map->n - 1; i >= 0; --i) { + map->p[i] = isl_basic_map_fix_val(map->p[i], type, pos, + isl_val_copy(v)); + if (remove_if_empty(map, i) < 0) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + isl_val_free(v); + return map; +error: + isl_map_free(map); + isl_val_free(v); + return NULL; +} + +/* Fix the value of the variable at position "pos" of type "type" of "set" + * to be equal to "v". + */ +__isl_give isl_set *isl_set_fix_val(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v) +{ + return isl_map_fix_val(set, type, pos, v); +} + +struct isl_map *isl_map_fix_input_si(struct isl_map *map, + unsigned input, int value) +{ + return isl_map_fix_si(map, isl_dim_in, input, value); +} + +struct isl_set *isl_set_fix_dim_si(struct isl_set *set, unsigned dim, int value) +{ + return (struct isl_set *) + isl_map_fix_si((struct isl_map *)set, isl_dim_set, dim, value); +} + +static __isl_give isl_basic_map *basic_map_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value, int upper) +{ + int j; + + if (!bmap) + return NULL; + isl_assert(bmap->ctx, pos < isl_basic_map_dim(bmap, type), goto error); + pos += isl_basic_map_offset(bmap, type); + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_extend_constraints(bmap, 0, 1); + j = isl_basic_map_alloc_inequality(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->ineq[j], 1 + isl_basic_map_total_dim(bmap)); + if (upper) { + isl_int_set_si(bmap->ineq[j][pos], -1); + isl_int_set_si(bmap->ineq[j][0], value); + } else { + isl_int_set_si(bmap->ineq[j][pos], 1); + isl_int_set_si(bmap->ineq[j][0], -value); + } + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_lower_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value) +{ + return basic_map_bound_si(bmap, type, pos, value, 0); +} + +/* Constrain the values of the given dimension to be no greater than "value". + */ +__isl_give isl_basic_map *isl_basic_map_upper_bound_si( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int value) +{ + return basic_map_bound_si(bmap, type, pos, value, 1); +} + +struct isl_basic_set *isl_basic_set_lower_bound_dim(struct isl_basic_set *bset, + unsigned dim, isl_int value) +{ + int j; + + bset = isl_basic_set_cow(bset); + bset = isl_basic_set_extend_constraints(bset, 0, 1); + j = isl_basic_set_alloc_inequality(bset); + if (j < 0) + goto error; + isl_seq_clr(bset->ineq[j], 1 + isl_basic_set_total_dim(bset)); + isl_int_set_si(bset->ineq[j][1 + isl_basic_set_n_param(bset) + dim], 1); + isl_int_neg(bset->ineq[j][0], value); + bset = isl_basic_set_simplify(bset); + return isl_basic_set_finalize(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +static __isl_give isl_map *map_bound_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value, int upper) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + isl_assert(map->ctx, pos < isl_map_dim(map, type), goto error); + for (i = 0; i < map->n; ++i) { + map->p[i] = basic_map_bound_si(map->p[i], + type, pos, value, upper); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_map *isl_map_lower_bound_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value) +{ + return map_bound_si(map, type, pos, value, 0); +} + +__isl_give isl_map *isl_map_upper_bound_si(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int value) +{ + return map_bound_si(map, type, pos, value, 1); +} + +__isl_give isl_set *isl_set_lower_bound_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value) +{ + return (struct isl_set *) + isl_map_lower_bound_si((struct isl_map *)set, type, pos, value); +} + +__isl_give isl_set *isl_set_upper_bound_si(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, int value) +{ + return isl_map_upper_bound_si(set, type, pos, value); +} + +/* Bound the given variable of "bmap" from below (or above is "upper" + * is set) to "value". + */ +static __isl_give isl_basic_map *basic_map_bound( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, isl_int value, int upper) +{ + int j; + + if (!bmap) + return NULL; + if (pos >= isl_basic_map_dim(bmap, type)) + isl_die(bmap->ctx, isl_error_invalid, + "index out of bounds", goto error); + pos += isl_basic_map_offset(bmap, type); + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_extend_constraints(bmap, 0, 1); + j = isl_basic_map_alloc_inequality(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->ineq[j], 1 + isl_basic_map_total_dim(bmap)); + if (upper) { + isl_int_set_si(bmap->ineq[j][pos], -1); + isl_int_set(bmap->ineq[j][0], value); + } else { + isl_int_set_si(bmap->ineq[j][pos], 1); + isl_int_neg(bmap->ineq[j][0], value); + } + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Bound the given variable of "map" from below (or above is "upper" + * is set) to "value". + */ +static __isl_give isl_map *map_bound(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int value, int upper) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + if (pos >= isl_map_dim(map, type)) + isl_die(map->ctx, isl_error_invalid, + "index out of bounds", goto error); + for (i = map->n - 1; i >= 0; --i) { + map->p[i] = basic_map_bound(map->p[i], type, pos, value, upper); + if (remove_if_empty(map, i) < 0) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_map *isl_map_lower_bound(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + return map_bound(map, type, pos, value, 0); +} + +__isl_give isl_map *isl_map_upper_bound(__isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + return map_bound(map, type, pos, value, 1); +} + +__isl_give isl_set *isl_set_lower_bound(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + return isl_map_lower_bound(set, type, pos, value); +} + +__isl_give isl_set *isl_set_upper_bound(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value) +{ + return isl_map_upper_bound(set, type, pos, value); +} + +/* Force the values of the variable at position "pos" of type "type" of "set" + * to be no smaller than "value". + */ +__isl_give isl_set *isl_set_lower_bound_val(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *value) +{ + if (!value) + goto error; + if (!isl_val_is_int(value)) + isl_die(isl_set_get_ctx(set), isl_error_invalid, + "expecting integer value", goto error); + set = isl_set_lower_bound(set, type, pos, value->n); + isl_val_free(value); + return set; +error: + isl_val_free(value); + isl_set_free(set); + return NULL; +} + +/* Force the values of the variable at position "pos" of type "type" of "set" + * to be no greater than "value". + */ +__isl_give isl_set *isl_set_upper_bound_val(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *value) +{ + if (!value) + goto error; + if (!isl_val_is_int(value)) + isl_die(isl_set_get_ctx(set), isl_error_invalid, + "expecting integer value", goto error); + set = isl_set_upper_bound(set, type, pos, value->n); + isl_val_free(value); + return set; +error: + isl_val_free(value); + isl_set_free(set); + return NULL; +} + +struct isl_set *isl_set_lower_bound_dim(struct isl_set *set, unsigned dim, + isl_int value) +{ + int i; + + set = isl_set_cow(set); + if (!set) + return NULL; + + isl_assert(set->ctx, dim < isl_set_n_dim(set), goto error); + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_lower_bound_dim(set->p[i], dim, value); + if (!set->p[i]) + goto error; + } + return set; +error: + isl_set_free(set); + return NULL; +} + +struct isl_map *isl_map_reverse(struct isl_map *map) +{ + int i; + + map = isl_map_cow(map); + if (!map) + return NULL; + + map->dim = isl_space_reverse(map->dim); + if (!map->dim) + goto error; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_reverse(map->p[i]); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +static struct isl_map *isl_basic_map_partial_lexopt( + struct isl_basic_map *bmap, struct isl_basic_set *dom, + struct isl_set **empty, int max) +{ + return isl_tab_basic_map_partial_lexopt(bmap, dom, empty, max); +} + +struct isl_map *isl_basic_map_partial_lexmax( + struct isl_basic_map *bmap, struct isl_basic_set *dom, + struct isl_set **empty) +{ + return isl_basic_map_partial_lexopt(bmap, dom, empty, 1); +} + +struct isl_map *isl_basic_map_partial_lexmin( + struct isl_basic_map *bmap, struct isl_basic_set *dom, + struct isl_set **empty) +{ + return isl_basic_map_partial_lexopt(bmap, dom, empty, 0); +} + +struct isl_set *isl_basic_set_partial_lexmin( + struct isl_basic_set *bset, struct isl_basic_set *dom, + struct isl_set **empty) +{ + return (struct isl_set *) + isl_basic_map_partial_lexmin((struct isl_basic_map *)bset, + dom, empty); +} + +struct isl_set *isl_basic_set_partial_lexmax( + struct isl_basic_set *bset, struct isl_basic_set *dom, + struct isl_set **empty) +{ + return (struct isl_set *) + isl_basic_map_partial_lexmax((struct isl_basic_map *)bset, + dom, empty); +} + +__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexmin_pw_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty) +{ + return isl_basic_map_partial_lexopt_pw_multi_aff(bmap, dom, empty, 0); +} + +__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexmax_pw_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty) +{ + return isl_basic_map_partial_lexopt_pw_multi_aff(bmap, dom, empty, 1); +} + +__isl_give isl_pw_multi_aff *isl_basic_set_partial_lexmin_pw_multi_aff( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty) +{ + return isl_basic_map_partial_lexmin_pw_multi_aff(bset, dom, empty); +} + +__isl_give isl_pw_multi_aff *isl_basic_set_partial_lexmax_pw_multi_aff( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty) +{ + return isl_basic_map_partial_lexmax_pw_multi_aff(bset, dom, empty); +} + +__isl_give isl_pw_multi_aff *isl_basic_map_lexopt_pw_multi_aff( + __isl_take isl_basic_map *bmap, int max) +{ + isl_basic_set *dom = NULL; + isl_space *dom_space; + + if (!bmap) + goto error; + dom_space = isl_space_domain(isl_space_copy(bmap->dim)); + dom = isl_basic_set_universe(dom_space); + return isl_basic_map_partial_lexopt_pw_multi_aff(bmap, dom, NULL, max); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_pw_multi_aff *isl_basic_map_lexmin_pw_multi_aff( + __isl_take isl_basic_map *bmap) +{ + return isl_basic_map_lexopt_pw_multi_aff(bmap, 0); +} + +#undef TYPE +#define TYPE isl_pw_multi_aff +#undef SUFFIX +#define SUFFIX _pw_multi_aff +#undef EMPTY +#define EMPTY isl_pw_multi_aff_empty +#undef ADD +#define ADD isl_pw_multi_aff_union_add +#include "isl_map_lexopt_templ.c" + +/* Given a map "map", compute the lexicographically minimal + * (or maximal) image element for each domain element in dom, + * in the form of an isl_pw_multi_aff. + * If "empty" is not NULL, then set *empty to those elements in dom that + * do not have an image element. + * + * We first compute the lexicographically minimal or maximal element + * in the first basic map. This results in a partial solution "res" + * and a subset "todo" of dom that still need to be handled. + * We then consider each of the remaining maps in "map" and successively + * update both "res" and "todo". + * If "empty" is NULL, then the todo sets are not needed and therefore + * also not computed. + */ +static __isl_give isl_pw_multi_aff *isl_map_partial_lexopt_aligned_pw_multi_aff( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty, int max) +{ + int i; + isl_pw_multi_aff *res; + isl_set *todo; + + if (!map || !dom) + goto error; + + if (isl_map_plain_is_empty(map)) { + if (empty) + *empty = dom; + else + isl_set_free(dom); + return isl_pw_multi_aff_from_map(map); + } + + res = basic_map_partial_lexopt_pw_multi_aff( + isl_basic_map_copy(map->p[0]), + isl_set_copy(dom), empty, max); + + if (empty) + todo = *empty; + for (i = 1; i < map->n; ++i) { + isl_pw_multi_aff *res_i; + + res_i = basic_map_partial_lexopt_pw_multi_aff( + isl_basic_map_copy(map->p[i]), + isl_set_copy(dom), empty, max); + + if (max) + res = isl_pw_multi_aff_union_lexmax(res, res_i); + else + res = isl_pw_multi_aff_union_lexmin(res, res_i); + + if (empty) + todo = isl_set_intersect(todo, *empty); + } + + isl_set_free(dom); + isl_map_free(map); + + if (empty) + *empty = todo; + + return res; +error: + if (empty) + *empty = NULL; + isl_set_free(dom); + isl_map_free(map); + return NULL; +} + +#undef TYPE +#define TYPE isl_map +#undef SUFFIX +#define SUFFIX +#undef EMPTY +#define EMPTY isl_map_empty +#undef ADD +#define ADD isl_map_union_disjoint +#include "isl_map_lexopt_templ.c" + +/* Given a map "map", compute the lexicographically minimal + * (or maximal) image element for each domain element in "dom", + * in the form of an isl_map. + * If "empty" is not NULL, then set *empty to those elements in "dom" that + * do not have an image element. + * + * If the input consists of more than one disjunct, then first + * compute the desired result in the form of an isl_pw_multi_aff and + * then convert that into an isl_map. + * + * This function used to have an explicit implementation in terms + * of isl_maps, but it would continually intersect the domains of + * partial results with the complement of the domain of the next + * partial solution, potentially leading to an explosion in the number + * of disjuncts if there are several disjuncts in the input. + * An even earlier implementation of this function would look for + * better results in the domain of the partial result and for extra + * results in the complement of this domain, which would lead to + * even more splintering. + */ +static __isl_give isl_map *isl_map_partial_lexopt_aligned( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty, int max) +{ + struct isl_map *res; + isl_pw_multi_aff *pma; + + if (!map || !dom) + goto error; + + if (isl_map_plain_is_empty(map)) { + if (empty) + *empty = dom; + else + isl_set_free(dom); + return map; + } + + if (map->n == 1) { + res = basic_map_partial_lexopt(isl_basic_map_copy(map->p[0]), + dom, empty, max); + isl_map_free(map); + return res; + } + + pma = isl_map_partial_lexopt_aligned_pw_multi_aff(map, dom, empty, max); + return isl_map_from_pw_multi_aff(pma); +error: + if (empty) + *empty = NULL; + isl_set_free(dom); + isl_map_free(map); + return NULL; +} + +__isl_give isl_map *isl_map_partial_lexmax( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty) +{ + return isl_map_partial_lexopt(map, dom, empty, 1); +} + +__isl_give isl_map *isl_map_partial_lexmin( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty) +{ + return isl_map_partial_lexopt(map, dom, empty, 0); +} + +__isl_give isl_set *isl_set_partial_lexmin( + __isl_take isl_set *set, __isl_take isl_set *dom, + __isl_give isl_set **empty) +{ + return (struct isl_set *) + isl_map_partial_lexmin((struct isl_map *)set, + dom, empty); +} + +__isl_give isl_set *isl_set_partial_lexmax( + __isl_take isl_set *set, __isl_take isl_set *dom, + __isl_give isl_set **empty) +{ + return (struct isl_set *) + isl_map_partial_lexmax((struct isl_map *)set, + dom, empty); +} + +/* Compute the lexicographic minimum (or maximum if "max" is set) + * of "bmap" over its domain. + * + * Since we are not interested in the part of the domain space where + * there is no solution, we initialize the domain to those constraints + * of "bmap" that only involve the parameters and the input dimensions. + * This relieves the parametric programming engine from detecting those + * inequalities and transferring them to the context. More importantly, + * it ensures that those inequalities are transferred first and not + * intermixed with inequalities that actually split the domain. + */ +__isl_give isl_map *isl_basic_map_lexopt(__isl_take isl_basic_map *bmap, int max) +{ + int n_div; + int n_out; + isl_basic_map *copy; + isl_basic_set *dom; + + n_div = isl_basic_map_dim(bmap, isl_dim_div); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + copy = isl_basic_map_copy(bmap); + copy = isl_basic_map_drop_constraints_involving_dims(copy, + isl_dim_div, 0, n_div); + copy = isl_basic_map_drop_constraints_involving_dims(copy, + isl_dim_out, 0, n_out); + dom = isl_basic_map_domain(copy); + return isl_basic_map_partial_lexopt(bmap, dom, NULL, max); +} + +__isl_give isl_map *isl_basic_map_lexmin(__isl_take isl_basic_map *bmap) +{ + return isl_basic_map_lexopt(bmap, 0); +} + +__isl_give isl_map *isl_basic_map_lexmax(__isl_take isl_basic_map *bmap) +{ + return isl_basic_map_lexopt(bmap, 1); +} + +__isl_give isl_set *isl_basic_set_lexmin(__isl_take isl_basic_set *bset) +{ + return (isl_set *)isl_basic_map_lexmin((isl_basic_map *)bset); +} + +__isl_give isl_set *isl_basic_set_lexmax(__isl_take isl_basic_set *bset) +{ + return (isl_set *)isl_basic_map_lexmax((isl_basic_map *)bset); +} + +/* Extract the first and only affine expression from list + * and then add it to *pwaff with the given dom. + * This domain is known to be disjoint from other domains + * because of the way isl_basic_map_foreach_lexmax works. + */ +static int update_dim_opt(__isl_take isl_basic_set *dom, + __isl_take isl_aff_list *list, void *user) +{ + isl_ctx *ctx = isl_basic_set_get_ctx(dom); + isl_aff *aff; + isl_pw_aff **pwaff = user; + isl_pw_aff *pwaff_i; + + if (!list) + goto error; + if (isl_aff_list_n_aff(list) != 1) + isl_die(ctx, isl_error_internal, + "expecting single element list", goto error); + + aff = isl_aff_list_get_aff(list, 0); + pwaff_i = isl_pw_aff_alloc(isl_set_from_basic_set(dom), aff); + + *pwaff = isl_pw_aff_add_disjoint(*pwaff, pwaff_i); + + isl_aff_list_free(list); + + return 0; +error: + isl_basic_set_free(dom); + isl_aff_list_free(list); + return -1; +} + +/* Given a basic map with one output dimension, compute the minimum or + * maximum of that dimension as an isl_pw_aff. + * + * The isl_pw_aff is constructed by having isl_basic_map_foreach_lexopt + * call update_dim_opt on each leaf of the result. + */ +static __isl_give isl_pw_aff *basic_map_dim_opt(__isl_keep isl_basic_map *bmap, + int max) +{ + isl_space *dim = isl_basic_map_get_space(bmap); + isl_pw_aff *pwaff; + int r; + + dim = isl_space_from_domain(isl_space_domain(dim)); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + pwaff = isl_pw_aff_empty(dim); + + r = isl_basic_map_foreach_lexopt(bmap, max, &update_dim_opt, &pwaff); + if (r < 0) + return isl_pw_aff_free(pwaff); + + return pwaff; +} + +/* Compute the minimum or maximum of the given output dimension + * as a function of the parameters and the input dimensions, + * but independently of the other output dimensions. + * + * We first project out the other output dimension and then compute + * the "lexicographic" maximum in each basic map, combining the results + * using isl_pw_aff_union_max. + */ +static __isl_give isl_pw_aff *map_dim_opt(__isl_take isl_map *map, int pos, + int max) +{ + int i; + isl_pw_aff *pwaff; + unsigned n_out; + + n_out = isl_map_dim(map, isl_dim_out); + map = isl_map_project_out(map, isl_dim_out, pos + 1, n_out - (pos + 1)); + map = isl_map_project_out(map, isl_dim_out, 0, pos); + if (!map) + return NULL; + + if (map->n == 0) { + isl_space *dim = isl_map_get_space(map); + isl_map_free(map); + return isl_pw_aff_empty(dim); + } + + pwaff = basic_map_dim_opt(map->p[0], max); + for (i = 1; i < map->n; ++i) { + isl_pw_aff *pwaff_i; + + pwaff_i = basic_map_dim_opt(map->p[i], max); + pwaff = isl_pw_aff_union_opt(pwaff, pwaff_i, max); + } + + isl_map_free(map); + + return pwaff; +} + +/* Compute the maximum of the given output dimension as a function of the + * parameters and input dimensions, but independently of + * the other output dimensions. + */ +__isl_give isl_pw_aff *isl_map_dim_max(__isl_take isl_map *map, int pos) +{ + return map_dim_opt(map, pos, 1); +} + +/* Compute the minimum or maximum of the given set dimension + * as a function of the parameters, + * but independently of the other set dimensions. + */ +static __isl_give isl_pw_aff *set_dim_opt(__isl_take isl_set *set, int pos, + int max) +{ + return map_dim_opt(set, pos, max); +} + +/* Compute the maximum of the given set dimension as a function of the + * parameters, but independently of the other set dimensions. + */ +__isl_give isl_pw_aff *isl_set_dim_max(__isl_take isl_set *set, int pos) +{ + return set_dim_opt(set, pos, 1); +} + +/* Compute the minimum of the given set dimension as a function of the + * parameters, but independently of the other set dimensions. + */ +__isl_give isl_pw_aff *isl_set_dim_min(__isl_take isl_set *set, int pos) +{ + return set_dim_opt(set, pos, 0); +} + +/* Apply a preimage specified by "mat" on the parameters of "bset". + * bset is assumed to have only parameters and divs. + */ +static struct isl_basic_set *basic_set_parameter_preimage( + struct isl_basic_set *bset, struct isl_mat *mat) +{ + unsigned nparam; + + if (!bset || !mat) + goto error; + + bset->dim = isl_space_cow(bset->dim); + if (!bset->dim) + goto error; + + nparam = isl_basic_set_dim(bset, isl_dim_param); + + isl_assert(bset->ctx, mat->n_row == 1 + nparam, goto error); + + bset->dim->nparam = 0; + bset->dim->n_out = nparam; + bset = isl_basic_set_preimage(bset, mat); + if (bset) { + bset->dim->nparam = bset->dim->n_out; + bset->dim->n_out = 0; + } + return bset; +error: + isl_mat_free(mat); + isl_basic_set_free(bset); + return NULL; +} + +/* Apply a preimage specified by "mat" on the parameters of "set". + * set is assumed to have only parameters and divs. + */ +static __isl_give isl_set *set_parameter_preimage(__isl_take isl_set *set, + __isl_take isl_mat *mat) +{ + isl_space *space; + unsigned nparam; + + if (!set || !mat) + goto error; + + nparam = isl_set_dim(set, isl_dim_param); + + if (mat->n_row != 1 + nparam) + isl_die(isl_set_get_ctx(set), isl_error_internal, + "unexpected number of rows", goto error); + + space = isl_set_get_space(set); + space = isl_space_move_dims(space, isl_dim_set, 0, + isl_dim_param, 0, nparam); + set = isl_set_reset_space(set, space); + set = isl_set_preimage(set, mat); + nparam = isl_set_dim(set, isl_dim_out); + space = isl_set_get_space(set); + space = isl_space_move_dims(space, isl_dim_param, 0, + isl_dim_out, 0, nparam); + set = isl_set_reset_space(set, space); + return set; +error: + isl_mat_free(mat); + isl_set_free(set); + return NULL; +} + +/* Intersect the basic set "bset" with the affine space specified by the + * equalities in "eq". + */ +static struct isl_basic_set *basic_set_append_equalities( + struct isl_basic_set *bset, struct isl_mat *eq) +{ + int i, k; + unsigned len; + + if (!bset || !eq) + goto error; + + bset = isl_basic_set_extend_space(bset, isl_space_copy(bset->dim), 0, + eq->n_row, 0); + if (!bset) + goto error; + + len = 1 + isl_space_dim(bset->dim, isl_dim_all) + bset->extra; + for (i = 0; i < eq->n_row; ++i) { + k = isl_basic_set_alloc_equality(bset); + if (k < 0) + goto error; + isl_seq_cpy(bset->eq[k], eq->row[i], eq->n_col); + isl_seq_clr(bset->eq[k] + eq->n_col, len - eq->n_col); + } + isl_mat_free(eq); + + bset = isl_basic_set_gauss(bset, NULL); + bset = isl_basic_set_finalize(bset); + + return bset; +error: + isl_mat_free(eq); + isl_basic_set_free(bset); + return NULL; +} + +/* Intersect the set "set" with the affine space specified by the + * equalities in "eq". + */ +static struct isl_set *set_append_equalities(struct isl_set *set, + struct isl_mat *eq) +{ + int i; + + if (!set || !eq) + goto error; + + for (i = 0; i < set->n; ++i) { + set->p[i] = basic_set_append_equalities(set->p[i], + isl_mat_copy(eq)); + if (!set->p[i]) + goto error; + } + isl_mat_free(eq); + return set; +error: + isl_mat_free(eq); + isl_set_free(set); + return NULL; +} + +/* Given a basic set "bset" that only involves parameters and existentially + * quantified variables, return the index of the first equality + * that only involves parameters. If there is no such equality then + * return bset->n_eq. + * + * This function assumes that isl_basic_set_gauss has been called on "bset". + */ +static int first_parameter_equality(__isl_keep isl_basic_set *bset) +{ + int i, j; + unsigned nparam, n_div; + + if (!bset) + return -1; + + nparam = isl_basic_set_dim(bset, isl_dim_param); + n_div = isl_basic_set_dim(bset, isl_dim_div); + + for (i = 0, j = n_div - 1; i < bset->n_eq && j >= 0; --j) { + if (!isl_int_is_zero(bset->eq[i][1 + nparam + j])) + ++i; + } + + return i; +} + +/* Compute an explicit representation for the existentially quantified + * variables in "bset" by computing the "minimal value" of the set + * variables. Since there are no set variables, the computation of + * the minimal value essentially computes an explicit representation + * of the non-empty part(s) of "bset". + * + * The input only involves parameters and existentially quantified variables. + * All equalities among parameters have been removed. + * + * Since the existentially quantified variables in the result are in general + * going to be different from those in the input, we first replace + * them by the minimal number of variables based on their equalities. + * This should simplify the parametric integer programming. + */ +static __isl_give isl_set *base_compute_divs(__isl_take isl_basic_set *bset) +{ + isl_morph *morph1, *morph2; + isl_set *set; + unsigned n; + + if (!bset) + return NULL; + if (bset->n_eq == 0) + return isl_basic_set_lexmin(bset); + + morph1 = isl_basic_set_parameter_compression(bset); + bset = isl_morph_basic_set(isl_morph_copy(morph1), bset); + bset = isl_basic_set_lift(bset); + morph2 = isl_basic_set_variable_compression(bset, isl_dim_set); + bset = isl_morph_basic_set(morph2, bset); + n = isl_basic_set_dim(bset, isl_dim_set); + bset = isl_basic_set_project_out(bset, isl_dim_set, 0, n); + + set = isl_basic_set_lexmin(bset); + + set = isl_morph_set(isl_morph_inverse(morph1), set); + + return set; +} + +/* Project the given basic set onto its parameter domain, possibly introducing + * new, explicit, existential variables in the constraints. + * The input has parameters and (possibly implicit) existential variables. + * The output has the same parameters, but only + * explicit existentially quantified variables. + * + * The actual projection is performed by pip, but pip doesn't seem + * to like equalities very much, so we first remove the equalities + * among the parameters by performing a variable compression on + * the parameters. Afterward, an inverse transformation is performed + * and the equalities among the parameters are inserted back in. + * + * The variable compression on the parameters may uncover additional + * equalities that were only implicit before. We therefore check + * if there are any new parameter equalities in the result and + * if so recurse. The removal of parameter equalities is required + * for the parameter compression performed by base_compute_divs. + */ +static struct isl_set *parameter_compute_divs(struct isl_basic_set *bset) +{ + int i; + struct isl_mat *eq; + struct isl_mat *T, *T2; + struct isl_set *set; + unsigned nparam; + + bset = isl_basic_set_cow(bset); + if (!bset) + return NULL; + + if (bset->n_eq == 0) + return base_compute_divs(bset); + + bset = isl_basic_set_gauss(bset, NULL); + if (!bset) + return NULL; + if (isl_basic_set_plain_is_empty(bset)) + return isl_set_from_basic_set(bset); + + i = first_parameter_equality(bset); + if (i == bset->n_eq) + return base_compute_divs(bset); + + nparam = isl_basic_set_dim(bset, isl_dim_param); + eq = isl_mat_sub_alloc6(bset->ctx, bset->eq, i, bset->n_eq - i, + 0, 1 + nparam); + eq = isl_mat_cow(eq); + T = isl_mat_variable_compression(isl_mat_copy(eq), &T2); + if (T && T->n_col == 0) { + isl_mat_free(T); + isl_mat_free(T2); + isl_mat_free(eq); + bset = isl_basic_set_set_to_empty(bset); + return isl_set_from_basic_set(bset); + } + bset = basic_set_parameter_preimage(bset, T); + + i = first_parameter_equality(bset); + if (!bset) + set = NULL; + else if (i == bset->n_eq) + set = base_compute_divs(bset); + else + set = parameter_compute_divs(bset); + set = set_parameter_preimage(set, T2); + set = set_append_equalities(set, eq); + return set; +} + +/* Insert the divs from "ls" before those of "bmap". + * + * The number of columns is not changed, which means that the last + * dimensions of "bmap" are being reintepreted as the divs from "ls". + * The caller is responsible for removing the same number of dimensions + * from the space of "bmap". + */ +static __isl_give isl_basic_map *insert_divs_from_local_space( + __isl_take isl_basic_map *bmap, __isl_keep isl_local_space *ls) +{ + int i; + int n_div; + int old_n_div; + + n_div = isl_local_space_dim(ls, isl_dim_div); + if (n_div == 0) + return bmap; + + old_n_div = bmap->n_div; + bmap = insert_div_rows(bmap, n_div); + if (!bmap) + return NULL; + + for (i = 0; i < n_div; ++i) { + isl_seq_cpy(bmap->div[i], ls->div->row[i], ls->div->n_col); + isl_seq_clr(bmap->div[i] + ls->div->n_col, old_n_div); + } + + return bmap; +} + +/* Replace the space of "bmap" by the space and divs of "ls". + * + * If "ls" has any divs, then we simplify the result since we may + * have discovered some additional equalities that could simplify + * the div expressions. + */ +static __isl_give isl_basic_map *basic_replace_space_by_local_space( + __isl_take isl_basic_map *bmap, __isl_take isl_local_space *ls) +{ + int n_div; + + bmap = isl_basic_map_cow(bmap); + if (!bmap || !ls) + goto error; + + n_div = isl_local_space_dim(ls, isl_dim_div); + bmap = insert_divs_from_local_space(bmap, ls); + if (!bmap) + goto error; + + isl_space_free(bmap->dim); + bmap->dim = isl_local_space_get_space(ls); + if (!bmap->dim) + goto error; + + isl_local_space_free(ls); + if (n_div > 0) + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_basic_map_free(bmap); + isl_local_space_free(ls); + return NULL; +} + +/* Replace the space of "map" by the space and divs of "ls". + */ +static __isl_give isl_map *replace_space_by_local_space(__isl_take isl_map *map, + __isl_take isl_local_space *ls) +{ + int i; + + map = isl_map_cow(map); + if (!map || !ls) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = basic_replace_space_by_local_space(map->p[i], + isl_local_space_copy(ls)); + if (!map->p[i]) + goto error; + } + isl_space_free(map->dim); + map->dim = isl_local_space_get_space(ls); + if (!map->dim) + goto error; + + isl_local_space_free(ls); + return map; +error: + isl_local_space_free(ls); + isl_map_free(map); + return NULL; +} + +/* Compute an explicit representation for the existentially + * quantified variables for which do not know any explicit representation yet. + * + * We first sort the existentially quantified variables so that the + * existentially quantified variables for which we already have an explicit + * representation are placed before those for which we do not. + * The input dimensions, the output dimensions and the existentially + * quantified variables for which we already have an explicit + * representation are then turned into parameters. + * compute_divs returns a map with the same parameters and + * no input or output dimensions and the dimension specification + * is reset to that of the input, including the existentially quantified + * variables for which we already had an explicit representation. + */ +static struct isl_map *compute_divs(struct isl_basic_map *bmap) +{ + struct isl_basic_set *bset; + struct isl_set *set; + struct isl_map *map; + isl_space *dim; + isl_local_space *ls; + unsigned nparam; + unsigned n_in; + unsigned n_out; + int n_known; + int i; + + bmap = isl_basic_map_sort_divs(bmap); + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + n_known = isl_basic_map_first_unknown_div(bmap); + if (n_known < 0) + return isl_map_from_basic_map(isl_basic_map_free(bmap)); + + nparam = isl_basic_map_dim(bmap, isl_dim_param); + n_in = isl_basic_map_dim(bmap, isl_dim_in); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + dim = isl_space_set_alloc(bmap->ctx, + nparam + n_in + n_out + n_known, 0); + if (!dim) + goto error; + + ls = isl_basic_map_get_local_space(bmap); + ls = isl_local_space_drop_dims(ls, isl_dim_div, + n_known, bmap->n_div - n_known); + if (n_known > 0) { + for (i = n_known; i < bmap->n_div; ++i) + swap_div(bmap, i - n_known, i); + bmap->n_div -= n_known; + bmap->extra -= n_known; + } + bmap = isl_basic_map_reset_space(bmap, dim); + bset = (struct isl_basic_set *)bmap; + + set = parameter_compute_divs(bset); + map = (struct isl_map *)set; + map = replace_space_by_local_space(map, ls); + + return map; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Remove the explicit representation of local variable "div", + * if there is any. + */ +__isl_give isl_basic_map *isl_basic_map_mark_div_unknown( + __isl_take isl_basic_map *bmap, int div) +{ + isl_bool known; + + known = isl_basic_map_div_is_known(bmap, div); + if (known < 0) + return isl_basic_map_free(bmap); + if (!known) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + isl_int_set_si(bmap->div[div][0], 0); + return bmap; +} + +/* Does local variable "div" of "bmap" have an explicit representation? + */ +isl_bool isl_basic_map_div_is_known(__isl_keep isl_basic_map *bmap, int div) +{ + if (!bmap) + return isl_bool_error; + if (div < 0 || div >= isl_basic_map_dim(bmap, isl_dim_div)) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "position out of bounds", return isl_bool_error); + return !isl_int_is_zero(bmap->div[div][0]); +} + +/* Return the position of the first local variable that does not + * have an explicit representation. + * Return the total number of local variables if they all have + * an explicit representation. + * Return -1 on error. + */ +int isl_basic_map_first_unknown_div(__isl_keep isl_basic_map *bmap) +{ + int i; + + if (!bmap) + return -1; + + for (i = 0; i < bmap->n_div; ++i) { + if (!isl_basic_map_div_is_known(bmap, i)) + return i; + } + return bmap->n_div; +} + +/* Does "bmap" have an explicit representation for all local variables? + */ +isl_bool isl_basic_map_divs_known(__isl_keep isl_basic_map *bmap) +{ + int first, n; + + n = isl_basic_map_dim(bmap, isl_dim_div); + first = isl_basic_map_first_unknown_div(bmap); + if (first < 0) + return isl_bool_error; + return first == n; +} + +/* Do all basic maps in "map" have an explicit representation + * for all local variables? + */ +isl_bool isl_map_divs_known(__isl_keep isl_map *map) +{ + int i; + + if (!map) + return isl_bool_error; + + for (i = 0; i < map->n; ++i) { + int known = isl_basic_map_divs_known(map->p[i]); + if (known <= 0) + return known; + } + + return isl_bool_true; +} + +/* If bmap contains any unknown divs, then compute explicit + * expressions for them. However, this computation may be + * quite expensive, so first try to remove divs that aren't + * strictly needed. + */ +struct isl_map *isl_basic_map_compute_divs(struct isl_basic_map *bmap) +{ + int known; + struct isl_map *map; + + known = isl_basic_map_divs_known(bmap); + if (known < 0) + goto error; + if (known) + return isl_map_from_basic_map(bmap); + + bmap = isl_basic_map_drop_redundant_divs(bmap); + + known = isl_basic_map_divs_known(bmap); + if (known < 0) + goto error; + if (known) + return isl_map_from_basic_map(bmap); + + map = compute_divs(bmap); + return map; +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_map *isl_map_compute_divs(struct isl_map *map) +{ + int i; + int known; + struct isl_map *res; + + if (!map) + return NULL; + if (map->n == 0) + return map; + + known = isl_map_divs_known(map); + if (known < 0) { + isl_map_free(map); + return NULL; + } + if (known) + return map; + + res = isl_basic_map_compute_divs(isl_basic_map_copy(map->p[0])); + for (i = 1 ; i < map->n; ++i) { + struct isl_map *r2; + r2 = isl_basic_map_compute_divs(isl_basic_map_copy(map->p[i])); + if (ISL_F_ISSET(map, ISL_MAP_DISJOINT)) + res = isl_map_union_disjoint(res, r2); + else + res = isl_map_union(res, r2); + } + isl_map_free(map); + + return res; +} + +struct isl_set *isl_basic_set_compute_divs(struct isl_basic_set *bset) +{ + return (struct isl_set *) + isl_basic_map_compute_divs((struct isl_basic_map *)bset); +} + +struct isl_set *isl_set_compute_divs(struct isl_set *set) +{ + return (struct isl_set *) + isl_map_compute_divs((struct isl_map *)set); +} + +struct isl_set *isl_map_domain(struct isl_map *map) +{ + int i; + struct isl_set *set; + + if (!map) + goto error; + + map = isl_map_cow(map); + if (!map) + return NULL; + + set = (struct isl_set *)map; + set->dim = isl_space_domain(set->dim); + if (!set->dim) + goto error; + for (i = 0; i < map->n; ++i) { + set->p[i] = isl_basic_map_domain(map->p[i]); + if (!set->p[i]) + goto error; + } + ISL_F_CLR(set, ISL_MAP_DISJOINT); + ISL_F_CLR(set, ISL_SET_NORMALIZED); + return set; +error: + isl_map_free(map); + return NULL; +} + +/* Return the union of "map1" and "map2", where we assume for now that + * "map1" and "map2" are disjoint. Note that the basic maps inside + * "map1" or "map2" may not be disjoint from each other. + * Also note that this function is also called from isl_map_union, + * which takes care of handling the situation where "map1" and "map2" + * may not be disjoint. + * + * If one of the inputs is empty, we can simply return the other input. + * Similarly, if one of the inputs is universal, then it is equal to the union. + */ +static __isl_give isl_map *map_union_disjoint(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + int i; + unsigned flags = 0; + struct isl_map *map = NULL; + int is_universe; + + if (!map1 || !map2) + goto error; + + if (!isl_space_is_equal(map1->dim, map2->dim)) + isl_die(isl_map_get_ctx(map1), isl_error_invalid, + "spaces don't match", goto error); + + if (map1->n == 0) { + isl_map_free(map1); + return map2; + } + if (map2->n == 0) { + isl_map_free(map2); + return map1; + } + + is_universe = isl_map_plain_is_universe(map1); + if (is_universe < 0) + goto error; + if (is_universe) { + isl_map_free(map2); + return map1; + } + + is_universe = isl_map_plain_is_universe(map2); + if (is_universe < 0) + goto error; + if (is_universe) { + isl_map_free(map1); + return map2; + } + + if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT) && + ISL_F_ISSET(map2, ISL_MAP_DISJOINT)) + ISL_FL_SET(flags, ISL_MAP_DISJOINT); + + map = isl_map_alloc_space(isl_space_copy(map1->dim), + map1->n + map2->n, flags); + if (!map) + goto error; + for (i = 0; i < map1->n; ++i) { + map = isl_map_add_basic_map(map, + isl_basic_map_copy(map1->p[i])); + if (!map) + goto error; + } + for (i = 0; i < map2->n; ++i) { + map = isl_map_add_basic_map(map, + isl_basic_map_copy(map2->p[i])); + if (!map) + goto error; + } + isl_map_free(map1); + isl_map_free(map2); + return map; +error: + isl_map_free(map); + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +/* Return the union of "map1" and "map2", where "map1" and "map2" are + * guaranteed to be disjoint by the caller. + * + * Note that this functions is called from within isl_map_make_disjoint, + * so we have to be careful not to touch the constraints of the inputs + * in any way. + */ +__isl_give isl_map *isl_map_union_disjoint(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_union_disjoint); +} + +/* Return the union of "map1" and "map2", where "map1" and "map2" may + * not be disjoint. The parameters are assumed to have been aligned. + * + * We currently simply call map_union_disjoint, the internal operation + * of which does not really depend on the inputs being disjoint. + * If the result contains more than one basic map, then we clear + * the disjoint flag since the result may contain basic maps from + * both inputs and these are not guaranteed to be disjoint. + * + * As a special case, if "map1" and "map2" are obviously equal, + * then we simply return "map1". + */ +static __isl_give isl_map *map_union_aligned(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + int equal; + + if (!map1 || !map2) + goto error; + + equal = isl_map_plain_is_equal(map1, map2); + if (equal < 0) + goto error; + if (equal) { + isl_map_free(map2); + return map1; + } + + map1 = map_union_disjoint(map1, map2); + if (!map1) + return NULL; + if (map1->n > 1) + ISL_F_CLR(map1, ISL_MAP_DISJOINT); + return map1; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +/* Return the union of "map1" and "map2", where "map1" and "map2" may + * not be disjoint. + */ +__isl_give isl_map *isl_map_union(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_union_aligned); +} + +struct isl_set *isl_set_union_disjoint( + struct isl_set *set1, struct isl_set *set2) +{ + return (struct isl_set *) + isl_map_union_disjoint( + (struct isl_map *)set1, (struct isl_map *)set2); +} + +struct isl_set *isl_set_union(struct isl_set *set1, struct isl_set *set2) +{ + return (struct isl_set *) + isl_map_union((struct isl_map *)set1, (struct isl_map *)set2); +} + +/* Apply "fn" to pairs of elements from "map" and "set" and collect + * the results. + * + * "map" and "set" are assumed to be compatible and non-NULL. + */ +static __isl_give isl_map *map_intersect_set(__isl_take isl_map *map, + __isl_take isl_set *set, + __isl_give isl_basic_map *fn(__isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *bset)) +{ + unsigned flags = 0; + struct isl_map *result; + int i, j; + + if (isl_set_plain_is_universe(set)) { + isl_set_free(set); + return map; + } + + if (ISL_F_ISSET(map, ISL_MAP_DISJOINT) && + ISL_F_ISSET(set, ISL_MAP_DISJOINT)) + ISL_FL_SET(flags, ISL_MAP_DISJOINT); + + result = isl_map_alloc_space(isl_space_copy(map->dim), + map->n * set->n, flags); + for (i = 0; result && i < map->n; ++i) + for (j = 0; j < set->n; ++j) { + result = isl_map_add_basic_map(result, + fn(isl_basic_map_copy(map->p[i]), + isl_basic_set_copy(set->p[j]))); + if (!result) + break; + } + + isl_map_free(map); + isl_set_free(set); + return result; +} + +static __isl_give isl_map *map_intersect_range(__isl_take isl_map *map, + __isl_take isl_set *set) +{ + if (!map || !set) + goto error; + + if (!isl_map_compatible_range(map, set)) + isl_die(set->ctx, isl_error_invalid, + "incompatible spaces", goto error); + + return map_intersect_set(map, set, &isl_basic_map_intersect_range); +error: + isl_map_free(map); + isl_set_free(set); + return NULL; +} + +__isl_give isl_map *isl_map_intersect_range(__isl_take isl_map *map, + __isl_take isl_set *set) +{ + return isl_map_align_params_map_map_and(map, set, &map_intersect_range); +} + +static __isl_give isl_map *map_intersect_domain(__isl_take isl_map *map, + __isl_take isl_set *set) +{ + if (!map || !set) + goto error; + + if (!isl_map_compatible_domain(map, set)) + isl_die(set->ctx, isl_error_invalid, + "incompatible spaces", goto error); + + return map_intersect_set(map, set, &isl_basic_map_intersect_domain); +error: + isl_map_free(map); + isl_set_free(set); + return NULL; +} + +__isl_give isl_map *isl_map_intersect_domain(__isl_take isl_map *map, + __isl_take isl_set *set) +{ + return isl_map_align_params_map_map_and(map, set, + &map_intersect_domain); +} + +static __isl_give isl_map *map_apply_domain(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + if (!map1 || !map2) + goto error; + map1 = isl_map_reverse(map1); + map1 = isl_map_apply_range(map1, map2); + return isl_map_reverse(map1); +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +__isl_give isl_map *isl_map_apply_domain(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_apply_domain); +} + +static __isl_give isl_map *map_apply_range(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_space *dim_result; + struct isl_map *result; + int i, j; + + if (!map1 || !map2) + goto error; + + dim_result = isl_space_join(isl_space_copy(map1->dim), + isl_space_copy(map2->dim)); + + result = isl_map_alloc_space(dim_result, map1->n * map2->n, 0); + if (!result) + goto error; + for (i = 0; i < map1->n; ++i) + for (j = 0; j < map2->n; ++j) { + result = isl_map_add_basic_map(result, + isl_basic_map_apply_range( + isl_basic_map_copy(map1->p[i]), + isl_basic_map_copy(map2->p[j]))); + if (!result) + goto error; + } + isl_map_free(map1); + isl_map_free(map2); + if (result && result->n <= 1) + ISL_F_SET(result, ISL_MAP_DISJOINT); + return result; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +__isl_give isl_map *isl_map_apply_range(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_apply_range); +} + +/* + * returns range - domain + */ +struct isl_basic_set *isl_basic_map_deltas(struct isl_basic_map *bmap) +{ + isl_space *target_space; + struct isl_basic_set *bset; + unsigned dim; + unsigned nparam; + int i; + + if (!bmap) + goto error; + isl_assert(bmap->ctx, isl_space_tuple_is_equal(bmap->dim, isl_dim_in, + bmap->dim, isl_dim_out), + goto error); + target_space = isl_space_domain(isl_basic_map_get_space(bmap)); + dim = isl_basic_map_n_in(bmap); + nparam = isl_basic_map_n_param(bmap); + bmap = isl_basic_map_from_range(isl_basic_map_wrap(bmap)); + bmap = isl_basic_map_add_dims(bmap, isl_dim_in, dim); + bmap = isl_basic_map_extend_constraints(bmap, dim, 0); + for (i = 0; i < dim; ++i) { + int j = isl_basic_map_alloc_equality(bmap); + if (j < 0) { + bmap = isl_basic_map_free(bmap); + break; + } + isl_seq_clr(bmap->eq[j], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[j][1+nparam+i], 1); + isl_int_set_si(bmap->eq[j][1+nparam+dim+i], 1); + isl_int_set_si(bmap->eq[j][1+nparam+2*dim+i], -1); + } + bset = isl_basic_map_domain(bmap); + bset = isl_basic_set_reset_space(bset, target_space); + return bset; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* + * returns range - domain + */ +__isl_give isl_set *isl_map_deltas(__isl_take isl_map *map) +{ + int i; + isl_space *dim; + struct isl_set *result; + + if (!map) + return NULL; + + isl_assert(map->ctx, isl_space_tuple_is_equal(map->dim, isl_dim_in, + map->dim, isl_dim_out), + goto error); + dim = isl_map_get_space(map); + dim = isl_space_domain(dim); + result = isl_set_alloc_space(dim, map->n, 0); + if (!result) + goto error; + for (i = 0; i < map->n; ++i) + result = isl_set_add_basic_set(result, + isl_basic_map_deltas(isl_basic_map_copy(map->p[i]))); + isl_map_free(map); + return result; +error: + isl_map_free(map); + return NULL; +} + +/* + * returns [domain -> range] -> range - domain + */ +__isl_give isl_basic_map *isl_basic_map_deltas_map( + __isl_take isl_basic_map *bmap) +{ + int i, k; + isl_space *dim; + isl_basic_map *domain; + int nparam, n; + unsigned total; + + if (!isl_space_tuple_is_equal(bmap->dim, isl_dim_in, + bmap->dim, isl_dim_out)) + isl_die(bmap->ctx, isl_error_invalid, + "domain and range don't match", goto error); + + nparam = isl_basic_map_dim(bmap, isl_dim_param); + n = isl_basic_map_dim(bmap, isl_dim_in); + + dim = isl_space_from_range(isl_space_domain(isl_basic_map_get_space(bmap))); + domain = isl_basic_map_universe(dim); + + bmap = isl_basic_map_from_domain(isl_basic_map_wrap(bmap)); + bmap = isl_basic_map_apply_range(bmap, domain); + bmap = isl_basic_map_extend_constraints(bmap, n, 0); + + total = isl_basic_map_total_dim(bmap); + + for (i = 0; i < n; ++i) { + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->eq[k], 1 + total); + isl_int_set_si(bmap->eq[k][1 + nparam + i], 1); + isl_int_set_si(bmap->eq[k][1 + nparam + n + i], -1); + isl_int_set_si(bmap->eq[k][1 + nparam + n + n + i], 1); + } + + bmap = isl_basic_map_gauss(bmap, NULL); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* + * returns [domain -> range] -> range - domain + */ +__isl_give isl_map *isl_map_deltas_map(__isl_take isl_map *map) +{ + int i; + isl_space *domain_dim; + + if (!map) + return NULL; + + if (!isl_space_tuple_is_equal(map->dim, isl_dim_in, + map->dim, isl_dim_out)) + isl_die(map->ctx, isl_error_invalid, + "domain and range don't match", goto error); + + map = isl_map_cow(map); + if (!map) + return NULL; + + domain_dim = isl_space_from_range(isl_space_domain(isl_map_get_space(map))); + map->dim = isl_space_from_domain(isl_space_wrap(map->dim)); + map->dim = isl_space_join(map->dim, domain_dim); + if (!map->dim) + goto error; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_deltas_map(map->p[i]); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +static __isl_give isl_basic_map *basic_map_identity(__isl_take isl_space *dims) +{ + struct isl_basic_map *bmap; + unsigned nparam; + unsigned dim; + int i; + + if (!dims) + return NULL; + + nparam = dims->nparam; + dim = dims->n_out; + bmap = isl_basic_map_alloc_space(dims, 0, dim, 0); + if (!bmap) + goto error; + + for (i = 0; i < dim; ++i) { + int j = isl_basic_map_alloc_equality(bmap); + if (j < 0) + goto error; + isl_seq_clr(bmap->eq[j], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[j][1+nparam+i], 1); + isl_int_set_si(bmap->eq[j][1+nparam+dim+i], -1); + } + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_identity(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + if (dim->n_in != dim->n_out) + isl_die(dim->ctx, isl_error_invalid, + "number of input and output dimensions needs to be " + "the same", goto error); + return basic_map_identity(dim); +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_map *isl_map_identity(__isl_take isl_space *dim) +{ + return isl_map_from_basic_map(isl_basic_map_identity(dim)); +} + +__isl_give isl_map *isl_set_identity(__isl_take isl_set *set) +{ + isl_space *dim = isl_set_get_space(set); + isl_map *id; + id = isl_map_identity(isl_space_map_from_set(dim)); + return isl_map_intersect_range(id, set); +} + +/* Construct a basic set with all set dimensions having only non-negative + * values. + */ +__isl_give isl_basic_set *isl_basic_set_positive_orthant( + __isl_take isl_space *space) +{ + int i; + unsigned nparam; + unsigned dim; + struct isl_basic_set *bset; + + if (!space) + return NULL; + nparam = space->nparam; + dim = space->n_out; + bset = isl_basic_set_alloc_space(space, 0, 0, dim); + if (!bset) + return NULL; + for (i = 0; i < dim; ++i) { + int k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_clr(bset->ineq[k], 1 + isl_basic_set_total_dim(bset)); + isl_int_set_si(bset->ineq[k][1 + nparam + i], 1); + } + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Construct the half-space x_pos >= 0. + */ +static __isl_give isl_basic_set *nonneg_halfspace(__isl_take isl_space *dim, + int pos) +{ + int k; + isl_basic_set *nonneg; + + nonneg = isl_basic_set_alloc_space(dim, 0, 0, 1); + k = isl_basic_set_alloc_inequality(nonneg); + if (k < 0) + goto error; + isl_seq_clr(nonneg->ineq[k], 1 + isl_basic_set_total_dim(nonneg)); + isl_int_set_si(nonneg->ineq[k][pos], 1); + + return isl_basic_set_finalize(nonneg); +error: + isl_basic_set_free(nonneg); + return NULL; +} + +/* Construct the half-space x_pos <= -1. + */ +static __isl_give isl_basic_set *neg_halfspace(__isl_take isl_space *dim, int pos) +{ + int k; + isl_basic_set *neg; + + neg = isl_basic_set_alloc_space(dim, 0, 0, 1); + k = isl_basic_set_alloc_inequality(neg); + if (k < 0) + goto error; + isl_seq_clr(neg->ineq[k], 1 + isl_basic_set_total_dim(neg)); + isl_int_set_si(neg->ineq[k][0], -1); + isl_int_set_si(neg->ineq[k][pos], -1); + + return isl_basic_set_finalize(neg); +error: + isl_basic_set_free(neg); + return NULL; +} + +__isl_give isl_set *isl_set_split_dims(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + unsigned offset; + isl_basic_set *nonneg; + isl_basic_set *neg; + + if (!set) + return NULL; + if (n == 0) + return set; + + isl_assert(set->ctx, first + n <= isl_set_dim(set, type), goto error); + + offset = pos(set->dim, type); + for (i = 0; i < n; ++i) { + nonneg = nonneg_halfspace(isl_set_get_space(set), + offset + first + i); + neg = neg_halfspace(isl_set_get_space(set), offset + first + i); + + set = isl_set_intersect(set, isl_basic_set_union(nonneg, neg)); + } + + return set; +error: + isl_set_free(set); + return NULL; +} + +static int foreach_orthant(__isl_take isl_set *set, int *signs, int first, + int len, int (*fn)(__isl_take isl_set *orthant, int *signs, void *user), + void *user) +{ + isl_set *half; + + if (!set) + return -1; + if (isl_set_plain_is_empty(set)) { + isl_set_free(set); + return 0; + } + if (first == len) + return fn(set, signs, user); + + signs[first] = 1; + half = isl_set_from_basic_set(nonneg_halfspace(isl_set_get_space(set), + 1 + first)); + half = isl_set_intersect(half, isl_set_copy(set)); + if (foreach_orthant(half, signs, first + 1, len, fn, user) < 0) + goto error; + + signs[first] = -1; + half = isl_set_from_basic_set(neg_halfspace(isl_set_get_space(set), + 1 + first)); + half = isl_set_intersect(half, set); + return foreach_orthant(half, signs, first + 1, len, fn, user); +error: + isl_set_free(set); + return -1; +} + +/* Call "fn" on the intersections of "set" with each of the orthants + * (except for obviously empty intersections). The orthant is identified + * by the signs array, with each entry having value 1 or -1 according + * to the sign of the corresponding variable. + */ +int isl_set_foreach_orthant(__isl_keep isl_set *set, + int (*fn)(__isl_take isl_set *orthant, int *signs, void *user), + void *user) +{ + unsigned nparam; + unsigned nvar; + int *signs; + int r; + + if (!set) + return -1; + if (isl_set_plain_is_empty(set)) + return 0; + + nparam = isl_set_dim(set, isl_dim_param); + nvar = isl_set_dim(set, isl_dim_set); + + signs = isl_alloc_array(set->ctx, int, nparam + nvar); + + r = foreach_orthant(isl_set_copy(set), signs, 0, nparam + nvar, + fn, user); + + free(signs); + + return r; +} + +isl_bool isl_set_is_equal(__isl_keep isl_set *set1, __isl_keep isl_set *set2) +{ + return isl_map_is_equal((struct isl_map *)set1, (struct isl_map *)set2); +} + +isl_bool isl_basic_map_is_subset(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2) +{ + int is_subset; + struct isl_map *map1; + struct isl_map *map2; + + if (!bmap1 || !bmap2) + return isl_bool_error; + + map1 = isl_map_from_basic_map(isl_basic_map_copy(bmap1)); + map2 = isl_map_from_basic_map(isl_basic_map_copy(bmap2)); + + is_subset = isl_map_is_subset(map1, map2); + + isl_map_free(map1); + isl_map_free(map2); + + return is_subset; +} + +isl_bool isl_basic_set_is_subset(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2) +{ + return isl_basic_map_is_subset(bset1, bset2); +} + +isl_bool isl_basic_map_is_equal(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2) +{ + isl_bool is_subset; + + if (!bmap1 || !bmap2) + return isl_bool_error; + is_subset = isl_basic_map_is_subset(bmap1, bmap2); + if (is_subset != isl_bool_true) + return is_subset; + is_subset = isl_basic_map_is_subset(bmap2, bmap1); + return is_subset; +} + +isl_bool isl_basic_set_is_equal(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2) +{ + return isl_basic_map_is_equal( + (struct isl_basic_map *)bset1, (struct isl_basic_map *)bset2); +} + +isl_bool isl_map_is_empty(__isl_keep isl_map *map) +{ + int i; + int is_empty; + + if (!map) + return isl_bool_error; + for (i = 0; i < map->n; ++i) { + is_empty = isl_basic_map_is_empty(map->p[i]); + if (is_empty < 0) + return isl_bool_error; + if (!is_empty) + return isl_bool_false; + } + return isl_bool_true; +} + +isl_bool isl_map_plain_is_empty(__isl_keep isl_map *map) +{ + return map ? map->n == 0 : isl_bool_error; +} + +isl_bool isl_set_plain_is_empty(__isl_keep isl_set *set) +{ + return set ? set->n == 0 : isl_bool_error; +} + +isl_bool isl_set_is_empty(__isl_keep isl_set *set) +{ + return isl_map_is_empty((struct isl_map *)set); +} + +int isl_map_has_equal_space(__isl_keep isl_map *map1, __isl_keep isl_map *map2) +{ + if (!map1 || !map2) + return -1; + + return isl_space_is_equal(map1->dim, map2->dim); +} + +int isl_set_has_equal_space(__isl_keep isl_set *set1, __isl_keep isl_set *set2) +{ + if (!set1 || !set2) + return -1; + + return isl_space_is_equal(set1->dim, set2->dim); +} + +static isl_bool map_is_equal(__isl_keep isl_map *map1, __isl_keep isl_map *map2) +{ + isl_bool is_subset; + + if (!map1 || !map2) + return isl_bool_error; + is_subset = isl_map_is_subset(map1, map2); + if (is_subset != isl_bool_true) + return is_subset; + is_subset = isl_map_is_subset(map2, map1); + return is_subset; +} + +isl_bool isl_map_is_equal(__isl_keep isl_map *map1, __isl_keep isl_map *map2) +{ + return isl_map_align_params_map_map_and_test(map1, map2, &map_is_equal); +} + +isl_bool isl_basic_map_is_strict_subset( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + isl_bool is_subset; + + if (!bmap1 || !bmap2) + return isl_bool_error; + is_subset = isl_basic_map_is_subset(bmap1, bmap2); + if (is_subset != isl_bool_true) + return is_subset; + is_subset = isl_basic_map_is_subset(bmap2, bmap1); + if (is_subset == isl_bool_error) + return is_subset; + return !is_subset; +} + +isl_bool isl_map_is_strict_subset(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + isl_bool is_subset; + + if (!map1 || !map2) + return isl_bool_error; + is_subset = isl_map_is_subset(map1, map2); + if (is_subset != isl_bool_true) + return is_subset; + is_subset = isl_map_is_subset(map2, map1); + if (is_subset == isl_bool_error) + return is_subset; + return !is_subset; +} + +isl_bool isl_set_is_strict_subset(__isl_keep isl_set *set1, + __isl_keep isl_set *set2) +{ + return isl_map_is_strict_subset((isl_map *)set1, (isl_map *)set2); +} + +/* Is "bmap" obviously equal to the universe with the same space? + * + * That is, does it not have any constraints? + */ +isl_bool isl_basic_map_plain_is_universe(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return isl_bool_error; + return bmap->n_eq == 0 && bmap->n_ineq == 0; +} + +/* Is "bset" obviously equal to the universe with the same space? + */ +isl_bool isl_basic_set_plain_is_universe(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_plain_is_universe(bset); +} + +/* If "c" does not involve any existentially quantified variables, + * then set *univ to false and abort + */ +static isl_stat involves_divs(__isl_take isl_constraint *c, void *user) +{ + isl_bool *univ = user; + unsigned n; + + n = isl_constraint_dim(c, isl_dim_div); + *univ = isl_constraint_involves_dims(c, isl_dim_div, 0, n); + isl_constraint_free(c); + if (*univ < 0 || !*univ) + return isl_stat_error; + return isl_stat_ok; +} + +/* Is "bmap" equal to the universe with the same space? + * + * First check if it is obviously equal to the universe. + * If not and if there are any constraints not involving + * existentially quantified variables, then it is certainly + * not equal to the universe. + * Otherwise, check if the universe is a subset of "bmap". + */ +isl_bool isl_basic_map_is_universe(__isl_keep isl_basic_map *bmap) +{ + isl_bool univ; + isl_basic_map *test; + + univ = isl_basic_map_plain_is_universe(bmap); + if (univ < 0 || univ) + return univ; + if (isl_basic_map_dim(bmap, isl_dim_div) == 0) + return isl_bool_false; + univ = isl_bool_true; + if (isl_basic_map_foreach_constraint(bmap, &involves_divs, &univ) < 0 && + univ) + return isl_bool_error; + if (univ < 0 || !univ) + return univ; + test = isl_basic_map_universe(isl_basic_map_get_space(bmap)); + univ = isl_basic_map_is_subset(test, bmap); + isl_basic_map_free(test); + return univ; +} + +/* Is "bset" equal to the universe with the same space? + */ +isl_bool isl_basic_set_is_universe(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_is_universe(bset); +} + +isl_bool isl_map_plain_is_universe(__isl_keep isl_map *map) +{ + int i; + + if (!map) + return isl_bool_error; + + for (i = 0; i < map->n; ++i) { + isl_bool r = isl_basic_map_plain_is_universe(map->p[i]); + if (r < 0 || r) + return r; + } + + return isl_bool_false; +} + +isl_bool isl_set_plain_is_universe(__isl_keep isl_set *set) +{ + return isl_map_plain_is_universe((isl_map *) set); +} + +isl_bool isl_basic_map_is_empty(__isl_keep isl_basic_map *bmap) +{ + struct isl_basic_set *bset = NULL; + struct isl_vec *sample = NULL; + isl_bool empty, non_empty; + + if (!bmap) + return isl_bool_error; + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + return isl_bool_true; + + if (isl_basic_map_plain_is_universe(bmap)) + return isl_bool_false; + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) { + struct isl_basic_map *copy = isl_basic_map_copy(bmap); + copy = isl_basic_map_remove_redundancies(copy); + empty = isl_basic_map_plain_is_empty(copy); + isl_basic_map_free(copy); + return empty; + } + + non_empty = isl_basic_map_plain_is_non_empty(bmap); + if (non_empty < 0) + return isl_bool_error; + if (non_empty) + return isl_bool_false; + isl_vec_free(bmap->sample); + bmap->sample = NULL; + bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap)); + if (!bset) + return isl_bool_error; + sample = isl_basic_set_sample_vec(bset); + if (!sample) + return isl_bool_error; + empty = sample->size == 0; + isl_vec_free(bmap->sample); + bmap->sample = sample; + if (empty) + ISL_F_SET(bmap, ISL_BASIC_MAP_EMPTY); + + return empty; +} + +isl_bool isl_basic_map_plain_is_empty(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return isl_bool_error; + return ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY); +} + +isl_bool isl_basic_set_plain_is_empty(__isl_keep isl_basic_set *bset) +{ + if (!bset) + return isl_bool_error; + return ISL_F_ISSET(bset, ISL_BASIC_SET_EMPTY); +} + +/* Is "bmap" known to be non-empty? + * + * That is, is the cached sample still valid? + */ +isl_bool isl_basic_map_plain_is_non_empty(__isl_keep isl_basic_map *bmap) +{ + unsigned total; + + if (!bmap) + return isl_bool_error; + if (!bmap->sample) + return isl_bool_false; + total = 1 + isl_basic_map_total_dim(bmap); + if (bmap->sample->size != total) + return isl_bool_false; + return isl_basic_map_contains(bmap, bmap->sample); +} + +isl_bool isl_basic_set_is_empty(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_is_empty((struct isl_basic_map *)bset); +} + +struct isl_map *isl_basic_map_union( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + struct isl_map *map; + if (!bmap1 || !bmap2) + goto error; + + isl_assert(bmap1->ctx, isl_space_is_equal(bmap1->dim, bmap2->dim), goto error); + + map = isl_map_alloc_space(isl_space_copy(bmap1->dim), 2, 0); + if (!map) + goto error; + map = isl_map_add_basic_map(map, bmap1); + map = isl_map_add_basic_map(map, bmap2); + return map; +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +struct isl_set *isl_basic_set_union( + struct isl_basic_set *bset1, struct isl_basic_set *bset2) +{ + return (struct isl_set *)isl_basic_map_union( + (struct isl_basic_map *)bset1, + (struct isl_basic_map *)bset2); +} + +/* Order divs such that any div only depends on previous divs */ +struct isl_basic_map *isl_basic_map_order_divs(struct isl_basic_map *bmap) +{ + int i; + unsigned off; + + if (!bmap) + return NULL; + + off = isl_space_dim(bmap->dim, isl_dim_all); + + for (i = 0; i < bmap->n_div; ++i) { + int pos; + if (isl_int_is_zero(bmap->div[i][0])) + continue; + pos = isl_seq_first_non_zero(bmap->div[i]+1+1+off+i, + bmap->n_div-i); + if (pos == -1) + continue; + if (pos == 0) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_internal, + "integer division depends on itself", + return isl_basic_map_free(bmap)); + isl_basic_map_swap_div(bmap, i, i + pos); + --i; + } + return bmap; +} + +struct isl_basic_set *isl_basic_set_order_divs(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_order_divs((struct isl_basic_map *)bset); +} + +__isl_give isl_map *isl_map_order_divs(__isl_take isl_map *map) +{ + int i; + + if (!map) + return 0; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_order_divs(map->p[i]); + if (!map->p[i]) + goto error; + } + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Apply the expansion computed by isl_merge_divs. + * The expansion itself is given by "exp" while the resulting + * list of divs is given by "div". + * + * Move the integer divisions of "bmap" into the right position + * according to "exp" and then introduce the additional integer + * divisions, adding div constraints. + * The moving should be done first to avoid moving coefficients + * in the definitions of the extra integer divisions. + */ +__isl_give isl_basic_map *isl_basic_map_expand_divs( + __isl_take isl_basic_set *bmap, __isl_take isl_mat *div, int *exp) +{ + int i, j; + int n_div; + + bmap = isl_basic_map_cow(bmap); + if (!bmap || !div) + goto error; + + if (div->n_row < bmap->n_div) + isl_die(isl_mat_get_ctx(div), isl_error_invalid, + "not an expansion", goto error); + + n_div = bmap->n_div; + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), + div->n_row - n_div, 0, + 2 * (div->n_row - n_div)); + + for (i = n_div; i < div->n_row; ++i) + if (isl_basic_map_alloc_div(bmap) < 0) + goto error; + + for (j = n_div - 1; j >= 0; --j) { + if (exp[j] == j) + break; + isl_basic_map_swap_div(bmap, j, exp[j]); + } + j = 0; + for (i = 0; i < div->n_row; ++i) { + if (j < n_div && exp[j] == i) { + j++; + } else { + isl_seq_cpy(bmap->div[i], div->row[i], div->n_col); + if (!isl_basic_map_div_is_known(bmap, i)) + continue; + if (isl_basic_map_add_div_constraints(bmap, i) < 0) + goto error; + } + } + + isl_mat_free(div); + return bmap; +error: + isl_basic_map_free(bmap); + isl_mat_free(div); + return NULL; +} + +/* Apply the expansion computed by isl_merge_divs. + * The expansion itself is given by "exp" while the resulting + * list of divs is given by "div". + */ +__isl_give isl_basic_set *isl_basic_set_expand_divs( + __isl_take isl_basic_set *bset, __isl_take isl_mat *div, int *exp) +{ + return isl_basic_map_expand_divs(bset, div, exp); +} + +/* Look for a div in dst that corresponds to the div "div" in src. + * The divs before "div" in src and dst are assumed to be the same. + * + * Returns -1 if no corresponding div was found and the position + * of the corresponding div in dst otherwise. + */ +static int find_div(struct isl_basic_map *dst, + struct isl_basic_map *src, unsigned div) +{ + int i; + + unsigned total = isl_space_dim(src->dim, isl_dim_all); + + isl_assert(dst->ctx, div <= dst->n_div, return -1); + for (i = div; i < dst->n_div; ++i) + if (isl_seq_eq(dst->div[i], src->div[div], 1+1+total+div) && + isl_seq_first_non_zero(dst->div[i]+1+1+total+div, + dst->n_div - div) == -1) + return i; + return -1; +} + +/* Align the divs of "dst" to those of "src", adding divs from "src" + * if needed. That is, make sure that the first src->n_div divs + * of the result are equal to those of src. + * + * The result is not finalized as by design it will have redundant + * divs if any divs from "src" were copied. + */ +__isl_give isl_basic_map *isl_basic_map_align_divs( + __isl_take isl_basic_map *dst, __isl_keep isl_basic_map *src) +{ + int i; + int known, extended; + unsigned total; + + if (!dst || !src) + return isl_basic_map_free(dst); + + if (src->n_div == 0) + return dst; + + known = isl_basic_map_divs_known(src); + if (known < 0) + return isl_basic_map_free(dst); + if (!known) + isl_die(isl_basic_map_get_ctx(src), isl_error_invalid, + "some src divs are unknown", + return isl_basic_map_free(dst)); + + src = isl_basic_map_order_divs(src); + + extended = 0; + total = isl_space_dim(src->dim, isl_dim_all); + for (i = 0; i < src->n_div; ++i) { + int j = find_div(dst, src, i); + if (j < 0) { + if (!extended) { + int extra = src->n_div - i; + dst = isl_basic_map_cow(dst); + if (!dst) + return NULL; + dst = isl_basic_map_extend_space(dst, + isl_space_copy(dst->dim), + extra, 0, 2 * extra); + extended = 1; + } + j = isl_basic_map_alloc_div(dst); + if (j < 0) + return isl_basic_map_free(dst); + isl_seq_cpy(dst->div[j], src->div[i], 1+1+total+i); + isl_seq_clr(dst->div[j]+1+1+total+i, dst->n_div - i); + if (isl_basic_map_add_div_constraints(dst, j) < 0) + return isl_basic_map_free(dst); + } + if (j != i) + isl_basic_map_swap_div(dst, i, j); + } + return dst; +} + +struct isl_basic_set *isl_basic_set_align_divs( + struct isl_basic_set *dst, struct isl_basic_set *src) +{ + return (struct isl_basic_set *)isl_basic_map_align_divs( + (struct isl_basic_map *)dst, (struct isl_basic_map *)src); +} + +struct isl_map *isl_map_align_divs(struct isl_map *map) +{ + int i; + + if (!map) + return NULL; + if (map->n == 0) + return map; + map = isl_map_compute_divs(map); + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 1; i < map->n; ++i) + map->p[0] = isl_basic_map_align_divs(map->p[0], map->p[i]); + for (i = 1; i < map->n; ++i) { + map->p[i] = isl_basic_map_align_divs(map->p[i], map->p[0]); + if (!map->p[i]) + return isl_map_free(map); + } + + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +} + +struct isl_set *isl_set_align_divs(struct isl_set *set) +{ + return (struct isl_set *)isl_map_align_divs((struct isl_map *)set); +} + +/* Align the divs of the basic maps in "map" to those + * of the basic maps in "list", as well as to the other basic maps in "map". + * The elements in "list" are assumed to have known divs. + */ +__isl_give isl_map *isl_map_align_divs_to_basic_map_list( + __isl_take isl_map *map, __isl_keep isl_basic_map_list *list) +{ + int i, n; + + map = isl_map_compute_divs(map); + map = isl_map_cow(map); + if (!map || !list) + return isl_map_free(map); + if (map->n == 0) + return map; + + n = isl_basic_map_list_n_basic_map(list); + for (i = 0; i < n; ++i) { + isl_basic_map *bmap; + + bmap = isl_basic_map_list_get_basic_map(list, i); + map->p[0] = isl_basic_map_align_divs(map->p[0], bmap); + isl_basic_map_free(bmap); + } + if (!map->p[0]) + return isl_map_free(map); + + return isl_map_align_divs(map); +} + +/* Align the divs of each element of "list" to those of "bmap". + * Both "bmap" and the elements of "list" are assumed to have known divs. + */ +__isl_give isl_basic_map_list *isl_basic_map_list_align_divs_to_basic_map( + __isl_take isl_basic_map_list *list, __isl_keep isl_basic_map *bmap) +{ + int i, n; + + if (!list || !bmap) + return isl_basic_map_list_free(list); + + n = isl_basic_map_list_n_basic_map(list); + for (i = 0; i < n; ++i) { + isl_basic_map *bmap_i; + + bmap_i = isl_basic_map_list_get_basic_map(list, i); + bmap_i = isl_basic_map_align_divs(bmap_i, bmap); + list = isl_basic_map_list_set_basic_map(list, i, bmap_i); + } + + return list; +} + +static __isl_give isl_set *set_apply( __isl_take isl_set *set, + __isl_take isl_map *map) +{ + if (!set || !map) + goto error; + isl_assert(set->ctx, isl_map_compatible_domain(map, set), goto error); + map = isl_map_intersect_domain(map, set); + set = isl_map_range(map); + return set; +error: + isl_set_free(set); + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_apply( __isl_take isl_set *set, + __isl_take isl_map *map) +{ + return isl_map_align_params_map_map_and(set, map, &set_apply); +} + +/* There is no need to cow as removing empty parts doesn't change + * the meaning of the set. + */ +struct isl_map *isl_map_remove_empty_parts(struct isl_map *map) +{ + int i; + + if (!map) + return NULL; + + for (i = map->n - 1; i >= 0; --i) + remove_if_empty(map, i); + + return map; +} + +struct isl_set *isl_set_remove_empty_parts(struct isl_set *set) +{ + return (struct isl_set *) + isl_map_remove_empty_parts((struct isl_map *)set); +} + +struct isl_basic_map *isl_map_copy_basic_map(struct isl_map *map) +{ + struct isl_basic_map *bmap; + if (!map || map->n == 0) + return NULL; + bmap = map->p[map->n-1]; + isl_assert(map->ctx, ISL_F_ISSET(bmap, ISL_BASIC_SET_FINAL), return NULL); + return isl_basic_map_copy(bmap); +} + +struct isl_basic_set *isl_set_copy_basic_set(struct isl_set *set) +{ + return (struct isl_basic_set *) + isl_map_copy_basic_map((struct isl_map *)set); +} + +__isl_give isl_map *isl_map_drop_basic_map(__isl_take isl_map *map, + __isl_keep isl_basic_map *bmap) +{ + int i; + + if (!map || !bmap) + goto error; + for (i = map->n-1; i >= 0; --i) { + if (map->p[i] != bmap) + continue; + map = isl_map_cow(map); + if (!map) + goto error; + isl_basic_map_free(map->p[i]); + if (i != map->n-1) { + ISL_F_CLR(map, ISL_SET_NORMALIZED); + map->p[i] = map->p[map->n-1]; + } + map->n--; + return map; + } + return map; +error: + isl_map_free(map); + return NULL; +} + +struct isl_set *isl_set_drop_basic_set(struct isl_set *set, + struct isl_basic_set *bset) +{ + return (struct isl_set *)isl_map_drop_basic_map((struct isl_map *)set, + (struct isl_basic_map *)bset); +} + +/* Given two basic sets bset1 and bset2, compute the maximal difference + * between the values of dimension pos in bset1 and those in bset2 + * for any common value of the parameters and dimensions preceding pos. + */ +static enum isl_lp_result basic_set_maximal_difference_at( + __isl_keep isl_basic_set *bset1, __isl_keep isl_basic_set *bset2, + int pos, isl_int *opt) +{ + isl_space *dims; + struct isl_basic_map *bmap1 = NULL; + struct isl_basic_map *bmap2 = NULL; + struct isl_ctx *ctx; + struct isl_vec *obj; + unsigned total; + unsigned nparam; + unsigned dim1, dim2; + enum isl_lp_result res; + + if (!bset1 || !bset2) + return isl_lp_error; + + nparam = isl_basic_set_n_param(bset1); + dim1 = isl_basic_set_n_dim(bset1); + dim2 = isl_basic_set_n_dim(bset2); + dims = isl_space_alloc(bset1->ctx, nparam, pos, dim1 - pos); + bmap1 = isl_basic_map_from_basic_set(isl_basic_set_copy(bset1), dims); + dims = isl_space_alloc(bset2->ctx, nparam, pos, dim2 - pos); + bmap2 = isl_basic_map_from_basic_set(isl_basic_set_copy(bset2), dims); + if (!bmap1 || !bmap2) + goto error; + bmap1 = isl_basic_map_cow(bmap1); + bmap1 = isl_basic_map_extend(bmap1, nparam, + pos, (dim1 - pos) + (dim2 - pos), + bmap2->n_div, bmap2->n_eq, bmap2->n_ineq); + bmap1 = add_constraints(bmap1, bmap2, 0, dim1 - pos); + if (!bmap1) + goto error2; + total = isl_basic_map_total_dim(bmap1); + ctx = bmap1->ctx; + obj = isl_vec_alloc(ctx, 1 + total); + if (!obj) + goto error2; + isl_seq_clr(obj->block.data, 1 + total); + isl_int_set_si(obj->block.data[1+nparam+pos], 1); + isl_int_set_si(obj->block.data[1+nparam+pos+(dim1-pos)], -1); + res = isl_basic_map_solve_lp(bmap1, 1, obj->block.data, ctx->one, + opt, NULL, NULL); + isl_basic_map_free(bmap1); + isl_vec_free(obj); + return res; +error: + isl_basic_map_free(bmap2); +error2: + isl_basic_map_free(bmap1); + return isl_lp_error; +} + +/* Given two _disjoint_ basic sets bset1 and bset2, check whether + * for any common value of the parameters and dimensions preceding pos + * in both basic sets, the values of dimension pos in bset1 are + * smaller or larger than those in bset2. + * + * Returns + * 1 if bset1 follows bset2 + * -1 if bset1 precedes bset2 + * 0 if bset1 and bset2 are incomparable + * -2 if some error occurred. + */ +int isl_basic_set_compare_at(struct isl_basic_set *bset1, + struct isl_basic_set *bset2, int pos) +{ + isl_int opt; + enum isl_lp_result res; + int cmp; + + isl_int_init(opt); + + res = basic_set_maximal_difference_at(bset1, bset2, pos, &opt); + + if (res == isl_lp_empty) + cmp = 0; + else if ((res == isl_lp_ok && isl_int_is_pos(opt)) || + res == isl_lp_unbounded) + cmp = 1; + else if (res == isl_lp_ok && isl_int_is_neg(opt)) + cmp = -1; + else + cmp = -2; + + isl_int_clear(opt); + return cmp; +} + +/* Given two basic sets bset1 and bset2, check whether + * for any common value of the parameters and dimensions preceding pos + * there is a value of dimension pos in bset1 that is larger + * than a value of the same dimension in bset2. + * + * Return + * 1 if there exists such a pair + * 0 if there is no such pair, but there is a pair of equal values + * -1 otherwise + * -2 if some error occurred. + */ +int isl_basic_set_follows_at(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2, int pos) +{ + isl_int opt; + enum isl_lp_result res; + int cmp; + + isl_int_init(opt); + + res = basic_set_maximal_difference_at(bset1, bset2, pos, &opt); + + if (res == isl_lp_empty) + cmp = -1; + else if ((res == isl_lp_ok && isl_int_is_pos(opt)) || + res == isl_lp_unbounded) + cmp = 1; + else if (res == isl_lp_ok && isl_int_is_neg(opt)) + cmp = -1; + else if (res == isl_lp_ok) + cmp = 0; + else + cmp = -2; + + isl_int_clear(opt); + return cmp; +} + +/* Given two sets set1 and set2, check whether + * for any common value of the parameters and dimensions preceding pos + * there is a value of dimension pos in set1 that is larger + * than a value of the same dimension in set2. + * + * Return + * 1 if there exists such a pair + * 0 if there is no such pair, but there is a pair of equal values + * -1 otherwise + * -2 if some error occurred. + */ +int isl_set_follows_at(__isl_keep isl_set *set1, + __isl_keep isl_set *set2, int pos) +{ + int i, j; + int follows = -1; + + if (!set1 || !set2) + return -2; + + for (i = 0; i < set1->n; ++i) + for (j = 0; j < set2->n; ++j) { + int f; + f = isl_basic_set_follows_at(set1->p[i], set2->p[j], pos); + if (f == 1 || f == -2) + return f; + if (f > follows) + follows = f; + } + + return follows; +} + +static int isl_basic_map_plain_has_fixed_var(__isl_keep isl_basic_map *bmap, + unsigned pos, isl_int *val) +{ + int i; + int d; + unsigned total; + + if (!bmap) + return -1; + total = isl_basic_map_total_dim(bmap); + for (i = 0, d = total-1; i < bmap->n_eq && d+1 > pos; ++i) { + for (; d+1 > pos; --d) + if (!isl_int_is_zero(bmap->eq[i][1+d])) + break; + if (d != pos) + continue; + if (isl_seq_first_non_zero(bmap->eq[i]+1, d) != -1) + return 0; + if (isl_seq_first_non_zero(bmap->eq[i]+1+d+1, total-d-1) != -1) + return 0; + if (!isl_int_is_one(bmap->eq[i][1+d])) + return 0; + if (val) + isl_int_neg(*val, bmap->eq[i][0]); + return 1; + } + return 0; +} + +static int isl_map_plain_has_fixed_var(__isl_keep isl_map *map, + unsigned pos, isl_int *val) +{ + int i; + isl_int v; + isl_int tmp; + int fixed; + + if (!map) + return -1; + if (map->n == 0) + return 0; + if (map->n == 1) + return isl_basic_map_plain_has_fixed_var(map->p[0], pos, val); + isl_int_init(v); + isl_int_init(tmp); + fixed = isl_basic_map_plain_has_fixed_var(map->p[0], pos, &v); + for (i = 1; fixed == 1 && i < map->n; ++i) { + fixed = isl_basic_map_plain_has_fixed_var(map->p[i], pos, &tmp); + if (fixed == 1 && isl_int_ne(tmp, v)) + fixed = 0; + } + if (val) + isl_int_set(*val, v); + isl_int_clear(tmp); + isl_int_clear(v); + return fixed; +} + +static int isl_basic_set_plain_has_fixed_var(__isl_keep isl_basic_set *bset, + unsigned pos, isl_int *val) +{ + return isl_basic_map_plain_has_fixed_var((struct isl_basic_map *)bset, + pos, val); +} + +static int isl_set_plain_has_fixed_var(__isl_keep isl_set *set, unsigned pos, + isl_int *val) +{ + return isl_map_plain_has_fixed_var((struct isl_map *)set, pos, val); +} + +int isl_basic_map_plain_is_fixed(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, isl_int *val) +{ + if (pos >= isl_basic_map_dim(bmap, type)) + return -1; + return isl_basic_map_plain_has_fixed_var(bmap, + isl_basic_map_offset(bmap, type) - 1 + pos, val); +} + +/* If "bmap" obviously lies on a hyperplane where the given dimension + * has a fixed value, then return that value. + * Otherwise return NaN. + */ +__isl_give isl_val *isl_basic_map_plain_get_val_if_fixed( + __isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos) +{ + isl_ctx *ctx; + isl_val *v; + int fixed; + + if (!bmap) + return NULL; + ctx = isl_basic_map_get_ctx(bmap); + v = isl_val_alloc(ctx); + if (!v) + return NULL; + fixed = isl_basic_map_plain_is_fixed(bmap, type, pos, &v->n); + if (fixed < 0) + return isl_val_free(v); + if (fixed) { + isl_int_set_si(v->d, 1); + return v; + } + isl_val_free(v); + return isl_val_nan(ctx); +} + +int isl_map_plain_is_fixed(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int *val) +{ + if (pos >= isl_map_dim(map, type)) + return -1; + return isl_map_plain_has_fixed_var(map, + map_offset(map, type) - 1 + pos, val); +} + +/* If "map" obviously lies on a hyperplane where the given dimension + * has a fixed value, then return that value. + * Otherwise return NaN. + */ +__isl_give isl_val *isl_map_plain_get_val_if_fixed(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos) +{ + isl_ctx *ctx; + isl_val *v; + int fixed; + + if (!map) + return NULL; + ctx = isl_map_get_ctx(map); + v = isl_val_alloc(ctx); + if (!v) + return NULL; + fixed = isl_map_plain_is_fixed(map, type, pos, &v->n); + if (fixed < 0) + return isl_val_free(v); + if (fixed) { + isl_int_set_si(v->d, 1); + return v; + } + isl_val_free(v); + return isl_val_nan(ctx); +} + +/* If "set" obviously lies on a hyperplane where the given dimension + * has a fixed value, then return that value. + * Otherwise return NaN. + */ +__isl_give isl_val *isl_set_plain_get_val_if_fixed(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return isl_map_plain_get_val_if_fixed(set, type, pos); +} + +int isl_set_plain_is_fixed(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int *val) +{ + return isl_map_plain_is_fixed(set, type, pos, val); +} + +/* Check if dimension dim has fixed value and if so and if val is not NULL, + * then return this fixed value in *val. + */ +int isl_basic_set_plain_dim_is_fixed(__isl_keep isl_basic_set *bset, + unsigned dim, isl_int *val) +{ + return isl_basic_set_plain_has_fixed_var(bset, + isl_basic_set_n_param(bset) + dim, val); +} + +/* Check if dimension dim has fixed value and if so and if val is not NULL, + * then return this fixed value in *val. + */ +int isl_set_plain_dim_is_fixed(__isl_keep isl_set *set, + unsigned dim, isl_int *val) +{ + return isl_set_plain_has_fixed_var(set, isl_set_n_param(set) + dim, val); +} + +/* Check if input variable in has fixed value and if so and if val is not NULL, + * then return this fixed value in *val. + */ +int isl_map_plain_input_is_fixed(__isl_keep isl_map *map, + unsigned in, isl_int *val) +{ + return isl_map_plain_has_fixed_var(map, isl_map_n_param(map) + in, val); +} + +/* Check if dimension dim has an (obvious) fixed lower bound and if so + * and if val is not NULL, then return this lower bound in *val. + */ +int isl_basic_set_plain_dim_has_fixed_lower_bound( + __isl_keep isl_basic_set *bset, unsigned dim, isl_int *val) +{ + int i, i_eq = -1, i_ineq = -1; + isl_int *c; + unsigned total; + unsigned nparam; + + if (!bset) + return -1; + total = isl_basic_set_total_dim(bset); + nparam = isl_basic_set_n_param(bset); + for (i = 0; i < bset->n_eq; ++i) { + if (isl_int_is_zero(bset->eq[i][1+nparam+dim])) + continue; + if (i_eq != -1) + return 0; + i_eq = i; + } + for (i = 0; i < bset->n_ineq; ++i) { + if (!isl_int_is_pos(bset->ineq[i][1+nparam+dim])) + continue; + if (i_eq != -1 || i_ineq != -1) + return 0; + i_ineq = i; + } + if (i_eq == -1 && i_ineq == -1) + return 0; + c = i_eq != -1 ? bset->eq[i_eq] : bset->ineq[i_ineq]; + /* The coefficient should always be one due to normalization. */ + if (!isl_int_is_one(c[1+nparam+dim])) + return 0; + if (isl_seq_first_non_zero(c+1, nparam+dim) != -1) + return 0; + if (isl_seq_first_non_zero(c+1+nparam+dim+1, + total - nparam - dim - 1) != -1) + return 0; + if (val) + isl_int_neg(*val, c[0]); + return 1; +} + +int isl_set_plain_dim_has_fixed_lower_bound(__isl_keep isl_set *set, + unsigned dim, isl_int *val) +{ + int i; + isl_int v; + isl_int tmp; + int fixed; + + if (!set) + return -1; + if (set->n == 0) + return 0; + if (set->n == 1) + return isl_basic_set_plain_dim_has_fixed_lower_bound(set->p[0], + dim, val); + isl_int_init(v); + isl_int_init(tmp); + fixed = isl_basic_set_plain_dim_has_fixed_lower_bound(set->p[0], + dim, &v); + for (i = 1; fixed == 1 && i < set->n; ++i) { + fixed = isl_basic_set_plain_dim_has_fixed_lower_bound(set->p[i], + dim, &tmp); + if (fixed == 1 && isl_int_ne(tmp, v)) + fixed = 0; + } + if (val) + isl_int_set(*val, v); + isl_int_clear(tmp); + isl_int_clear(v); + return fixed; +} + +/* Return -1 if the constraint "c1" should be sorted before "c2" + * and 1 if it should be sorted after "c2". + * Return 0 if the two constraints are the same (up to the constant term). + * + * In particular, if a constraint involves later variables than another + * then it is sorted after this other constraint. + * uset_gist depends on constraints without existentially quantified + * variables sorting first. + * + * For constraints that have the same latest variable, those + * with the same coefficient for this latest variable (first in absolute value + * and then in actual value) are grouped together. + * This is useful for detecting pairs of constraints that can + * be chained in their printed representation. + * + * Finally, within a group, constraints are sorted according to + * their coefficients (excluding the constant term). + */ +static int sort_constraint_cmp(const void *p1, const void *p2, void *arg) +{ + isl_int **c1 = (isl_int **) p1; + isl_int **c2 = (isl_int **) p2; + int l1, l2; + unsigned size = *(unsigned *) arg; + int cmp; + + l1 = isl_seq_last_non_zero(*c1 + 1, size); + l2 = isl_seq_last_non_zero(*c2 + 1, size); + + if (l1 != l2) + return l1 - l2; + + cmp = isl_int_abs_cmp((*c1)[1 + l1], (*c2)[1 + l1]); + if (cmp != 0) + return cmp; + cmp = isl_int_cmp((*c1)[1 + l1], (*c2)[1 + l1]); + if (cmp != 0) + return -cmp; + + return isl_seq_cmp(*c1 + 1, *c2 + 1, size); +} + +/* Return -1 if the constraint "c1" of "bmap" is sorted before "c2" + * by isl_basic_map_sort_constraints, 1 if it is sorted after "c2" + * and 0 if the two constraints are the same (up to the constant term). + */ +int isl_basic_map_constraint_cmp(__isl_keep isl_basic_map *bmap, + isl_int *c1, isl_int *c2) +{ + unsigned total; + + if (!bmap) + return -2; + total = isl_basic_map_total_dim(bmap); + return sort_constraint_cmp(&c1, &c2, &total); +} + +__isl_give isl_basic_map *isl_basic_map_sort_constraints( + __isl_take isl_basic_map *bmap) +{ + unsigned total; + + if (!bmap) + return NULL; + if (bmap->n_ineq == 0) + return bmap; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NORMALIZED)) + return bmap; + total = isl_basic_map_total_dim(bmap); + if (isl_sort(bmap->ineq, bmap->n_ineq, sizeof(isl_int *), + &sort_constraint_cmp, &total) < 0) + return isl_basic_map_free(bmap); + return bmap; +} + +__isl_give isl_basic_set *isl_basic_set_sort_constraints( + __isl_take isl_basic_set *bset) +{ + return (struct isl_basic_set *)isl_basic_map_sort_constraints( + (struct isl_basic_map *)bset); +} + +struct isl_basic_map *isl_basic_map_normalize(struct isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NORMALIZED)) + return bmap; + bmap = isl_basic_map_remove_redundancies(bmap); + bmap = isl_basic_map_sort_constraints(bmap); + if (bmap) + ISL_F_SET(bmap, ISL_BASIC_MAP_NORMALIZED); + return bmap; +} + +struct isl_basic_set *isl_basic_set_normalize(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *)isl_basic_map_normalize( + (struct isl_basic_map *)bset); +} + +int isl_basic_map_plain_cmp(const __isl_keep isl_basic_map *bmap1, + const __isl_keep isl_basic_map *bmap2) +{ + int i, cmp; + unsigned total; + + if (!bmap1 || !bmap2) + return -1; + + if (bmap1 == bmap2) + return 0; + if (ISL_F_ISSET(bmap1, ISL_BASIC_MAP_RATIONAL) != + ISL_F_ISSET(bmap2, ISL_BASIC_MAP_RATIONAL)) + return ISL_F_ISSET(bmap1, ISL_BASIC_MAP_RATIONAL) ? -1 : 1; + if (isl_basic_map_n_param(bmap1) != isl_basic_map_n_param(bmap2)) + return isl_basic_map_n_param(bmap1) - isl_basic_map_n_param(bmap2); + if (isl_basic_map_n_in(bmap1) != isl_basic_map_n_in(bmap2)) + return isl_basic_map_n_out(bmap1) - isl_basic_map_n_out(bmap2); + if (isl_basic_map_n_out(bmap1) != isl_basic_map_n_out(bmap2)) + return isl_basic_map_n_out(bmap1) - isl_basic_map_n_out(bmap2); + if (ISL_F_ISSET(bmap1, ISL_BASIC_MAP_EMPTY) && + ISL_F_ISSET(bmap2, ISL_BASIC_MAP_EMPTY)) + return 0; + if (ISL_F_ISSET(bmap1, ISL_BASIC_MAP_EMPTY)) + return 1; + if (ISL_F_ISSET(bmap2, ISL_BASIC_MAP_EMPTY)) + return -1; + if (bmap1->n_eq != bmap2->n_eq) + return bmap1->n_eq - bmap2->n_eq; + if (bmap1->n_ineq != bmap2->n_ineq) + return bmap1->n_ineq - bmap2->n_ineq; + if (bmap1->n_div != bmap2->n_div) + return bmap1->n_div - bmap2->n_div; + total = isl_basic_map_total_dim(bmap1); + for (i = 0; i < bmap1->n_eq; ++i) { + cmp = isl_seq_cmp(bmap1->eq[i], bmap2->eq[i], 1+total); + if (cmp) + return cmp; + } + for (i = 0; i < bmap1->n_ineq; ++i) { + cmp = isl_seq_cmp(bmap1->ineq[i], bmap2->ineq[i], 1+total); + if (cmp) + return cmp; + } + for (i = 0; i < bmap1->n_div; ++i) { + cmp = isl_seq_cmp(bmap1->div[i], bmap2->div[i], 1+1+total); + if (cmp) + return cmp; + } + return 0; +} + +int isl_basic_set_plain_cmp(const __isl_keep isl_basic_set *bset1, + const __isl_keep isl_basic_set *bset2) +{ + return isl_basic_map_plain_cmp(bset1, bset2); +} + +int isl_set_plain_cmp(__isl_keep isl_set *set1, __isl_keep isl_set *set2) +{ + int i, cmp; + + if (set1 == set2) + return 0; + if (set1->n != set2->n) + return set1->n - set2->n; + + for (i = 0; i < set1->n; ++i) { + cmp = isl_basic_set_plain_cmp(set1->p[i], set2->p[i]); + if (cmp) + return cmp; + } + + return 0; +} + +isl_bool isl_basic_map_plain_is_equal(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2) +{ + if (!bmap1 || !bmap2) + return isl_bool_error; + return isl_basic_map_plain_cmp(bmap1, bmap2) == 0; +} + +isl_bool isl_basic_set_plain_is_equal(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2) +{ + return isl_basic_map_plain_is_equal((isl_basic_map *)bset1, + (isl_basic_map *)bset2); +} + +static int qsort_bmap_cmp(const void *p1, const void *p2) +{ + const struct isl_basic_map *bmap1 = *(const struct isl_basic_map **)p1; + const struct isl_basic_map *bmap2 = *(const struct isl_basic_map **)p2; + + return isl_basic_map_plain_cmp(bmap1, bmap2); +} + +/* Sort the basic maps of "map" and remove duplicate basic maps. + * + * While removing basic maps, we make sure that the basic maps remain + * sorted because isl_map_normalize expects the basic maps of the result + * to be sorted. + */ +static __isl_give isl_map *sort_and_remove_duplicates(__isl_take isl_map *map) +{ + int i, j; + + map = isl_map_remove_empty_parts(map); + if (!map) + return NULL; + qsort(map->p, map->n, sizeof(struct isl_basic_map *), qsort_bmap_cmp); + for (i = map->n - 1; i >= 1; --i) { + if (!isl_basic_map_plain_is_equal(map->p[i - 1], map->p[i])) + continue; + isl_basic_map_free(map->p[i-1]); + for (j = i; j < map->n; ++j) + map->p[j - 1] = map->p[j]; + map->n--; + } + + return map; +} + +/* Remove obvious duplicates among the basic maps of "map". + * + * Unlike isl_map_normalize, this function does not remove redundant + * constraints and only removes duplicates that have exactly the same + * constraints in the input. It does sort the constraints and + * the basic maps to ease the detection of duplicates. + * + * If "map" has already been normalized or if the basic maps are + * disjoint, then there can be no duplicates. + */ +__isl_give isl_map *isl_map_remove_obvious_duplicates(__isl_take isl_map *map) +{ + int i; + isl_basic_map *bmap; + + if (!map) + return NULL; + if (map->n <= 1) + return map; + if (ISL_F_ISSET(map, ISL_MAP_NORMALIZED | ISL_MAP_DISJOINT)) + return map; + for (i = 0; i < map->n; ++i) { + bmap = isl_basic_map_copy(map->p[i]); + bmap = isl_basic_map_sort_constraints(bmap); + if (!bmap) + return isl_map_free(map); + isl_basic_map_free(map->p[i]); + map->p[i] = bmap; + } + + map = sort_and_remove_duplicates(map); + return map; +} + +/* We normalize in place, but if anything goes wrong we need + * to return NULL, so we need to make sure we don't change the + * meaning of any possible other copies of map. + */ +__isl_give isl_map *isl_map_normalize(__isl_take isl_map *map) +{ + int i; + struct isl_basic_map *bmap; + + if (!map) + return NULL; + if (ISL_F_ISSET(map, ISL_MAP_NORMALIZED)) + return map; + for (i = 0; i < map->n; ++i) { + bmap = isl_basic_map_normalize(isl_basic_map_copy(map->p[i])); + if (!bmap) + goto error; + isl_basic_map_free(map->p[i]); + map->p[i] = bmap; + } + + map = sort_and_remove_duplicates(map); + if (map) + ISL_F_SET(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +struct isl_set *isl_set_normalize(struct isl_set *set) +{ + return (struct isl_set *)isl_map_normalize((struct isl_map *)set); +} + +isl_bool isl_map_plain_is_equal(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + int i; + isl_bool equal; + + if (!map1 || !map2) + return isl_bool_error; + + if (map1 == map2) + return isl_bool_true; + if (!isl_space_is_equal(map1->dim, map2->dim)) + return isl_bool_false; + + map1 = isl_map_copy(map1); + map2 = isl_map_copy(map2); + map1 = isl_map_normalize(map1); + map2 = isl_map_normalize(map2); + if (!map1 || !map2) + goto error; + equal = map1->n == map2->n; + for (i = 0; equal && i < map1->n; ++i) { + equal = isl_basic_map_plain_is_equal(map1->p[i], map2->p[i]); + if (equal < 0) + goto error; + } + isl_map_free(map1); + isl_map_free(map2); + return equal; +error: + isl_map_free(map1); + isl_map_free(map2); + return isl_bool_error; +} + +isl_bool isl_set_plain_is_equal(__isl_keep isl_set *set1, + __isl_keep isl_set *set2) +{ + return isl_map_plain_is_equal((struct isl_map *)set1, + (struct isl_map *)set2); +} + +/* Return an interval that ranges from min to max (inclusive) + */ +struct isl_basic_set *isl_basic_set_interval(struct isl_ctx *ctx, + isl_int min, isl_int max) +{ + int k; + struct isl_basic_set *bset = NULL; + + bset = isl_basic_set_alloc(ctx, 0, 1, 0, 0, 2); + if (!bset) + goto error; + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_int_set_si(bset->ineq[k][1], 1); + isl_int_neg(bset->ineq[k][0], min); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_int_set_si(bset->ineq[k][1], -1); + isl_int_set(bset->ineq[k][0], max); + + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Return the basic maps in "map" as a list. + */ +__isl_give isl_basic_map_list *isl_map_get_basic_map_list( + __isl_keep isl_map *map) +{ + int i; + isl_ctx *ctx; + isl_basic_map_list *list; + + if (!map) + return NULL; + ctx = isl_map_get_ctx(map); + list = isl_basic_map_list_alloc(ctx, map->n); + + for (i = 0; i < map->n; ++i) { + isl_basic_map *bmap; + + bmap = isl_basic_map_copy(map->p[i]); + list = isl_basic_map_list_add(list, bmap); + } + + return list; +} + +/* Return the intersection of the elements in the non-empty list "list". + * All elements are assumed to live in the same space. + */ +__isl_give isl_basic_map *isl_basic_map_list_intersect( + __isl_take isl_basic_map_list *list) +{ + int i, n; + isl_basic_map *bmap; + + if (!list) + return NULL; + n = isl_basic_map_list_n_basic_map(list); + if (n < 1) + isl_die(isl_basic_map_list_get_ctx(list), isl_error_invalid, + "expecting non-empty list", goto error); + + bmap = isl_basic_map_list_get_basic_map(list, 0); + for (i = 1; i < n; ++i) { + isl_basic_map *bmap_i; + + bmap_i = isl_basic_map_list_get_basic_map(list, i); + bmap = isl_basic_map_intersect(bmap, bmap_i); + } + + isl_basic_map_list_free(list); + return bmap; +error: + isl_basic_map_list_free(list); + return NULL; +} + +/* Return the intersection of the elements in the non-empty list "list". + * All elements are assumed to live in the same space. + */ +__isl_give isl_basic_set *isl_basic_set_list_intersect( + __isl_take isl_basic_set_list *list) +{ + return isl_basic_map_list_intersect(list); +} + +/* Return the union of the elements in the non-empty list "list". + * All elements are assumed to live in the same space. + */ +__isl_give isl_set *isl_set_list_union(__isl_take isl_set_list *list) +{ + int i, n; + isl_set *set; + + if (!list) + return NULL; + n = isl_set_list_n_set(list); + if (n < 1) + isl_die(isl_set_list_get_ctx(list), isl_error_invalid, + "expecting non-empty list", goto error); + + set = isl_set_list_get_set(list, 0); + for (i = 1; i < n; ++i) { + isl_set *set_i; + + set_i = isl_set_list_get_set(list, i); + set = isl_set_union(set, set_i); + } + + isl_set_list_free(list); + return set; +error: + isl_set_list_free(list); + return NULL; +} + +/* Return the Cartesian product of the basic sets in list (in the given order). + */ +__isl_give isl_basic_set *isl_basic_set_list_product( + __isl_take struct isl_basic_set_list *list) +{ + int i; + unsigned dim; + unsigned nparam; + unsigned extra; + unsigned n_eq; + unsigned n_ineq; + struct isl_basic_set *product = NULL; + + if (!list) + goto error; + isl_assert(list->ctx, list->n > 0, goto error); + isl_assert(list->ctx, list->p[0], goto error); + nparam = isl_basic_set_n_param(list->p[0]); + dim = isl_basic_set_n_dim(list->p[0]); + extra = list->p[0]->n_div; + n_eq = list->p[0]->n_eq; + n_ineq = list->p[0]->n_ineq; + for (i = 1; i < list->n; ++i) { + isl_assert(list->ctx, list->p[i], goto error); + isl_assert(list->ctx, + nparam == isl_basic_set_n_param(list->p[i]), goto error); + dim += isl_basic_set_n_dim(list->p[i]); + extra += list->p[i]->n_div; + n_eq += list->p[i]->n_eq; + n_ineq += list->p[i]->n_ineq; + } + product = isl_basic_set_alloc(list->ctx, nparam, dim, extra, + n_eq, n_ineq); + if (!product) + goto error; + dim = 0; + for (i = 0; i < list->n; ++i) { + isl_basic_set_add_constraints(product, + isl_basic_set_copy(list->p[i]), dim); + dim += isl_basic_set_n_dim(list->p[i]); + } + isl_basic_set_list_free(list); + return product; +error: + isl_basic_set_free(product); + isl_basic_set_list_free(list); + return NULL; +} + +struct isl_basic_map *isl_basic_map_product( + struct isl_basic_map *bmap1, struct isl_basic_map *bmap2) +{ + isl_space *dim_result = NULL; + struct isl_basic_map *bmap; + unsigned in1, in2, out1, out2, nparam, total, pos; + struct isl_dim_map *dim_map1, *dim_map2; + + if (!bmap1 || !bmap2) + goto error; + + isl_assert(bmap1->ctx, isl_space_match(bmap1->dim, isl_dim_param, + bmap2->dim, isl_dim_param), goto error); + dim_result = isl_space_product(isl_space_copy(bmap1->dim), + isl_space_copy(bmap2->dim)); + + in1 = isl_basic_map_n_in(bmap1); + in2 = isl_basic_map_n_in(bmap2); + out1 = isl_basic_map_n_out(bmap1); + out2 = isl_basic_map_n_out(bmap2); + nparam = isl_basic_map_n_param(bmap1); + + total = nparam + in1 + in2 + out1 + out2 + bmap1->n_div + bmap2->n_div; + dim_map1 = isl_dim_map_alloc(bmap1->ctx, total); + dim_map2 = isl_dim_map_alloc(bmap1->ctx, total); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos += in1); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += in2); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += out1); + isl_dim_map_div(dim_map1, bmap1, pos += out2); + isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div); + + bmap = isl_basic_map_alloc_space(dim_result, + bmap1->n_div + bmap2->n_div, + bmap1->n_eq + bmap2->n_eq, + bmap1->n_ineq + bmap2->n_ineq); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_flat_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2) +{ + isl_basic_map *prod; + + prod = isl_basic_map_product(bmap1, bmap2); + prod = isl_basic_map_flatten(prod); + return prod; +} + +__isl_give isl_basic_set *isl_basic_set_flat_product( + __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2) +{ + return isl_basic_map_flat_range_product(bset1, bset2); +} + +__isl_give isl_basic_map *isl_basic_map_domain_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2) +{ + isl_space *space_result = NULL; + isl_basic_map *bmap; + unsigned in1, in2, out, nparam, total, pos; + struct isl_dim_map *dim_map1, *dim_map2; + + if (!bmap1 || !bmap2) + goto error; + + space_result = isl_space_domain_product(isl_space_copy(bmap1->dim), + isl_space_copy(bmap2->dim)); + + in1 = isl_basic_map_dim(bmap1, isl_dim_in); + in2 = isl_basic_map_dim(bmap2, isl_dim_in); + out = isl_basic_map_dim(bmap1, isl_dim_out); + nparam = isl_basic_map_dim(bmap1, isl_dim_param); + + total = nparam + in1 + in2 + out + bmap1->n_div + bmap2->n_div; + dim_map1 = isl_dim_map_alloc(bmap1->ctx, total); + dim_map2 = isl_dim_map_alloc(bmap1->ctx, total); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos += in1); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += in2); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos); + isl_dim_map_div(dim_map1, bmap1, pos += out); + isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div); + + bmap = isl_basic_map_alloc_space(space_result, + bmap1->n_div + bmap2->n_div, + bmap1->n_eq + bmap2->n_eq, + bmap1->n_ineq + bmap2->n_ineq); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_range_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2) +{ + isl_space *dim_result = NULL; + isl_basic_map *bmap; + unsigned in, out1, out2, nparam, total, pos; + struct isl_dim_map *dim_map1, *dim_map2; + + if (!bmap1 || !bmap2) + goto error; + + if (!isl_space_match(bmap1->dim, isl_dim_param, + bmap2->dim, isl_dim_param)) + isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid, + "parameters don't match", goto error); + + dim_result = isl_space_range_product(isl_space_copy(bmap1->dim), + isl_space_copy(bmap2->dim)); + + in = isl_basic_map_dim(bmap1, isl_dim_in); + out1 = isl_basic_map_n_out(bmap1); + out2 = isl_basic_map_n_out(bmap2); + nparam = isl_basic_map_n_param(bmap1); + + total = nparam + in + out1 + out2 + bmap1->n_div + bmap2->n_div; + dim_map1 = isl_dim_map_alloc(bmap1->ctx, total); + dim_map2 = isl_dim_map_alloc(bmap1->ctx, total); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos); + isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += in); + isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += out1); + isl_dim_map_div(dim_map1, bmap1, pos += out2); + isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div); + + bmap = isl_basic_map_alloc_space(dim_result, + bmap1->n_div + bmap2->n_div, + bmap1->n_eq + bmap2->n_eq, + bmap1->n_ineq + bmap2->n_ineq); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1); + bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_flat_range_product( + __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2) +{ + isl_basic_map *prod; + + prod = isl_basic_map_range_product(bmap1, bmap2); + prod = isl_basic_map_flatten_range(prod); + return prod; +} + +/* Apply "basic_map_product" to each pair of basic maps in "map1" and "map2" + * and collect the results. + * The result live in the space obtained by calling "space_product" + * on the spaces of "map1" and "map2". + * If "remove_duplicates" is set then the result may contain duplicates + * (even if the inputs do not) and so we try and remove the obvious + * duplicates. + */ +static __isl_give isl_map *map_product(__isl_take isl_map *map1, + __isl_take isl_map *map2, + __isl_give isl_space *(*space_product)(__isl_take isl_space *left, + __isl_take isl_space *right), + __isl_give isl_basic_map *(*basic_map_product)( + __isl_take isl_basic_map *left, + __isl_take isl_basic_map *right), + int remove_duplicates) +{ + unsigned flags = 0; + struct isl_map *result; + int i, j; + + if (!map1 || !map2) + goto error; + + isl_assert(map1->ctx, isl_space_match(map1->dim, isl_dim_param, + map2->dim, isl_dim_param), goto error); + + if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT) && + ISL_F_ISSET(map2, ISL_MAP_DISJOINT)) + ISL_FL_SET(flags, ISL_MAP_DISJOINT); + + result = isl_map_alloc_space(space_product(isl_space_copy(map1->dim), + isl_space_copy(map2->dim)), + map1->n * map2->n, flags); + if (!result) + goto error; + for (i = 0; i < map1->n; ++i) + for (j = 0; j < map2->n; ++j) { + struct isl_basic_map *part; + part = basic_map_product(isl_basic_map_copy(map1->p[i]), + isl_basic_map_copy(map2->p[j])); + if (isl_basic_map_is_empty(part)) + isl_basic_map_free(part); + else + result = isl_map_add_basic_map(result, part); + if (!result) + goto error; + } + if (remove_duplicates) + result = isl_map_remove_obvious_duplicates(result); + isl_map_free(map1); + isl_map_free(map2); + return result; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +/* Given two maps A -> B and C -> D, construct a map [A -> C] -> [B -> D] + */ +static __isl_give isl_map *map_product_aligned(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return map_product(map1, map2, &isl_space_product, + &isl_basic_map_product, 0); +} + +__isl_give isl_map *isl_map_product(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_product_aligned); +} + +/* Given two maps A -> B and C -> D, construct a map (A, C) -> (B, D) + */ +__isl_give isl_map *isl_map_flat_product(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *prod; + + prod = isl_map_product(map1, map2); + prod = isl_map_flatten(prod); + return prod; +} + +/* Given two set A and B, construct its Cartesian product A x B. + */ +struct isl_set *isl_set_product(struct isl_set *set1, struct isl_set *set2) +{ + return isl_map_range_product(set1, set2); +} + +__isl_give isl_set *isl_set_flat_product(__isl_take isl_set *set1, + __isl_take isl_set *set2) +{ + return isl_map_flat_range_product(set1, set2); +} + +/* Given two maps A -> B and C -> D, construct a map [A -> C] -> (B * D) + */ +static __isl_give isl_map *map_domain_product_aligned(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return map_product(map1, map2, &isl_space_domain_product, + &isl_basic_map_domain_product, 1); +} + +/* Given two maps A -> B and C -> D, construct a map (A * C) -> [B -> D] + */ +static __isl_give isl_map *map_range_product_aligned(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return map_product(map1, map2, &isl_space_range_product, + &isl_basic_map_range_product, 1); +} + +__isl_give isl_map *isl_map_domain_product(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, + &map_domain_product_aligned); +} + +__isl_give isl_map *isl_map_range_product(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, + &map_range_product_aligned); +} + +/* Given a map of the form [A -> B] -> [C -> D], return the map A -> C. + */ +__isl_give isl_map *isl_map_factor_domain(__isl_take isl_map *map) +{ + isl_space *space; + int total1, keep1, total2, keep2; + + if (!map) + return NULL; + if (!isl_space_domain_is_wrapping(map->dim) || + !isl_space_range_is_wrapping(map->dim)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "not a product", return isl_map_free(map)); + + space = isl_map_get_space(map); + total1 = isl_space_dim(space, isl_dim_in); + total2 = isl_space_dim(space, isl_dim_out); + space = isl_space_factor_domain(space); + keep1 = isl_space_dim(space, isl_dim_in); + keep2 = isl_space_dim(space, isl_dim_out); + map = isl_map_project_out(map, isl_dim_in, keep1, total1 - keep1); + map = isl_map_project_out(map, isl_dim_out, keep2, total2 - keep2); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Given a map of the form [A -> B] -> [C -> D], return the map B -> D. + */ +__isl_give isl_map *isl_map_factor_range(__isl_take isl_map *map) +{ + isl_space *space; + int total1, keep1, total2, keep2; + + if (!map) + return NULL; + if (!isl_space_domain_is_wrapping(map->dim) || + !isl_space_range_is_wrapping(map->dim)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "not a product", return isl_map_free(map)); + + space = isl_map_get_space(map); + total1 = isl_space_dim(space, isl_dim_in); + total2 = isl_space_dim(space, isl_dim_out); + space = isl_space_factor_range(space); + keep1 = isl_space_dim(space, isl_dim_in); + keep2 = isl_space_dim(space, isl_dim_out); + map = isl_map_project_out(map, isl_dim_in, 0, total1 - keep1); + map = isl_map_project_out(map, isl_dim_out, 0, total2 - keep2); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Given a map of the form [A -> B] -> C, return the map A -> C. + */ +__isl_give isl_map *isl_map_domain_factor_domain(__isl_take isl_map *map) +{ + isl_space *space; + int total, keep; + + if (!map) + return NULL; + if (!isl_space_domain_is_wrapping(map->dim)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "domain is not a product", return isl_map_free(map)); + + space = isl_map_get_space(map); + total = isl_space_dim(space, isl_dim_in); + space = isl_space_domain_factor_domain(space); + keep = isl_space_dim(space, isl_dim_in); + map = isl_map_project_out(map, isl_dim_in, keep, total - keep); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Given a map of the form [A -> B] -> C, return the map B -> C. + */ +__isl_give isl_map *isl_map_domain_factor_range(__isl_take isl_map *map) +{ + isl_space *space; + int total, keep; + + if (!map) + return NULL; + if (!isl_space_domain_is_wrapping(map->dim)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "domain is not a product", return isl_map_free(map)); + + space = isl_map_get_space(map); + total = isl_space_dim(space, isl_dim_in); + space = isl_space_domain_factor_range(space); + keep = isl_space_dim(space, isl_dim_in); + map = isl_map_project_out(map, isl_dim_in, 0, total - keep); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Given a map A -> [B -> C], extract the map A -> B. + */ +__isl_give isl_map *isl_map_range_factor_domain(__isl_take isl_map *map) +{ + isl_space *space; + int total, keep; + + if (!map) + return NULL; + if (!isl_space_range_is_wrapping(map->dim)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "range is not a product", return isl_map_free(map)); + + space = isl_map_get_space(map); + total = isl_space_dim(space, isl_dim_out); + space = isl_space_range_factor_domain(space); + keep = isl_space_dim(space, isl_dim_out); + map = isl_map_project_out(map, isl_dim_out, keep, total - keep); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Given a map A -> [B -> C], extract the map A -> C. + */ +__isl_give isl_map *isl_map_range_factor_range(__isl_take isl_map *map) +{ + isl_space *space; + int total, keep; + + if (!map) + return NULL; + if (!isl_space_range_is_wrapping(map->dim)) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "range is not a product", return isl_map_free(map)); + + space = isl_map_get_space(map); + total = isl_space_dim(space, isl_dim_out); + space = isl_space_range_factor_range(space); + keep = isl_space_dim(space, isl_dim_out); + map = isl_map_project_out(map, isl_dim_out, 0, total - keep); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Given two maps A -> B and C -> D, construct a map (A, C) -> (B * D) + */ +__isl_give isl_map *isl_map_flat_domain_product(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *prod; + + prod = isl_map_domain_product(map1, map2); + prod = isl_map_flatten_domain(prod); + return prod; +} + +/* Given two maps A -> B and C -> D, construct a map (A * C) -> (B, D) + */ +__isl_give isl_map *isl_map_flat_range_product(__isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_map *prod; + + prod = isl_map_range_product(map1, map2); + prod = isl_map_flatten_range(prod); + return prod; +} + +uint32_t isl_basic_map_get_hash(__isl_keep isl_basic_map *bmap) +{ + int i; + uint32_t hash = isl_hash_init(); + unsigned total; + + if (!bmap) + return 0; + bmap = isl_basic_map_copy(bmap); + bmap = isl_basic_map_normalize(bmap); + if (!bmap) + return 0; + total = isl_basic_map_total_dim(bmap); + isl_hash_byte(hash, bmap->n_eq & 0xFF); + for (i = 0; i < bmap->n_eq; ++i) { + uint32_t c_hash; + c_hash = isl_seq_get_hash(bmap->eq[i], 1 + total); + isl_hash_hash(hash, c_hash); + } + isl_hash_byte(hash, bmap->n_ineq & 0xFF); + for (i = 0; i < bmap->n_ineq; ++i) { + uint32_t c_hash; + c_hash = isl_seq_get_hash(bmap->ineq[i], 1 + total); + isl_hash_hash(hash, c_hash); + } + isl_hash_byte(hash, bmap->n_div & 0xFF); + for (i = 0; i < bmap->n_div; ++i) { + uint32_t c_hash; + if (isl_int_is_zero(bmap->div[i][0])) + continue; + isl_hash_byte(hash, i & 0xFF); + c_hash = isl_seq_get_hash(bmap->div[i], 1 + 1 + total); + isl_hash_hash(hash, c_hash); + } + isl_basic_map_free(bmap); + return hash; +} + +uint32_t isl_basic_set_get_hash(__isl_keep isl_basic_set *bset) +{ + return isl_basic_map_get_hash((isl_basic_map *)bset); +} + +uint32_t isl_map_get_hash(__isl_keep isl_map *map) +{ + int i; + uint32_t hash; + + if (!map) + return 0; + map = isl_map_copy(map); + map = isl_map_normalize(map); + if (!map) + return 0; + + hash = isl_hash_init(); + for (i = 0; i < map->n; ++i) { + uint32_t bmap_hash; + bmap_hash = isl_basic_map_get_hash(map->p[i]); + isl_hash_hash(hash, bmap_hash); + } + + isl_map_free(map); + + return hash; +} + +uint32_t isl_set_get_hash(__isl_keep isl_set *set) +{ + return isl_map_get_hash((isl_map *)set); +} + +/* Check if the value for dimension dim is completely determined + * by the values of the other parameters and variables. + * That is, check if dimension dim is involved in an equality. + */ +int isl_basic_set_dim_is_unique(struct isl_basic_set *bset, unsigned dim) +{ + int i; + unsigned nparam; + + if (!bset) + return -1; + nparam = isl_basic_set_n_param(bset); + for (i = 0; i < bset->n_eq; ++i) + if (!isl_int_is_zero(bset->eq[i][1 + nparam + dim])) + return 1; + return 0; +} + +/* Check if the value for dimension dim is completely determined + * by the values of the other parameters and variables. + * That is, check if dimension dim is involved in an equality + * for each of the subsets. + */ +int isl_set_dim_is_unique(struct isl_set *set, unsigned dim) +{ + int i; + + if (!set) + return -1; + for (i = 0; i < set->n; ++i) { + int unique; + unique = isl_basic_set_dim_is_unique(set->p[i], dim); + if (unique != 1) + return unique; + } + return 1; +} + +/* Return the number of basic maps in the (current) representation of "map". + */ +int isl_map_n_basic_map(__isl_keep isl_map *map) +{ + return map ? map->n : 0; +} + +int isl_set_n_basic_set(__isl_keep isl_set *set) +{ + return set ? set->n : 0; +} + +isl_stat isl_map_foreach_basic_map(__isl_keep isl_map *map, + isl_stat (*fn)(__isl_take isl_basic_map *bmap, void *user), void *user) +{ + int i; + + if (!map) + return isl_stat_error; + + for (i = 0; i < map->n; ++i) + if (fn(isl_basic_map_copy(map->p[i]), user) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +isl_stat isl_set_foreach_basic_set(__isl_keep isl_set *set, + isl_stat (*fn)(__isl_take isl_basic_set *bset, void *user), void *user) +{ + int i; + + if (!set) + return isl_stat_error; + + for (i = 0; i < set->n; ++i) + if (fn(isl_basic_set_copy(set->p[i]), user) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Return a list of basic sets, the union of which is equal to "set". + */ +__isl_give isl_basic_set_list *isl_set_get_basic_set_list( + __isl_keep isl_set *set) +{ + int i; + isl_basic_set_list *list; + + if (!set) + return NULL; + + list = isl_basic_set_list_alloc(isl_set_get_ctx(set), set->n); + for (i = 0; i < set->n; ++i) { + isl_basic_set *bset; + + bset = isl_basic_set_copy(set->p[i]); + list = isl_basic_set_list_add(list, bset); + } + + return list; +} + +__isl_give isl_basic_set *isl_basic_set_lift(__isl_take isl_basic_set *bset) +{ + isl_space *dim; + + if (!bset) + return NULL; + + bset = isl_basic_set_cow(bset); + if (!bset) + return NULL; + + dim = isl_basic_set_get_space(bset); + dim = isl_space_lift(dim, bset->n_div); + if (!dim) + goto error; + isl_space_free(bset->dim); + bset->dim = dim; + bset->extra -= bset->n_div; + bset->n_div = 0; + + bset = isl_basic_set_finalize(bset); + + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +__isl_give isl_set *isl_set_lift(__isl_take isl_set *set) +{ + int i; + isl_space *dim; + unsigned n_div; + + set = isl_set_align_divs(set); + + if (!set) + return NULL; + + set = isl_set_cow(set); + if (!set) + return NULL; + + n_div = set->p[0]->n_div; + dim = isl_set_get_space(set); + dim = isl_space_lift(dim, n_div); + if (!dim) + goto error; + isl_space_free(set->dim); + set->dim = dim; + + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_lift(set->p[i]); + if (!set->p[i]) + goto error; + } + + return set; +error: + isl_set_free(set); + return NULL; +} + +__isl_give isl_map *isl_set_lifting(__isl_take isl_set *set) +{ + isl_space *dim; + struct isl_basic_map *bmap; + unsigned n_set; + unsigned n_div; + unsigned n_param; + unsigned total; + int i, k, l; + + set = isl_set_align_divs(set); + + if (!set) + return NULL; + + dim = isl_set_get_space(set); + if (set->n == 0 || set->p[0]->n_div == 0) { + isl_set_free(set); + return isl_map_identity(isl_space_map_from_set(dim)); + } + + n_div = set->p[0]->n_div; + dim = isl_space_map_from_set(dim); + n_param = isl_space_dim(dim, isl_dim_param); + n_set = isl_space_dim(dim, isl_dim_in); + dim = isl_space_extend(dim, n_param, n_set, n_set + n_div); + bmap = isl_basic_map_alloc_space(dim, 0, n_set, 2 * n_div); + for (i = 0; i < n_set; ++i) + bmap = var_equal(bmap, i); + + total = n_param + n_set + n_set + n_div; + for (i = 0; i < n_div; ++i) { + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->ineq[k], set->p[0]->div[i]+1, 1+n_param); + isl_seq_clr(bmap->ineq[k]+1+n_param, n_set); + isl_seq_cpy(bmap->ineq[k]+1+n_param+n_set, + set->p[0]->div[i]+1+1+n_param, n_set + n_div); + isl_int_neg(bmap->ineq[k][1+n_param+n_set+n_set+i], + set->p[0]->div[i][0]); + + l = isl_basic_map_alloc_inequality(bmap); + if (l < 0) + goto error; + isl_seq_neg(bmap->ineq[l], bmap->ineq[k], 1 + total); + isl_int_add(bmap->ineq[l][0], bmap->ineq[l][0], + set->p[0]->div[i][0]); + isl_int_sub_ui(bmap->ineq[l][0], bmap->ineq[l][0], 1); + } + + isl_set_free(set); + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_finalize(bmap); + return isl_map_from_basic_map(bmap); +error: + isl_set_free(set); + isl_basic_map_free(bmap); + return NULL; +} + +int isl_basic_set_size(__isl_keep isl_basic_set *bset) +{ + unsigned dim; + int size = 0; + + if (!bset) + return -1; + + dim = isl_basic_set_total_dim(bset); + size += bset->n_eq * (1 + dim); + size += bset->n_ineq * (1 + dim); + size += bset->n_div * (2 + dim); + + return size; +} + +int isl_set_size(__isl_keep isl_set *set) +{ + int i; + int size = 0; + + if (!set) + return -1; + + for (i = 0; i < set->n; ++i) + size += isl_basic_set_size(set->p[i]); + + return size; +} + +/* Check if there is any lower bound (if lower == 0) and/or upper + * bound (if upper == 0) on the specified dim. + */ +static isl_bool basic_map_dim_is_bounded(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, int lower, int upper) +{ + int i; + + if (!bmap) + return isl_bool_error; + + isl_assert(bmap->ctx, pos < isl_basic_map_dim(bmap, type), + return isl_bool_error); + + pos += isl_basic_map_offset(bmap, type); + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (!isl_int_is_zero(bmap->div[i][1 + pos])) + return isl_bool_true; + } + + for (i = 0; i < bmap->n_eq; ++i) + if (!isl_int_is_zero(bmap->eq[i][pos])) + return isl_bool_true; + + for (i = 0; i < bmap->n_ineq; ++i) { + int sgn = isl_int_sgn(bmap->ineq[i][pos]); + if (sgn > 0) + lower = 1; + if (sgn < 0) + upper = 1; + } + + return lower && upper; +} + +int isl_basic_map_dim_is_bounded(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos) +{ + return basic_map_dim_is_bounded(bmap, type, pos, 0, 0); +} + +isl_bool isl_basic_map_dim_has_lower_bound(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos) +{ + return basic_map_dim_is_bounded(bmap, type, pos, 0, 1); +} + +isl_bool isl_basic_map_dim_has_upper_bound(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos) +{ + return basic_map_dim_is_bounded(bmap, type, pos, 1, 0); +} + +int isl_map_dim_is_bounded(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos) +{ + int i; + + if (!map) + return -1; + + for (i = 0; i < map->n; ++i) { + int bounded; + bounded = isl_basic_map_dim_is_bounded(map->p[i], type, pos); + if (bounded < 0 || !bounded) + return bounded; + } + + return 1; +} + +/* Return 1 if the specified dim is involved in both an upper bound + * and a lower bound. + */ +int isl_set_dim_is_bounded(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return isl_map_dim_is_bounded((isl_map *)set, type, pos); +} + +/* Does "map" have a bound (according to "fn") for any of its basic maps? + */ +static isl_bool has_any_bound(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos, + isl_bool (*fn)(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos)) +{ + int i; + + if (!map) + return isl_bool_error; + + for (i = 0; i < map->n; ++i) { + isl_bool bounded; + bounded = fn(map->p[i], type, pos); + if (bounded < 0 || bounded) + return bounded; + } + + return isl_bool_false; +} + +/* Return 1 if the specified dim is involved in any lower bound. + */ +isl_bool isl_set_dim_has_any_lower_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return has_any_bound(set, type, pos, + &isl_basic_map_dim_has_lower_bound); +} + +/* Return 1 if the specified dim is involved in any upper bound. + */ +isl_bool isl_set_dim_has_any_upper_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return has_any_bound(set, type, pos, + &isl_basic_map_dim_has_upper_bound); +} + +/* Does "map" have a bound (according to "fn") for all of its basic maps? + */ +static isl_bool has_bound(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos, + isl_bool (*fn)(__isl_keep isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos)) +{ + int i; + + if (!map) + return isl_bool_error; + + for (i = 0; i < map->n; ++i) { + isl_bool bounded; + bounded = fn(map->p[i], type, pos); + if (bounded < 0 || !bounded) + return bounded; + } + + return isl_bool_true; +} + +/* Return 1 if the specified dim has a lower bound (in each of its basic sets). + */ +isl_bool isl_set_dim_has_lower_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return has_bound(set, type, pos, &isl_basic_map_dim_has_lower_bound); +} + +/* Return 1 if the specified dim has an upper bound (in each of its basic sets). + */ +isl_bool isl_set_dim_has_upper_bound(__isl_keep isl_set *set, + enum isl_dim_type type, unsigned pos) +{ + return has_bound(set, type, pos, &isl_basic_map_dim_has_upper_bound); +} + +/* For each of the "n" variables starting at "first", determine + * the sign of the variable and put the results in the first "n" + * elements of the array "signs". + * Sign + * 1 means that the variable is non-negative + * -1 means that the variable is non-positive + * 0 means the variable attains both positive and negative values. + */ +int isl_basic_set_vars_get_sign(__isl_keep isl_basic_set *bset, + unsigned first, unsigned n, int *signs) +{ + isl_vec *bound = NULL; + struct isl_tab *tab = NULL; + struct isl_tab_undo *snap; + int i; + + if (!bset || !signs) + return -1; + + bound = isl_vec_alloc(bset->ctx, 1 + isl_basic_set_total_dim(bset)); + tab = isl_tab_from_basic_set(bset, 0); + if (!bound || !tab) + goto error; + + isl_seq_clr(bound->el, bound->size); + isl_int_set_si(bound->el[0], -1); + + snap = isl_tab_snap(tab); + for (i = 0; i < n; ++i) { + int empty; + + isl_int_set_si(bound->el[1 + first + i], -1); + if (isl_tab_add_ineq(tab, bound->el) < 0) + goto error; + empty = tab->empty; + isl_int_set_si(bound->el[1 + first + i], 0); + if (isl_tab_rollback(tab, snap) < 0) + goto error; + + if (empty) { + signs[i] = 1; + continue; + } + + isl_int_set_si(bound->el[1 + first + i], 1); + if (isl_tab_add_ineq(tab, bound->el) < 0) + goto error; + empty = tab->empty; + isl_int_set_si(bound->el[1 + first + i], 0); + if (isl_tab_rollback(tab, snap) < 0) + goto error; + + signs[i] = empty ? -1 : 0; + } + + isl_tab_free(tab); + isl_vec_free(bound); + return 0; +error: + isl_tab_free(tab); + isl_vec_free(bound); + return -1; +} + +int isl_basic_set_dims_get_sign(__isl_keep isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n, int *signs) +{ + if (!bset || !signs) + return -1; + isl_assert(bset->ctx, first + n <= isl_basic_set_dim(bset, type), + return -1); + + first += pos(bset->dim, type) - 1; + return isl_basic_set_vars_get_sign(bset, first, n, signs); +} + +/* Is it possible for the integer division "div" to depend (possibly + * indirectly) on any output dimensions? + * + * If the div is undefined, then we conservatively assume that it + * may depend on them. + * Otherwise, we check if it actually depends on them or on any integer + * divisions that may depend on them. + */ +static int div_may_involve_output(__isl_keep isl_basic_map *bmap, int div) +{ + int i; + unsigned n_out, o_out; + unsigned n_div, o_div; + + if (isl_int_is_zero(bmap->div[div][0])) + return 1; + + n_out = isl_basic_map_dim(bmap, isl_dim_out); + o_out = isl_basic_map_offset(bmap, isl_dim_out); + + if (isl_seq_first_non_zero(bmap->div[div] + 1 + o_out, n_out) != -1) + return 1; + + n_div = isl_basic_map_dim(bmap, isl_dim_div); + o_div = isl_basic_map_offset(bmap, isl_dim_div); + + for (i = 0; i < n_div; ++i) { + if (isl_int_is_zero(bmap->div[div][1 + o_div + i])) + continue; + if (div_may_involve_output(bmap, i)) + return 1; + } + + return 0; +} + +/* Return the first integer division of "bmap" in the range + * [first, first + n[ that may depend on any output dimensions and + * that has a non-zero coefficient in "c" (where the first coefficient + * in "c" corresponds to integer division "first"). + */ +static int first_div_may_involve_output(__isl_keep isl_basic_map *bmap, + isl_int *c, int first, int n) +{ + int k; + + if (!bmap) + return -1; + + for (k = first; k < first + n; ++k) { + if (isl_int_is_zero(c[k])) + continue; + if (div_may_involve_output(bmap, k)) + return k; + } + + return first + n; +} + +/* Look for a pair of inequality constraints in "bmap" of the form + * + * -l + i >= 0 or i >= l + * and + * n + l - i >= 0 or i <= l + n + * + * with n < "m" and i the output dimension at position "pos". + * (Note that n >= 0 as otherwise the two constraints would conflict.) + * Furthermore, "l" is only allowed to involve parameters, input dimensions + * and earlier output dimensions, as well as integer divisions that do + * not involve any of the output dimensions. + * + * Return the index of the first inequality constraint or bmap->n_ineq + * if no such pair can be found. + */ +static int find_modulo_constraint_pair(__isl_keep isl_basic_map *bmap, + int pos, isl_int m) +{ + int i, j; + isl_ctx *ctx; + unsigned total; + unsigned n_div, o_div; + unsigned n_out, o_out; + int less; + + if (!bmap) + return -1; + + ctx = isl_basic_map_get_ctx(bmap); + total = isl_basic_map_total_dim(bmap); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + o_out = isl_basic_map_offset(bmap, isl_dim_out); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + o_div = isl_basic_map_offset(bmap, isl_dim_div); + for (i = 0; i < bmap->n_ineq; ++i) { + if (!isl_int_abs_eq(bmap->ineq[i][o_out + pos], ctx->one)) + continue; + if (isl_seq_first_non_zero(bmap->ineq[i] + o_out + pos + 1, + n_out - (pos + 1)) != -1) + continue; + if (first_div_may_involve_output(bmap, bmap->ineq[i] + o_div, + 0, n_div) < n_div) + continue; + for (j = i + 1; j < bmap->n_ineq; ++j) { + if (!isl_int_abs_eq(bmap->ineq[j][o_out + pos], + ctx->one)) + continue; + if (!isl_seq_is_neg(bmap->ineq[i] + 1, + bmap->ineq[j] + 1, total)) + continue; + break; + } + if (j >= bmap->n_ineq) + continue; + isl_int_add(bmap->ineq[i][0], + bmap->ineq[i][0], bmap->ineq[j][0]); + less = isl_int_abs_lt(bmap->ineq[i][0], m); + isl_int_sub(bmap->ineq[i][0], + bmap->ineq[i][0], bmap->ineq[j][0]); + if (!less) + continue; + if (isl_int_is_one(bmap->ineq[i][o_out + pos])) + return i; + else + return j; + } + + return bmap->n_ineq; +} + +/* Return the index of the equality of "bmap" that defines + * the output dimension "pos" in terms of earlier dimensions. + * The equality may also involve integer divisions, as long + * as those integer divisions are defined in terms of + * parameters or input dimensions. + * In this case, *div is set to the number of integer divisions and + * *ineq is set to the number of inequality constraints (provided + * div and ineq are not NULL). + * + * The equality may also involve a single integer division involving + * the output dimensions (typically only output dimension "pos") as + * long as the coefficient of output dimension "pos" is 1 or -1 and + * there is a pair of constraints i >= l and i <= l + n, with i referring + * to output dimension "pos", l an expression involving only earlier + * dimensions and n smaller than the coefficient of the integer division + * in the equality. In this case, the output dimension can be defined + * in terms of a modulo expression that does not involve the integer division. + * *div is then set to this single integer division and + * *ineq is set to the index of constraint i >= l. + * + * Return bmap->n_eq if there is no such equality. + * Return -1 on error. + */ +int isl_basic_map_output_defining_equality(__isl_keep isl_basic_map *bmap, + int pos, int *div, int *ineq) +{ + int j, k, l; + unsigned n_out, o_out; + unsigned n_div, o_div; + + if (!bmap) + return -1; + + n_out = isl_basic_map_dim(bmap, isl_dim_out); + o_out = isl_basic_map_offset(bmap, isl_dim_out); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + o_div = isl_basic_map_offset(bmap, isl_dim_div); + + if (ineq) + *ineq = bmap->n_ineq; + if (div) + *div = n_div; + for (j = 0; j < bmap->n_eq; ++j) { + if (isl_int_is_zero(bmap->eq[j][o_out + pos])) + continue; + if (isl_seq_first_non_zero(bmap->eq[j] + o_out + pos + 1, + n_out - (pos + 1)) != -1) + continue; + k = first_div_may_involve_output(bmap, bmap->eq[j] + o_div, + 0, n_div); + if (k >= n_div) + return j; + if (!isl_int_is_one(bmap->eq[j][o_out + pos]) && + !isl_int_is_negone(bmap->eq[j][o_out + pos])) + continue; + if (first_div_may_involve_output(bmap, bmap->eq[j] + o_div, + k + 1, n_div - (k+1)) < n_div) + continue; + l = find_modulo_constraint_pair(bmap, pos, + bmap->eq[j][o_div + k]); + if (l < 0) + return -1; + if (l >= bmap->n_ineq) + continue; + if (div) + *div = k; + if (ineq) + *ineq = l; + return j; + } + + return bmap->n_eq; +} + +/* Check if the given basic map is obviously single-valued. + * In particular, for each output dimension, check that there is + * an equality that defines the output dimension in terms of + * earlier dimensions. + */ +isl_bool isl_basic_map_plain_is_single_valued(__isl_keep isl_basic_map *bmap) +{ + int i; + unsigned n_out; + + if (!bmap) + return isl_bool_error; + + n_out = isl_basic_map_dim(bmap, isl_dim_out); + + for (i = 0; i < n_out; ++i) { + int eq; + + eq = isl_basic_map_output_defining_equality(bmap, i, + NULL, NULL); + if (eq < 0) + return isl_bool_error; + if (eq >= bmap->n_eq) + return isl_bool_false; + } + + return isl_bool_true; +} + +/* Check if the given basic map is single-valued. + * We simply compute + * + * M \circ M^-1 + * + * and check if the result is a subset of the identity mapping. + */ +isl_bool isl_basic_map_is_single_valued(__isl_keep isl_basic_map *bmap) +{ + isl_space *space; + isl_basic_map *test; + isl_basic_map *id; + isl_bool sv; + + sv = isl_basic_map_plain_is_single_valued(bmap); + if (sv < 0 || sv) + return sv; + + test = isl_basic_map_reverse(isl_basic_map_copy(bmap)); + test = isl_basic_map_apply_range(test, isl_basic_map_copy(bmap)); + + space = isl_basic_map_get_space(bmap); + space = isl_space_map_from_set(isl_space_range(space)); + id = isl_basic_map_identity(space); + + sv = isl_basic_map_is_subset(test, id); + + isl_basic_map_free(test); + isl_basic_map_free(id); + + return sv; +} + +/* Check if the given map is obviously single-valued. + */ +isl_bool isl_map_plain_is_single_valued(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + if (map->n == 0) + return isl_bool_true; + if (map->n >= 2) + return isl_bool_false; + + return isl_basic_map_plain_is_single_valued(map->p[0]); +} + +/* Check if the given map is single-valued. + * We simply compute + * + * M \circ M^-1 + * + * and check if the result is a subset of the identity mapping. + */ +isl_bool isl_map_is_single_valued(__isl_keep isl_map *map) +{ + isl_space *dim; + isl_map *test; + isl_map *id; + isl_bool sv; + + sv = isl_map_plain_is_single_valued(map); + if (sv < 0 || sv) + return sv; + + test = isl_map_reverse(isl_map_copy(map)); + test = isl_map_apply_range(test, isl_map_copy(map)); + + dim = isl_space_map_from_set(isl_space_range(isl_map_get_space(map))); + id = isl_map_identity(dim); + + sv = isl_map_is_subset(test, id); + + isl_map_free(test); + isl_map_free(id); + + return sv; +} + +isl_bool isl_map_is_injective(__isl_keep isl_map *map) +{ + isl_bool in; + + map = isl_map_copy(map); + map = isl_map_reverse(map); + in = isl_map_is_single_valued(map); + isl_map_free(map); + + return in; +} + +/* Check if the given map is obviously injective. + */ +isl_bool isl_map_plain_is_injective(__isl_keep isl_map *map) +{ + isl_bool in; + + map = isl_map_copy(map); + map = isl_map_reverse(map); + in = isl_map_plain_is_single_valued(map); + isl_map_free(map); + + return in; +} + +isl_bool isl_map_is_bijective(__isl_keep isl_map *map) +{ + isl_bool sv; + + sv = isl_map_is_single_valued(map); + if (sv < 0 || !sv) + return sv; + + return isl_map_is_injective(map); +} + +isl_bool isl_set_is_singleton(__isl_keep isl_set *set) +{ + return isl_map_is_single_valued((isl_map *)set); +} + +/* Does "map" only map elements to themselves? + * + * If the domain and range spaces are different, then "map" + * is considered not to be an identity relation, even if it is empty. + * Otherwise, construct the maximal identity relation and + * check whether "map" is a subset of this relation. + */ +isl_bool isl_map_is_identity(__isl_keep isl_map *map) +{ + isl_space *space; + isl_map *id; + isl_bool equal, is_identity; + + space = isl_map_get_space(map); + equal = isl_space_tuple_is_equal(space, isl_dim_in, space, isl_dim_out); + isl_space_free(space); + if (equal < 0 || !equal) + return equal; + + id = isl_map_identity(isl_map_get_space(map)); + is_identity = isl_map_is_subset(map, id); + isl_map_free(id); + + return is_identity; +} + +int isl_map_is_translation(__isl_keep isl_map *map) +{ + int ok; + isl_set *delta; + + delta = isl_map_deltas(isl_map_copy(map)); + ok = isl_set_is_singleton(delta); + isl_set_free(delta); + + return ok; +} + +static int unique(isl_int *p, unsigned pos, unsigned len) +{ + if (isl_seq_first_non_zero(p, pos) != -1) + return 0; + if (isl_seq_first_non_zero(p + pos + 1, len - pos - 1) != -1) + return 0; + return 1; +} + +int isl_basic_set_is_box(__isl_keep isl_basic_set *bset) +{ + int i, j; + unsigned nvar; + unsigned ovar; + + if (!bset) + return -1; + + if (isl_basic_set_dim(bset, isl_dim_div) != 0) + return 0; + + nvar = isl_basic_set_dim(bset, isl_dim_set); + ovar = isl_space_offset(bset->dim, isl_dim_set); + for (j = 0; j < nvar; ++j) { + int lower = 0, upper = 0; + for (i = 0; i < bset->n_eq; ++i) { + if (isl_int_is_zero(bset->eq[i][1 + ovar + j])) + continue; + if (!unique(bset->eq[i] + 1 + ovar, j, nvar)) + return 0; + break; + } + if (i < bset->n_eq) + continue; + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_int_is_zero(bset->ineq[i][1 + ovar + j])) + continue; + if (!unique(bset->ineq[i] + 1 + ovar, j, nvar)) + return 0; + if (isl_int_is_pos(bset->ineq[i][1 + ovar + j])) + lower = 1; + else + upper = 1; + } + if (!lower || !upper) + return 0; + } + + return 1; +} + +int isl_set_is_box(__isl_keep isl_set *set) +{ + if (!set) + return -1; + if (set->n != 1) + return 0; + + return isl_basic_set_is_box(set->p[0]); +} + +isl_bool isl_basic_set_is_wrapping(__isl_keep isl_basic_set *bset) +{ + if (!bset) + return isl_bool_error; + + return isl_space_is_wrapping(bset->dim); +} + +isl_bool isl_set_is_wrapping(__isl_keep isl_set *set) +{ + if (!set) + return isl_bool_error; + + return isl_space_is_wrapping(set->dim); +} + +/* Modify the space of "map" through a call to "change". + * If "can_change" is set (not NULL), then first call it to check + * if the modification is allowed, printing the error message "cannot_change" + * if it is not. + */ +static __isl_give isl_map *isl_map_change_space(__isl_take isl_map *map, + isl_bool (*can_change)(__isl_keep isl_map *map), + const char *cannot_change, + __isl_give isl_space *(*change)(__isl_take isl_space *space)) +{ + isl_bool ok; + isl_space *space; + + if (!map) + return NULL; + + ok = can_change ? can_change(map) : isl_bool_true; + if (ok < 0) + return isl_map_free(map); + if (!ok) + isl_die(isl_map_get_ctx(map), isl_error_invalid, cannot_change, + return isl_map_free(map)); + + space = change(isl_map_get_space(map)); + map = isl_map_reset_space(map, space); + + return map; +} + +/* Is the domain of "map" a wrapped relation? + */ +isl_bool isl_map_domain_is_wrapping(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + + return isl_space_domain_is_wrapping(map->dim); +} + +/* Is the range of "map" a wrapped relation? + */ +isl_bool isl_map_range_is_wrapping(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + + return isl_space_range_is_wrapping(map->dim); +} + +__isl_give isl_basic_set *isl_basic_map_wrap(__isl_take isl_basic_map *bmap) +{ + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + bmap->dim = isl_space_wrap(bmap->dim); + if (!bmap->dim) + goto error; + + bmap = isl_basic_map_finalize(bmap); + + return (isl_basic_set *)bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Given a map A -> B, return the set (A -> B). + */ +__isl_give isl_set *isl_map_wrap(__isl_take isl_map *map) +{ + return isl_map_change_space(map, NULL, NULL, &isl_space_wrap); +} + +__isl_give isl_basic_map *isl_basic_set_unwrap(__isl_take isl_basic_set *bset) +{ + bset = isl_basic_set_cow(bset); + if (!bset) + return NULL; + + bset->dim = isl_space_unwrap(bset->dim); + if (!bset->dim) + goto error; + + bset = isl_basic_set_finalize(bset); + + return (isl_basic_map *)bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Given a set (A -> B), return the map A -> B. + * Error out if "set" is not of the form (A -> B). + */ +__isl_give isl_map *isl_set_unwrap(__isl_take isl_set *set) +{ + return isl_map_change_space(set, &isl_set_is_wrapping, + "not a wrapping set", &isl_space_unwrap); +} + +__isl_give isl_basic_map *isl_basic_map_reset(__isl_take isl_basic_map *bmap, + enum isl_dim_type type) +{ + if (!bmap) + return NULL; + + if (!isl_space_is_named_or_nested(bmap->dim, type)) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + bmap->dim = isl_space_reset(bmap->dim, type); + if (!bmap->dim) + goto error; + + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_map *isl_map_reset(__isl_take isl_map *map, + enum isl_dim_type type) +{ + int i; + + if (!map) + return NULL; + + if (!isl_space_is_named_or_nested(map->dim, type)) + return map; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_reset(map->p[i], type); + if (!map->p[i]) + goto error; + } + map->dim = isl_space_reset(map->dim, type); + if (!map->dim) + goto error; + + return map; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_flatten(__isl_take isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + if (!bmap->dim->nested[0] && !bmap->dim->nested[1]) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + bmap->dim = isl_space_flatten(bmap->dim); + if (!bmap->dim) + goto error; + + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_flatten(__isl_take isl_basic_set *bset) +{ + return (isl_basic_set *)isl_basic_map_flatten((isl_basic_map *)bset); +} + +__isl_give isl_basic_map *isl_basic_map_flatten_domain( + __isl_take isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + if (!bmap->dim->nested[0]) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + bmap->dim = isl_space_flatten_domain(bmap->dim); + if (!bmap->dim) + goto error; + + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_flatten_range( + __isl_take isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + if (!bmap->dim->nested[1]) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + bmap->dim = isl_space_flatten_range(bmap->dim); + if (!bmap->dim) + goto error; + + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Remove any internal structure from the spaces of domain and range of "map". + */ +__isl_give isl_map *isl_map_flatten(__isl_take isl_map *map) +{ + if (!map) + return NULL; + + if (!map->dim->nested[0] && !map->dim->nested[1]) + return map; + + return isl_map_change_space(map, NULL, NULL, &isl_space_flatten); +} + +__isl_give isl_set *isl_set_flatten(__isl_take isl_set *set) +{ + return (isl_set *)isl_map_flatten((isl_map *)set); +} + +__isl_give isl_map *isl_set_flatten_map(__isl_take isl_set *set) +{ + isl_space *dim, *flat_dim; + isl_map *map; + + dim = isl_set_get_space(set); + flat_dim = isl_space_flatten(isl_space_copy(dim)); + map = isl_map_identity(isl_space_join(isl_space_reverse(dim), flat_dim)); + map = isl_map_intersect_domain(map, set); + + return map; +} + +/* Remove any internal structure from the space of the domain of "map". + */ +__isl_give isl_map *isl_map_flatten_domain(__isl_take isl_map *map) +{ + if (!map) + return NULL; + + if (!map->dim->nested[0]) + return map; + + return isl_map_change_space(map, NULL, NULL, &isl_space_flatten_domain); +} + +/* Remove any internal structure from the space of the range of "map". + */ +__isl_give isl_map *isl_map_flatten_range(__isl_take isl_map *map) +{ + if (!map) + return NULL; + + if (!map->dim->nested[1]) + return map; + + return isl_map_change_space(map, NULL, NULL, &isl_space_flatten_range); +} + +/* Reorder the dimensions of "bmap" according to the given dim_map + * and set the dimension specification to "dim" and + * perform Gaussian elimination on the result. + */ +__isl_give isl_basic_map *isl_basic_map_realign(__isl_take isl_basic_map *bmap, + __isl_take isl_space *dim, __isl_take struct isl_dim_map *dim_map) +{ + isl_basic_map *res; + unsigned flags; + + bmap = isl_basic_map_cow(bmap); + if (!bmap || !dim || !dim_map) + goto error; + + flags = bmap->flags; + ISL_FL_CLR(flags, ISL_BASIC_MAP_FINAL); + ISL_FL_CLR(flags, ISL_BASIC_MAP_NORMALIZED); + ISL_FL_CLR(flags, ISL_BASIC_MAP_NORMALIZED_DIVS); + res = isl_basic_map_alloc_space(dim, + bmap->n_div, bmap->n_eq, bmap->n_ineq); + res = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map); + if (res) + res->flags = flags; + res = isl_basic_map_gauss(res, NULL); + res = isl_basic_map_finalize(res); + return res; +error: + free(dim_map); + isl_basic_map_free(bmap); + isl_space_free(dim); + return NULL; +} + +/* Reorder the dimensions of "map" according to given reordering. + */ +__isl_give isl_map *isl_map_realign(__isl_take isl_map *map, + __isl_take isl_reordering *r) +{ + int i; + struct isl_dim_map *dim_map; + + map = isl_map_cow(map); + dim_map = isl_dim_map_from_reordering(r); + if (!map || !r || !dim_map) + goto error; + + for (i = 0; i < map->n; ++i) { + struct isl_dim_map *dim_map_i; + + dim_map_i = isl_dim_map_extend(dim_map, map->p[i]); + + map->p[i] = isl_basic_map_realign(map->p[i], + isl_space_copy(r->dim), dim_map_i); + + if (!map->p[i]) + goto error; + } + + map = isl_map_reset_space(map, isl_space_copy(r->dim)); + + isl_reordering_free(r); + free(dim_map); + return map; +error: + free(dim_map); + isl_map_free(map); + isl_reordering_free(r); + return NULL; +} + +__isl_give isl_set *isl_set_realign(__isl_take isl_set *set, + __isl_take isl_reordering *r) +{ + return (isl_set *)isl_map_realign((isl_map *)set, r); +} + +__isl_give isl_map *isl_map_align_params(__isl_take isl_map *map, + __isl_take isl_space *model) +{ + isl_ctx *ctx; + + if (!map || !model) + goto error; + + ctx = isl_space_get_ctx(model); + if (!isl_space_has_named_params(model)) + isl_die(ctx, isl_error_invalid, + "model has unnamed parameters", goto error); + if (!isl_space_has_named_params(map->dim)) + isl_die(ctx, isl_error_invalid, + "relation has unnamed parameters", goto error); + if (!isl_space_match(map->dim, isl_dim_param, model, isl_dim_param)) { + isl_reordering *exp; + + model = isl_space_drop_dims(model, isl_dim_in, + 0, isl_space_dim(model, isl_dim_in)); + model = isl_space_drop_dims(model, isl_dim_out, + 0, isl_space_dim(model, isl_dim_out)); + exp = isl_parameter_alignment_reordering(map->dim, model); + exp = isl_reordering_extend_space(exp, isl_map_get_space(map)); + map = isl_map_realign(map, exp); + } + + isl_space_free(model); + return map; +error: + isl_space_free(model); + isl_map_free(map); + return NULL; +} + +__isl_give isl_set *isl_set_align_params(__isl_take isl_set *set, + __isl_take isl_space *model) +{ + return isl_map_align_params(set, model); +} + +/* Align the parameters of "bmap" to those of "model", introducing + * additional parameters if needed. + */ +__isl_give isl_basic_map *isl_basic_map_align_params( + __isl_take isl_basic_map *bmap, __isl_take isl_space *model) +{ + isl_ctx *ctx; + + if (!bmap || !model) + goto error; + + ctx = isl_space_get_ctx(model); + if (!isl_space_has_named_params(model)) + isl_die(ctx, isl_error_invalid, + "model has unnamed parameters", goto error); + if (!isl_space_has_named_params(bmap->dim)) + isl_die(ctx, isl_error_invalid, + "relation has unnamed parameters", goto error); + if (!isl_space_match(bmap->dim, isl_dim_param, model, isl_dim_param)) { + isl_reordering *exp; + struct isl_dim_map *dim_map; + + model = isl_space_drop_dims(model, isl_dim_in, + 0, isl_space_dim(model, isl_dim_in)); + model = isl_space_drop_dims(model, isl_dim_out, + 0, isl_space_dim(model, isl_dim_out)); + exp = isl_parameter_alignment_reordering(bmap->dim, model); + exp = isl_reordering_extend_space(exp, + isl_basic_map_get_space(bmap)); + dim_map = isl_dim_map_from_reordering(exp); + bmap = isl_basic_map_realign(bmap, + exp ? isl_space_copy(exp->dim) : NULL, + isl_dim_map_extend(dim_map, bmap)); + isl_reordering_free(exp); + free(dim_map); + } + + isl_space_free(model); + return bmap; +error: + isl_space_free(model); + isl_basic_map_free(bmap); + return NULL; +} + +/* Align the parameters of "bset" to those of "model", introducing + * additional parameters if needed. + */ +__isl_give isl_basic_set *isl_basic_set_align_params( + __isl_take isl_basic_set *bset, __isl_take isl_space *model) +{ + return isl_basic_map_align_params(bset, model); +} + +__isl_give isl_mat *isl_basic_map_equalities_matrix( + __isl_keep isl_basic_map *bmap, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5) +{ + enum isl_dim_type c[5] = { c1, c2, c3, c4, c5 }; + struct isl_mat *mat; + int i, j, k; + int pos; + + if (!bmap) + return NULL; + mat = isl_mat_alloc(bmap->ctx, bmap->n_eq, + isl_basic_map_total_dim(bmap) + 1); + if (!mat) + return NULL; + for (i = 0; i < bmap->n_eq; ++i) + for (j = 0, pos = 0; j < 5; ++j) { + int off = isl_basic_map_offset(bmap, c[j]); + for (k = 0; k < isl_basic_map_dim(bmap, c[j]); ++k) { + isl_int_set(mat->row[i][pos], + bmap->eq[i][off + k]); + ++pos; + } + } + + return mat; +} + +__isl_give isl_mat *isl_basic_map_inequalities_matrix( + __isl_keep isl_basic_map *bmap, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5) +{ + enum isl_dim_type c[5] = { c1, c2, c3, c4, c5 }; + struct isl_mat *mat; + int i, j, k; + int pos; + + if (!bmap) + return NULL; + mat = isl_mat_alloc(bmap->ctx, bmap->n_ineq, + isl_basic_map_total_dim(bmap) + 1); + if (!mat) + return NULL; + for (i = 0; i < bmap->n_ineq; ++i) + for (j = 0, pos = 0; j < 5; ++j) { + int off = isl_basic_map_offset(bmap, c[j]); + for (k = 0; k < isl_basic_map_dim(bmap, c[j]); ++k) { + isl_int_set(mat->row[i][pos], + bmap->ineq[i][off + k]); + ++pos; + } + } + + return mat; +} + +__isl_give isl_basic_map *isl_basic_map_from_constraint_matrices( + __isl_take isl_space *dim, + __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, + enum isl_dim_type c4, enum isl_dim_type c5) +{ + enum isl_dim_type c[5] = { c1, c2, c3, c4, c5 }; + isl_basic_map *bmap; + unsigned total; + unsigned extra; + int i, j, k, l; + int pos; + + if (!dim || !eq || !ineq) + goto error; + + if (eq->n_col != ineq->n_col) + isl_die(dim->ctx, isl_error_invalid, + "equalities and inequalities matrices should have " + "same number of columns", goto error); + + total = 1 + isl_space_dim(dim, isl_dim_all); + + if (eq->n_col < total) + isl_die(dim->ctx, isl_error_invalid, + "number of columns too small", goto error); + + extra = eq->n_col - total; + + bmap = isl_basic_map_alloc_space(isl_space_copy(dim), extra, + eq->n_row, ineq->n_row); + if (!bmap) + goto error; + for (i = 0; i < extra; ++i) { + k = isl_basic_map_alloc_div(bmap); + if (k < 0) + goto error; + isl_int_set_si(bmap->div[k][0], 0); + } + for (i = 0; i < eq->n_row; ++i) { + l = isl_basic_map_alloc_equality(bmap); + if (l < 0) + goto error; + for (j = 0, pos = 0; j < 5; ++j) { + int off = isl_basic_map_offset(bmap, c[j]); + for (k = 0; k < isl_basic_map_dim(bmap, c[j]); ++k) { + isl_int_set(bmap->eq[l][off + k], + eq->row[i][pos]); + ++pos; + } + } + } + for (i = 0; i < ineq->n_row; ++i) { + l = isl_basic_map_alloc_inequality(bmap); + if (l < 0) + goto error; + for (j = 0, pos = 0; j < 5; ++j) { + int off = isl_basic_map_offset(bmap, c[j]); + for (k = 0; k < isl_basic_map_dim(bmap, c[j]); ++k) { + isl_int_set(bmap->ineq[l][off + k], + ineq->row[i][pos]); + ++pos; + } + } + } + + isl_space_free(dim); + isl_mat_free(eq); + isl_mat_free(ineq); + + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_space_free(dim); + isl_mat_free(eq); + isl_mat_free(ineq); + return NULL; +} + +__isl_give isl_mat *isl_basic_set_equalities_matrix( + __isl_keep isl_basic_set *bset, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4) +{ + return isl_basic_map_equalities_matrix((isl_basic_map *)bset, + c1, c2, c3, c4, isl_dim_in); +} + +__isl_give isl_mat *isl_basic_set_inequalities_matrix( + __isl_keep isl_basic_set *bset, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4) +{ + return isl_basic_map_inequalities_matrix((isl_basic_map *)bset, + c1, c2, c3, c4, isl_dim_in); +} + +__isl_give isl_basic_set *isl_basic_set_from_constraint_matrices( + __isl_take isl_space *dim, + __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1, + enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4) +{ + return (isl_basic_set*) + isl_basic_map_from_constraint_matrices(dim, eq, ineq, + c1, c2, c3, c4, isl_dim_in); +} + +isl_bool isl_basic_map_can_zip(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return isl_bool_error; + + return isl_space_can_zip(bmap->dim); +} + +isl_bool isl_map_can_zip(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + + return isl_space_can_zip(map->dim); +} + +/* Given a basic map (A -> B) -> (C -> D), return the corresponding basic map + * (A -> C) -> (B -> D). + */ +__isl_give isl_basic_map *isl_basic_map_zip(__isl_take isl_basic_map *bmap) +{ + unsigned pos; + unsigned n1; + unsigned n2; + + if (!bmap) + return NULL; + + if (!isl_basic_map_can_zip(bmap)) + isl_die(bmap->ctx, isl_error_invalid, + "basic map cannot be zipped", goto error); + pos = isl_basic_map_offset(bmap, isl_dim_in) + + isl_space_dim(bmap->dim->nested[0], isl_dim_in); + n1 = isl_space_dim(bmap->dim->nested[0], isl_dim_out); + n2 = isl_space_dim(bmap->dim->nested[1], isl_dim_in); + bmap = isl_basic_map_cow(bmap); + bmap = isl_basic_map_swap_vars(bmap, pos, n1, n2); + if (!bmap) + return NULL; + bmap->dim = isl_space_zip(bmap->dim); + if (!bmap->dim) + goto error; + bmap = isl_basic_map_mark_final(bmap); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Given a map (A -> B) -> (C -> D), return the corresponding map + * (A -> C) -> (B -> D). + */ +__isl_give isl_map *isl_map_zip(__isl_take isl_map *map) +{ + int i; + + if (!map) + return NULL; + + if (!isl_map_can_zip(map)) + isl_die(map->ctx, isl_error_invalid, "map cannot be zipped", + goto error); + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_zip(map->p[i]); + if (!map->p[i]) + goto error; + } + + map->dim = isl_space_zip(map->dim); + if (!map->dim) + goto error; + + return map; +error: + isl_map_free(map); + return NULL; +} + +/* Can we apply isl_basic_map_curry to "bmap"? + * That is, does it have a nested relation in its domain? + */ +isl_bool isl_basic_map_can_curry(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return isl_bool_error; + + return isl_space_can_curry(bmap->dim); +} + +/* Can we apply isl_map_curry to "map"? + * That is, does it have a nested relation in its domain? + */ +isl_bool isl_map_can_curry(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + + return isl_space_can_curry(map->dim); +} + +/* Given a basic map (A -> B) -> C, return the corresponding basic map + * A -> (B -> C). + */ +__isl_give isl_basic_map *isl_basic_map_curry(__isl_take isl_basic_map *bmap) +{ + + if (!bmap) + return NULL; + + if (!isl_basic_map_can_curry(bmap)) + isl_die(bmap->ctx, isl_error_invalid, + "basic map cannot be curried", goto error); + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + bmap->dim = isl_space_curry(bmap->dim); + if (!bmap->dim) + goto error; + bmap = isl_basic_map_mark_final(bmap); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Given a map (A -> B) -> C, return the corresponding map + * A -> (B -> C). + */ +__isl_give isl_map *isl_map_curry(__isl_take isl_map *map) +{ + return isl_map_change_space(map, &isl_map_can_curry, + "map cannot be curried", &isl_space_curry); +} + +/* Can isl_map_range_curry be applied to "map"? + * That is, does it have a nested relation in its range, + * the domain of which is itself a nested relation? + */ +isl_bool isl_map_can_range_curry(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + + return isl_space_can_range_curry(map->dim); +} + +/* Given a map A -> ((B -> C) -> D), return the corresponding map + * A -> (B -> (C -> D)). + */ +__isl_give isl_map *isl_map_range_curry(__isl_take isl_map *map) +{ + return isl_map_change_space(map, &isl_map_can_range_curry, + "map range cannot be curried", + &isl_space_range_curry); +} + +/* Can we apply isl_basic_map_uncurry to "bmap"? + * That is, does it have a nested relation in its domain? + */ +isl_bool isl_basic_map_can_uncurry(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return isl_bool_error; + + return isl_space_can_uncurry(bmap->dim); +} + +/* Can we apply isl_map_uncurry to "map"? + * That is, does it have a nested relation in its domain? + */ +isl_bool isl_map_can_uncurry(__isl_keep isl_map *map) +{ + if (!map) + return isl_bool_error; + + return isl_space_can_uncurry(map->dim); +} + +/* Given a basic map A -> (B -> C), return the corresponding basic map + * (A -> B) -> C. + */ +__isl_give isl_basic_map *isl_basic_map_uncurry(__isl_take isl_basic_map *bmap) +{ + + if (!bmap) + return NULL; + + if (!isl_basic_map_can_uncurry(bmap)) + isl_die(bmap->ctx, isl_error_invalid, + "basic map cannot be uncurried", + return isl_basic_map_free(bmap)); + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + bmap->dim = isl_space_uncurry(bmap->dim); + if (!bmap->dim) + return isl_basic_map_free(bmap); + bmap = isl_basic_map_mark_final(bmap); + return bmap; +} + +/* Given a map A -> (B -> C), return the corresponding map + * (A -> B) -> C. + */ +__isl_give isl_map *isl_map_uncurry(__isl_take isl_map *map) +{ + return isl_map_change_space(map, &isl_map_can_uncurry, + "map cannot be uncurried", &isl_space_uncurry); +} + +/* Construct a basic map mapping the domain of the affine expression + * to a one-dimensional range prescribed by the affine expression. + * + * A NaN affine expression cannot be converted to a basic map. + */ +__isl_give isl_basic_map *isl_basic_map_from_aff(__isl_take isl_aff *aff) +{ + int k; + int pos; + isl_bool is_nan; + isl_local_space *ls; + isl_basic_map *bmap = NULL; + + if (!aff) + return NULL; + is_nan = isl_aff_is_nan(aff); + if (is_nan < 0) + goto error; + if (is_nan) + isl_die(isl_aff_get_ctx(aff), isl_error_invalid, + "cannot convert NaN", goto error); + + ls = isl_aff_get_local_space(aff); + bmap = isl_basic_map_from_local_space(ls); + bmap = isl_basic_map_extend_constraints(bmap, 1, 0); + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + + pos = isl_basic_map_offset(bmap, isl_dim_out); + isl_seq_cpy(bmap->eq[k], aff->v->el + 1, pos); + isl_int_neg(bmap->eq[k][pos], aff->v->el[0]); + isl_seq_cpy(bmap->eq[k] + pos + 1, aff->v->el + 1 + pos, + aff->v->size - (pos + 1)); + + isl_aff_free(aff); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_aff_free(aff); + isl_basic_map_free(bmap); + return NULL; +} + +/* Construct a map mapping the domain of the affine expression + * to a one-dimensional range prescribed by the affine expression. + */ +__isl_give isl_map *isl_map_from_aff(__isl_take isl_aff *aff) +{ + isl_basic_map *bmap; + + bmap = isl_basic_map_from_aff(aff); + return isl_map_from_basic_map(bmap); +} + +/* Construct a basic map mapping the domain the multi-affine expression + * to its range, with each dimension in the range equated to the + * corresponding affine expression. + */ +__isl_give isl_basic_map *isl_basic_map_from_multi_aff( + __isl_take isl_multi_aff *maff) +{ + int i; + isl_space *space; + isl_basic_map *bmap; + + if (!maff) + return NULL; + + if (isl_space_dim(maff->space, isl_dim_out) != maff->n) + isl_die(isl_multi_aff_get_ctx(maff), isl_error_internal, + "invalid space", goto error); + + space = isl_space_domain(isl_multi_aff_get_space(maff)); + bmap = isl_basic_map_universe(isl_space_from_domain(space)); + + for (i = 0; i < maff->n; ++i) { + isl_aff *aff; + isl_basic_map *bmap_i; + + aff = isl_aff_copy(maff->p[i]); + bmap_i = isl_basic_map_from_aff(aff); + + bmap = isl_basic_map_flat_range_product(bmap, bmap_i); + } + + bmap = isl_basic_map_reset_space(bmap, isl_multi_aff_get_space(maff)); + + isl_multi_aff_free(maff); + return bmap; +error: + isl_multi_aff_free(maff); + return NULL; +} + +/* Construct a map mapping the domain the multi-affine expression + * to its range, with each dimension in the range equated to the + * corresponding affine expression. + */ +__isl_give isl_map *isl_map_from_multi_aff(__isl_take isl_multi_aff *maff) +{ + isl_basic_map *bmap; + + bmap = isl_basic_map_from_multi_aff(maff); + return isl_map_from_basic_map(bmap); +} + +/* Construct a basic map mapping a domain in the given space to + * to an n-dimensional range, with n the number of elements in the list, + * where each coordinate in the range is prescribed by the + * corresponding affine expression. + * The domains of all affine expressions in the list are assumed to match + * domain_dim. + */ +__isl_give isl_basic_map *isl_basic_map_from_aff_list( + __isl_take isl_space *domain_dim, __isl_take isl_aff_list *list) +{ + int i; + isl_space *dim; + isl_basic_map *bmap; + + if (!list) + return NULL; + + dim = isl_space_from_domain(domain_dim); + bmap = isl_basic_map_universe(dim); + + for (i = 0; i < list->n; ++i) { + isl_aff *aff; + isl_basic_map *bmap_i; + + aff = isl_aff_copy(list->p[i]); + bmap_i = isl_basic_map_from_aff(aff); + + bmap = isl_basic_map_flat_range_product(bmap, bmap_i); + } + + isl_aff_list_free(list); + return bmap; +} + +__isl_give isl_set *isl_set_equate(__isl_take isl_set *set, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + return isl_map_equate(set, type1, pos1, type2, pos2); +} + +/* Construct a basic map where the given dimensions are equal to each other. + */ +static __isl_give isl_basic_map *equator(__isl_take isl_space *space, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *bmap = NULL; + int i; + + if (!space) + return NULL; + + if (pos1 >= isl_space_dim(space, type1)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "index out of bounds", goto error); + if (pos2 >= isl_space_dim(space, type2)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "index out of bounds", goto error); + + if (type1 == type2 && pos1 == pos2) + return isl_basic_map_universe(space); + + bmap = isl_basic_map_alloc_space(isl_space_copy(space), 0, 1, 0); + i = isl_basic_map_alloc_equality(bmap); + if (i < 0) + goto error; + isl_seq_clr(bmap->eq[i], 1 + isl_basic_map_total_dim(bmap)); + pos1 += isl_basic_map_offset(bmap, type1); + pos2 += isl_basic_map_offset(bmap, type2); + isl_int_set_si(bmap->eq[i][pos1], -1); + isl_int_set_si(bmap->eq[i][pos2], 1); + bmap = isl_basic_map_finalize(bmap); + isl_space_free(space); + return bmap; +error: + isl_space_free(space); + isl_basic_map_free(bmap); + return NULL; +} + +/* Add a constraint imposing that the given two dimensions are equal. + */ +__isl_give isl_basic_map *isl_basic_map_equate(__isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *eq; + + eq = equator(isl_basic_map_get_space(bmap), type1, pos1, type2, pos2); + + bmap = isl_basic_map_intersect(bmap, eq); + + return bmap; +} + +/* Add a constraint imposing that the given two dimensions are equal. + */ +__isl_give isl_map *isl_map_equate(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *bmap; + + bmap = equator(isl_map_get_space(map), type1, pos1, type2, pos2); + + map = isl_map_intersect(map, isl_map_from_basic_map(bmap)); + + return map; +} + +/* Add a constraint imposing that the given two dimensions have opposite values. + */ +__isl_give isl_map *isl_map_oppose(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *bmap = NULL; + int i; + + if (!map) + return NULL; + + if (pos1 >= isl_map_dim(map, type1)) + isl_die(map->ctx, isl_error_invalid, + "index out of bounds", goto error); + if (pos2 >= isl_map_dim(map, type2)) + isl_die(map->ctx, isl_error_invalid, + "index out of bounds", goto error); + + bmap = isl_basic_map_alloc_space(isl_map_get_space(map), 0, 1, 0); + i = isl_basic_map_alloc_equality(bmap); + if (i < 0) + goto error; + isl_seq_clr(bmap->eq[i], 1 + isl_basic_map_total_dim(bmap)); + pos1 += isl_basic_map_offset(bmap, type1); + pos2 += isl_basic_map_offset(bmap, type2); + isl_int_set_si(bmap->eq[i][pos1], 1); + isl_int_set_si(bmap->eq[i][pos2], 1); + bmap = isl_basic_map_finalize(bmap); + + map = isl_map_intersect(map, isl_map_from_basic_map(bmap)); + + return map; +error: + isl_basic_map_free(bmap); + isl_map_free(map); + return NULL; +} + +/* Construct a constraint imposing that the value of the first dimension is + * greater than or equal to that of the second. + */ +static __isl_give isl_constraint *constraint_order_ge( + __isl_take isl_space *space, enum isl_dim_type type1, int pos1, + enum isl_dim_type type2, int pos2) +{ + isl_constraint *c; + + if (!space) + return NULL; + + c = isl_constraint_alloc_inequality(isl_local_space_from_space(space)); + + if (pos1 >= isl_constraint_dim(c, type1)) + isl_die(isl_constraint_get_ctx(c), isl_error_invalid, + "index out of bounds", return isl_constraint_free(c)); + if (pos2 >= isl_constraint_dim(c, type2)) + isl_die(isl_constraint_get_ctx(c), isl_error_invalid, + "index out of bounds", return isl_constraint_free(c)); + + if (type1 == type2 && pos1 == pos2) + return c; + + c = isl_constraint_set_coefficient_si(c, type1, pos1, 1); + c = isl_constraint_set_coefficient_si(c, type2, pos2, -1); + + return c; +} + +/* Add a constraint imposing that the value of the first dimension is + * greater than or equal to that of the second. + */ +__isl_give isl_basic_map *isl_basic_map_order_ge(__isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_constraint *c; + isl_space *space; + + if (type1 == type2 && pos1 == pos2) + return bmap; + space = isl_basic_map_get_space(bmap); + c = constraint_order_ge(space, type1, pos1, type2, pos2); + bmap = isl_basic_map_add_constraint(bmap, c); + + return bmap; +} + +/* Add a constraint imposing that the value of the first dimension is + * greater than or equal to that of the second. + */ +__isl_give isl_map *isl_map_order_ge(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_constraint *c; + isl_space *space; + + if (type1 == type2 && pos1 == pos2) + return map; + space = isl_map_get_space(map); + c = constraint_order_ge(space, type1, pos1, type2, pos2); + map = isl_map_add_constraint(map, c); + + return map; +} + +/* Add a constraint imposing that the value of the first dimension is + * less than or equal to that of the second. + */ +__isl_give isl_map *isl_map_order_le(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + return isl_map_order_ge(map, type2, pos2, type1, pos1); +} + +/* Construct a basic map where the value of the first dimension is + * greater than that of the second. + */ +static __isl_give isl_basic_map *greator(__isl_take isl_space *space, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *bmap = NULL; + int i; + + if (!space) + return NULL; + + if (pos1 >= isl_space_dim(space, type1)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "index out of bounds", goto error); + if (pos2 >= isl_space_dim(space, type2)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "index out of bounds", goto error); + + if (type1 == type2 && pos1 == pos2) + return isl_basic_map_empty(space); + + bmap = isl_basic_map_alloc_space(space, 0, 0, 1); + i = isl_basic_map_alloc_inequality(bmap); + if (i < 0) + return isl_basic_map_free(bmap); + isl_seq_clr(bmap->ineq[i], 1 + isl_basic_map_total_dim(bmap)); + pos1 += isl_basic_map_offset(bmap, type1); + pos2 += isl_basic_map_offset(bmap, type2); + isl_int_set_si(bmap->ineq[i][pos1], 1); + isl_int_set_si(bmap->ineq[i][pos2], -1); + isl_int_set_si(bmap->ineq[i][0], -1); + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_space_free(space); + isl_basic_map_free(bmap); + return NULL; +} + +/* Add a constraint imposing that the value of the first dimension is + * greater than that of the second. + */ +__isl_give isl_basic_map *isl_basic_map_order_gt(__isl_take isl_basic_map *bmap, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *gt; + + gt = greator(isl_basic_map_get_space(bmap), type1, pos1, type2, pos2); + + bmap = isl_basic_map_intersect(bmap, gt); + + return bmap; +} + +/* Add a constraint imposing that the value of the first dimension is + * greater than that of the second. + */ +__isl_give isl_map *isl_map_order_gt(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + isl_basic_map *bmap; + + bmap = greator(isl_map_get_space(map), type1, pos1, type2, pos2); + + map = isl_map_intersect(map, isl_map_from_basic_map(bmap)); + + return map; +} + +/* Add a constraint imposing that the value of the first dimension is + * smaller than that of the second. + */ +__isl_give isl_map *isl_map_order_lt(__isl_take isl_map *map, + enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2) +{ + return isl_map_order_gt(map, type2, pos2, type1, pos1); +} + +__isl_give isl_aff *isl_basic_map_get_div(__isl_keep isl_basic_map *bmap, + int pos) +{ + isl_aff *div; + isl_local_space *ls; + + if (!bmap) + return NULL; + + if (!isl_basic_map_divs_known(bmap)) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "some divs are unknown", return NULL); + + ls = isl_basic_map_get_local_space(bmap); + div = isl_local_space_get_div(ls, pos); + isl_local_space_free(ls); + + return div; +} + +__isl_give isl_aff *isl_basic_set_get_div(__isl_keep isl_basic_set *bset, + int pos) +{ + return isl_basic_map_get_div(bset, pos); +} + +/* Plug in "subs" for dimension "type", "pos" of "bset". + * + * Let i be the dimension to replace and let "subs" be of the form + * + * f/d + * + * Any integer division with a non-zero coefficient for i, + * + * floor((a i + g)/m) + * + * is replaced by + * + * floor((a f + d g)/(m d)) + * + * Constraints of the form + * + * a i + g + * + * are replaced by + * + * a f + d g + * + * We currently require that "subs" is an integral expression. + * Handling rational expressions may require us to add stride constraints + * as we do in isl_basic_set_preimage_multi_aff. + */ +__isl_give isl_basic_set *isl_basic_set_substitute( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs) +{ + int i; + isl_int v; + isl_ctx *ctx; + + if (bset && isl_basic_set_plain_is_empty(bset)) + return bset; + + bset = isl_basic_set_cow(bset); + if (!bset || !subs) + goto error; + + ctx = isl_basic_set_get_ctx(bset); + if (!isl_space_is_equal(bset->dim, subs->ls->dim)) + isl_die(ctx, isl_error_invalid, + "spaces don't match", goto error); + if (isl_local_space_dim(subs->ls, isl_dim_div) != 0) + isl_die(ctx, isl_error_unsupported, + "cannot handle divs yet", goto error); + if (!isl_int_is_one(subs->v->el[0])) + isl_die(ctx, isl_error_invalid, + "can only substitute integer expressions", goto error); + + pos += isl_basic_set_offset(bset, type); + + isl_int_init(v); + + for (i = 0; i < bset->n_eq; ++i) { + if (isl_int_is_zero(bset->eq[i][pos])) + continue; + isl_int_set(v, bset->eq[i][pos]); + isl_int_set_si(bset->eq[i][pos], 0); + isl_seq_combine(bset->eq[i], subs->v->el[0], bset->eq[i], + v, subs->v->el + 1, subs->v->size - 1); + } + + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_int_is_zero(bset->ineq[i][pos])) + continue; + isl_int_set(v, bset->ineq[i][pos]); + isl_int_set_si(bset->ineq[i][pos], 0); + isl_seq_combine(bset->ineq[i], subs->v->el[0], bset->ineq[i], + v, subs->v->el + 1, subs->v->size - 1); + } + + for (i = 0; i < bset->n_div; ++i) { + if (isl_int_is_zero(bset->div[i][1 + pos])) + continue; + isl_int_set(v, bset->div[i][1 + pos]); + isl_int_set_si(bset->div[i][1 + pos], 0); + isl_seq_combine(bset->div[i] + 1, + subs->v->el[0], bset->div[i] + 1, + v, subs->v->el + 1, subs->v->size - 1); + isl_int_mul(bset->div[i][0], bset->div[i][0], subs->v->el[0]); + } + + isl_int_clear(v); + + bset = isl_basic_set_simplify(bset); + return isl_basic_set_finalize(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Plug in "subs" for dimension "type", "pos" of "set". + */ +__isl_give isl_set *isl_set_substitute(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs) +{ + int i; + + if (set && isl_set_plain_is_empty(set)) + return set; + + set = isl_set_cow(set); + if (!set || !subs) + goto error; + + for (i = set->n - 1; i >= 0; --i) { + set->p[i] = isl_basic_set_substitute(set->p[i], type, pos, subs); + if (remove_if_empty(set, i) < 0) + goto error; + } + + return set; +error: + isl_set_free(set); + return NULL; +} + +/* Check if the range of "ma" is compatible with the domain or range + * (depending on "type") of "bmap". + * Return -1 if anything is wrong. + */ +static int check_basic_map_compatible_range_multi_aff( + __isl_keep isl_basic_map *bmap, enum isl_dim_type type, + __isl_keep isl_multi_aff *ma) +{ + int m; + isl_space *ma_space; + + ma_space = isl_multi_aff_get_space(ma); + + m = isl_space_match(bmap->dim, isl_dim_param, ma_space, isl_dim_param); + if (m < 0) + goto error; + if (!m) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "parameters don't match", goto error); + m = isl_space_tuple_is_equal(bmap->dim, type, ma_space, isl_dim_out); + if (m < 0) + goto error; + if (!m) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "spaces don't match", goto error); + + isl_space_free(ma_space); + return m; +error: + isl_space_free(ma_space); + return -1; +} + +/* Copy the divs from "ma" to "bmap", adding zeros for the "n_before" + * coefficients before the transformed range of dimensions, + * the "n_after" coefficients after the transformed range of dimensions + * and the coefficients of the other divs in "bmap". + */ +static int set_ma_divs(__isl_keep isl_basic_map *bmap, + __isl_keep isl_multi_aff *ma, int n_before, int n_after, int n_div) +{ + int i; + int n_param; + int n_set; + isl_local_space *ls; + + if (n_div == 0) + return 0; + + ls = isl_aff_get_domain_local_space(ma->p[0]); + if (!ls) + return -1; + + n_param = isl_local_space_dim(ls, isl_dim_param); + n_set = isl_local_space_dim(ls, isl_dim_set); + for (i = 0; i < n_div; ++i) { + int o_bmap = 0, o_ls = 0; + + isl_seq_cpy(bmap->div[i], ls->div->row[i], 1 + 1 + n_param); + o_bmap += 1 + 1 + n_param; + o_ls += 1 + 1 + n_param; + isl_seq_clr(bmap->div[i] + o_bmap, n_before); + o_bmap += n_before; + isl_seq_cpy(bmap->div[i] + o_bmap, + ls->div->row[i] + o_ls, n_set); + o_bmap += n_set; + o_ls += n_set; + isl_seq_clr(bmap->div[i] + o_bmap, n_after); + o_bmap += n_after; + isl_seq_cpy(bmap->div[i] + o_bmap, + ls->div->row[i] + o_ls, n_div); + o_bmap += n_div; + o_ls += n_div; + isl_seq_clr(bmap->div[i] + o_bmap, bmap->n_div - n_div); + if (isl_basic_set_add_div_constraints(bmap, i) < 0) + goto error; + } + + isl_local_space_free(ls); + return 0; +error: + isl_local_space_free(ls); + return -1; +} + +/* How many stride constraints does "ma" enforce? + * That is, how many of the affine expressions have a denominator + * different from one? + */ +static int multi_aff_strides(__isl_keep isl_multi_aff *ma) +{ + int i; + int strides = 0; + + for (i = 0; i < ma->n; ++i) + if (!isl_int_is_one(ma->p[i]->v->el[0])) + strides++; + + return strides; +} + +/* For each affine expression in ma of the form + * + * x_i = (f_i y + h_i)/m_i + * + * with m_i different from one, add a constraint to "bmap" + * of the form + * + * f_i y + h_i = m_i alpha_i + * + * with alpha_i an additional existentially quantified variable. + * + * The input variables of "ma" correspond to a subset of the variables + * of "bmap". There are "n_before" variables in "bmap" before this + * subset and "n_after" variables after this subset. + * The integer divisions of the affine expressions in "ma" are assumed + * to have been aligned. There are "n_div_ma" of them and + * they appear first in "bmap", straight after the "n_after" variables. + */ +static __isl_give isl_basic_map *add_ma_strides( + __isl_take isl_basic_map *bmap, __isl_keep isl_multi_aff *ma, + int n_before, int n_after, int n_div_ma) +{ + int i, k; + int div; + int total; + int n_param; + int n_in; + + total = isl_basic_map_total_dim(bmap); + n_param = isl_multi_aff_dim(ma, isl_dim_param); + n_in = isl_multi_aff_dim(ma, isl_dim_in); + for (i = 0; i < ma->n; ++i) { + int o_bmap = 0, o_ma = 1; + + if (isl_int_is_one(ma->p[i]->v->el[0])) + continue; + div = isl_basic_map_alloc_div(bmap); + k = isl_basic_map_alloc_equality(bmap); + if (div < 0 || k < 0) + goto error; + isl_int_set_si(bmap->div[div][0], 0); + isl_seq_cpy(bmap->eq[k] + o_bmap, + ma->p[i]->v->el + o_ma, 1 + n_param); + o_bmap += 1 + n_param; + o_ma += 1 + n_param; + isl_seq_clr(bmap->eq[k] + o_bmap, n_before); + o_bmap += n_before; + isl_seq_cpy(bmap->eq[k] + o_bmap, + ma->p[i]->v->el + o_ma, n_in); + o_bmap += n_in; + o_ma += n_in; + isl_seq_clr(bmap->eq[k] + o_bmap, n_after); + o_bmap += n_after; + isl_seq_cpy(bmap->eq[k] + o_bmap, + ma->p[i]->v->el + o_ma, n_div_ma); + o_bmap += n_div_ma; + o_ma += n_div_ma; + isl_seq_clr(bmap->eq[k] + o_bmap, 1 + total - o_bmap); + isl_int_neg(bmap->eq[k][1 + total], ma->p[i]->v->el[0]); + total++; + } + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Replace the domain or range space (depending on "type) of "space" by "set". + */ +static __isl_give isl_space *isl_space_set(__isl_take isl_space *space, + enum isl_dim_type type, __isl_take isl_space *set) +{ + if (type == isl_dim_in) { + space = isl_space_range(space); + space = isl_space_map_from_domain_and_range(set, space); + } else { + space = isl_space_domain(space); + space = isl_space_map_from_domain_and_range(space, set); + } + + return space; +} + +/* Compute the preimage of the domain or range (depending on "type") + * of "bmap" under the function represented by "ma". + * In other words, plug in "ma" in the domain or range of "bmap". + * The result is a basic map that lives in the same space as "bmap" + * except that the domain or range has been replaced by + * the domain space of "ma". + * + * If bmap is represented by + * + * A(p) + S u + B x + T v + C(divs) >= 0, + * + * where u and x are input and output dimensions if type == isl_dim_out + * while x and v are input and output dimensions if type == isl_dim_in, + * and ma is represented by + * + * x = D(p) + F(y) + G(divs') + * + * then the result is + * + * A(p) + B D(p) + S u + B F(y) + T v + B G(divs') + C(divs) >= 0 + * + * The divs in the input set are similarly adjusted. + * In particular + * + * floor((a_i(p) + s u + b_i x + t v + c_i(divs))/n_i) + * + * becomes + * + * floor((a_i(p) + b_i D(p) + s u + b_i F(y) + t v + + * B_i G(divs') + c_i(divs))/n_i) + * + * If bmap is not a rational map and if F(y) involves any denominators + * + * x_i = (f_i y + h_i)/m_i + * + * then additional constraints are added to ensure that we only + * map back integer points. That is we enforce + * + * f_i y + h_i = m_i alpha_i + * + * with alpha_i an additional existentially quantified variable. + * + * We first copy over the divs from "ma". + * Then we add the modified constraints and divs from "bmap". + * Finally, we add the stride constraints, if needed. + */ +__isl_give isl_basic_map *isl_basic_map_preimage_multi_aff( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, + __isl_take isl_multi_aff *ma) +{ + int i, k; + isl_space *space; + isl_basic_map *res = NULL; + int n_before, n_after, n_div_bmap, n_div_ma; + isl_int f, c1, c2, g; + int rational, strides; + + isl_int_init(f); + isl_int_init(c1); + isl_int_init(c2); + isl_int_init(g); + + ma = isl_multi_aff_align_divs(ma); + if (!bmap || !ma) + goto error; + if (check_basic_map_compatible_range_multi_aff(bmap, type, ma) < 0) + goto error; + + if (type == isl_dim_in) { + n_before = 0; + n_after = isl_basic_map_dim(bmap, isl_dim_out); + } else { + n_before = isl_basic_map_dim(bmap, isl_dim_in); + n_after = 0; + } + n_div_bmap = isl_basic_map_dim(bmap, isl_dim_div); + n_div_ma = ma->n ? isl_aff_dim(ma->p[0], isl_dim_div) : 0; + + space = isl_multi_aff_get_domain_space(ma); + space = isl_space_set(isl_basic_map_get_space(bmap), type, space); + rational = isl_basic_map_is_rational(bmap); + strides = rational ? 0 : multi_aff_strides(ma); + res = isl_basic_map_alloc_space(space, n_div_ma + n_div_bmap + strides, + bmap->n_eq + strides, bmap->n_ineq + 2 * n_div_ma); + if (rational) + res = isl_basic_map_set_rational(res); + + for (i = 0; i < n_div_ma + n_div_bmap; ++i) + if (isl_basic_map_alloc_div(res) < 0) + goto error; + + if (set_ma_divs(res, ma, n_before, n_after, n_div_ma) < 0) + goto error; + + for (i = 0; i < bmap->n_eq; ++i) { + k = isl_basic_map_alloc_equality(res); + if (k < 0) + goto error; + isl_seq_preimage(res->eq[k], bmap->eq[i], ma, n_before, + n_after, n_div_ma, n_div_bmap, f, c1, c2, g, 0); + } + + for (i = 0; i < bmap->n_ineq; ++i) { + k = isl_basic_map_alloc_inequality(res); + if (k < 0) + goto error; + isl_seq_preimage(res->ineq[k], bmap->ineq[i], ma, n_before, + n_after, n_div_ma, n_div_bmap, f, c1, c2, g, 0); + } + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) { + isl_int_set_si(res->div[n_div_ma + i][0], 0); + continue; + } + isl_seq_preimage(res->div[n_div_ma + i], bmap->div[i], ma, + n_before, n_after, n_div_ma, n_div_bmap, + f, c1, c2, g, 1); + } + + if (strides) + res = add_ma_strides(res, ma, n_before, n_after, n_div_ma); + + isl_int_clear(f); + isl_int_clear(c1); + isl_int_clear(c2); + isl_int_clear(g); + isl_basic_map_free(bmap); + isl_multi_aff_free(ma); + res = isl_basic_set_simplify(res); + return isl_basic_map_finalize(res); +error: + isl_int_clear(f); + isl_int_clear(c1); + isl_int_clear(c2); + isl_int_clear(g); + isl_basic_map_free(bmap); + isl_multi_aff_free(ma); + isl_basic_map_free(res); + return NULL; +} + +/* Compute the preimage of "bset" under the function represented by "ma". + * In other words, plug in "ma" in "bset". The result is a basic set + * that lives in the domain space of "ma". + */ +__isl_give isl_basic_set *isl_basic_set_preimage_multi_aff( + __isl_take isl_basic_set *bset, __isl_take isl_multi_aff *ma) +{ + return isl_basic_map_preimage_multi_aff(bset, isl_dim_set, ma); +} + +/* Compute the preimage of the domain of "bmap" under the function + * represented by "ma". + * In other words, plug in "ma" in the domain of "bmap". + * The result is a basic map that lives in the same space as "bmap" + * except that the domain has been replaced by the domain space of "ma". + */ +__isl_give isl_basic_map *isl_basic_map_preimage_domain_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma) +{ + return isl_basic_map_preimage_multi_aff(bmap, isl_dim_in, ma); +} + +/* Compute the preimage of the range of "bmap" under the function + * represented by "ma". + * In other words, plug in "ma" in the range of "bmap". + * The result is a basic map that lives in the same space as "bmap" + * except that the range has been replaced by the domain space of "ma". + */ +__isl_give isl_basic_map *isl_basic_map_preimage_range_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma) +{ + return isl_basic_map_preimage_multi_aff(bmap, isl_dim_out, ma); +} + +/* Check if the range of "ma" is compatible with the domain or range + * (depending on "type") of "map". + * Return -1 if anything is wrong. + */ +static int check_map_compatible_range_multi_aff( + __isl_keep isl_map *map, enum isl_dim_type type, + __isl_keep isl_multi_aff *ma) +{ + int m; + isl_space *ma_space; + + ma_space = isl_multi_aff_get_space(ma); + m = isl_space_tuple_is_equal(map->dim, type, ma_space, isl_dim_out); + isl_space_free(ma_space); + if (m >= 0 && !m) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "spaces don't match", return -1); + return m; +} + +/* Compute the preimage of the domain or range (depending on "type") + * of "map" under the function represented by "ma". + * In other words, plug in "ma" in the domain or range of "map". + * The result is a map that lives in the same space as "map" + * except that the domain or range has been replaced by + * the domain space of "ma". + * + * The parameters are assumed to have been aligned. + */ +static __isl_give isl_map *map_preimage_multi_aff(__isl_take isl_map *map, + enum isl_dim_type type, __isl_take isl_multi_aff *ma) +{ + int i; + isl_space *space; + + map = isl_map_cow(map); + ma = isl_multi_aff_align_divs(ma); + if (!map || !ma) + goto error; + if (check_map_compatible_range_multi_aff(map, type, ma) < 0) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_preimage_multi_aff(map->p[i], type, + isl_multi_aff_copy(ma)); + if (!map->p[i]) + goto error; + } + + space = isl_multi_aff_get_domain_space(ma); + space = isl_space_set(isl_map_get_space(map), type, space); + + isl_space_free(map->dim); + map->dim = space; + if (!map->dim) + goto error; + + isl_multi_aff_free(ma); + if (map->n > 1) + ISL_F_CLR(map, ISL_MAP_DISJOINT); + ISL_F_CLR(map, ISL_SET_NORMALIZED); + return map; +error: + isl_multi_aff_free(ma); + isl_map_free(map); + return NULL; +} + +/* Compute the preimage of the domain or range (depending on "type") + * of "map" under the function represented by "ma". + * In other words, plug in "ma" in the domain or range of "map". + * The result is a map that lives in the same space as "map" + * except that the domain or range has been replaced by + * the domain space of "ma". + */ +__isl_give isl_map *isl_map_preimage_multi_aff(__isl_take isl_map *map, + enum isl_dim_type type, __isl_take isl_multi_aff *ma) +{ + if (!map || !ma) + goto error; + + if (isl_space_match(map->dim, isl_dim_param, ma->space, isl_dim_param)) + return map_preimage_multi_aff(map, type, ma); + + if (!isl_space_has_named_params(map->dim) || + !isl_space_has_named_params(ma->space)) + isl_die(map->ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + map = isl_map_align_params(map, isl_multi_aff_get_space(ma)); + ma = isl_multi_aff_align_params(ma, isl_map_get_space(map)); + + return map_preimage_multi_aff(map, type, ma); +error: + isl_multi_aff_free(ma); + return isl_map_free(map); +} + +/* Compute the preimage of "set" under the function represented by "ma". + * In other words, plug in "ma" in "set". The result is a set + * that lives in the domain space of "ma". + */ +__isl_give isl_set *isl_set_preimage_multi_aff(__isl_take isl_set *set, + __isl_take isl_multi_aff *ma) +{ + return isl_map_preimage_multi_aff(set, isl_dim_set, ma); +} + +/* Compute the preimage of the domain of "map" under the function + * represented by "ma". + * In other words, plug in "ma" in the domain of "map". + * The result is a map that lives in the same space as "map" + * except that the domain has been replaced by the domain space of "ma". + */ +__isl_give isl_map *isl_map_preimage_domain_multi_aff(__isl_take isl_map *map, + __isl_take isl_multi_aff *ma) +{ + return isl_map_preimage_multi_aff(map, isl_dim_in, ma); +} + +/* Compute the preimage of the range of "map" under the function + * represented by "ma". + * In other words, plug in "ma" in the range of "map". + * The result is a map that lives in the same space as "map" + * except that the range has been replaced by the domain space of "ma". + */ +__isl_give isl_map *isl_map_preimage_range_multi_aff(__isl_take isl_map *map, + __isl_take isl_multi_aff *ma) +{ + return isl_map_preimage_multi_aff(map, isl_dim_out, ma); +} + +/* Compute the preimage of "map" under the function represented by "pma". + * In other words, plug in "pma" in the domain or range of "map". + * The result is a map that lives in the same space as "map", + * except that the space of type "type" has been replaced by + * the domain space of "pma". + * + * The parameters of "map" and "pma" are assumed to have been aligned. + */ +static __isl_give isl_map *isl_map_preimage_pw_multi_aff_aligned( + __isl_take isl_map *map, enum isl_dim_type type, + __isl_take isl_pw_multi_aff *pma) +{ + int i; + isl_map *res; + + if (!pma) + goto error; + + if (pma->n == 0) { + isl_pw_multi_aff_free(pma); + res = isl_map_empty(isl_map_get_space(map)); + isl_map_free(map); + return res; + } + + res = isl_map_preimage_multi_aff(isl_map_copy(map), type, + isl_multi_aff_copy(pma->p[0].maff)); + if (type == isl_dim_in) + res = isl_map_intersect_domain(res, + isl_map_copy(pma->p[0].set)); + else + res = isl_map_intersect_range(res, + isl_map_copy(pma->p[0].set)); + + for (i = 1; i < pma->n; ++i) { + isl_map *res_i; + + res_i = isl_map_preimage_multi_aff(isl_map_copy(map), type, + isl_multi_aff_copy(pma->p[i].maff)); + if (type == isl_dim_in) + res_i = isl_map_intersect_domain(res_i, + isl_map_copy(pma->p[i].set)); + else + res_i = isl_map_intersect_range(res_i, + isl_map_copy(pma->p[i].set)); + res = isl_map_union(res, res_i); + } + + isl_pw_multi_aff_free(pma); + isl_map_free(map); + return res; +error: + isl_pw_multi_aff_free(pma); + isl_map_free(map); + return NULL; +} + +/* Compute the preimage of "map" under the function represented by "pma". + * In other words, plug in "pma" in the domain or range of "map". + * The result is a map that lives in the same space as "map", + * except that the space of type "type" has been replaced by + * the domain space of "pma". + */ +__isl_give isl_map *isl_map_preimage_pw_multi_aff(__isl_take isl_map *map, + enum isl_dim_type type, __isl_take isl_pw_multi_aff *pma) +{ + if (!map || !pma) + goto error; + + if (isl_space_match(map->dim, isl_dim_param, pma->dim, isl_dim_param)) + return isl_map_preimage_pw_multi_aff_aligned(map, type, pma); + + if (!isl_space_has_named_params(map->dim) || + !isl_space_has_named_params(pma->dim)) + isl_die(map->ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + map = isl_map_align_params(map, isl_pw_multi_aff_get_space(pma)); + pma = isl_pw_multi_aff_align_params(pma, isl_map_get_space(map)); + + return isl_map_preimage_pw_multi_aff_aligned(map, type, pma); +error: + isl_pw_multi_aff_free(pma); + return isl_map_free(map); +} + +/* Compute the preimage of "set" under the function represented by "pma". + * In other words, plug in "pma" in "set". The result is a set + * that lives in the domain space of "pma". + */ +__isl_give isl_set *isl_set_preimage_pw_multi_aff(__isl_take isl_set *set, + __isl_take isl_pw_multi_aff *pma) +{ + return isl_map_preimage_pw_multi_aff(set, isl_dim_set, pma); +} + +/* Compute the preimage of the domain of "map" under the function + * represented by "pma". + * In other words, plug in "pma" in the domain of "map". + * The result is a map that lives in the same space as "map", + * except that domain space has been replaced by the domain space of "pma". + */ +__isl_give isl_map *isl_map_preimage_domain_pw_multi_aff( + __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma) +{ + return isl_map_preimage_pw_multi_aff(map, isl_dim_in, pma); +} + +/* Compute the preimage of the range of "map" under the function + * represented by "pma". + * In other words, plug in "pma" in the range of "map". + * The result is a map that lives in the same space as "map", + * except that range space has been replaced by the domain space of "pma". + */ +__isl_give isl_map *isl_map_preimage_range_pw_multi_aff( + __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma) +{ + return isl_map_preimage_pw_multi_aff(map, isl_dim_out, pma); +} + +/* Compute the preimage of "map" under the function represented by "mpa". + * In other words, plug in "mpa" in the domain or range of "map". + * The result is a map that lives in the same space as "map", + * except that the space of type "type" has been replaced by + * the domain space of "mpa". + * + * If the map does not involve any constraints that refer to the + * dimensions of the substituted space, then the only possible + * effect of "mpa" on the map is to map the space to a different space. + * We create a separate isl_multi_aff to effectuate this change + * in order to avoid spurious splitting of the map along the pieces + * of "mpa". + */ +__isl_give isl_map *isl_map_preimage_multi_pw_aff(__isl_take isl_map *map, + enum isl_dim_type type, __isl_take isl_multi_pw_aff *mpa) +{ + int n; + isl_pw_multi_aff *pma; + + if (!map || !mpa) + goto error; + + n = isl_map_dim(map, type); + if (!isl_map_involves_dims(map, type, 0, n)) { + isl_space *space; + isl_multi_aff *ma; + + space = isl_multi_pw_aff_get_space(mpa); + isl_multi_pw_aff_free(mpa); + ma = isl_multi_aff_zero(space); + return isl_map_preimage_multi_aff(map, type, ma); + } + + pma = isl_pw_multi_aff_from_multi_pw_aff(mpa); + return isl_map_preimage_pw_multi_aff(map, type, pma); +error: + isl_map_free(map); + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Compute the preimage of "map" under the function represented by "mpa". + * In other words, plug in "mpa" in the domain "map". + * The result is a map that lives in the same space as "map", + * except that domain space has been replaced by the domain space of "mpa". + */ +__isl_give isl_map *isl_map_preimage_domain_multi_pw_aff( + __isl_take isl_map *map, __isl_take isl_multi_pw_aff *mpa) +{ + return isl_map_preimage_multi_pw_aff(map, isl_dim_in, mpa); +} + +/* Compute the preimage of "set" by the function represented by "mpa". + * In other words, plug in "mpa" in "set". + */ +__isl_give isl_set *isl_set_preimage_multi_pw_aff(__isl_take isl_set *set, + __isl_take isl_multi_pw_aff *mpa) +{ + return isl_map_preimage_multi_pw_aff(set, isl_dim_set, mpa); +} Index: lib/Analysis/isl/isl_map_lexopt_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map_lexopt_templ.c @@ -0,0 +1,149 @@ +/* + * Copyright 2010 INRIA Saclay + * Copyright 2012 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +/* Function for computing the lexicographic optimum of a map + * in the form of either an isl_map or an isl_pw_multi_aff. + */ + +#define xSF(TYPE,SUFFIX) TYPE ## SUFFIX +#define SF(TYPE,SUFFIX) xSF(TYPE,SUFFIX) + +/* Given a basic map "bmap", compute the lexicographically minimal + * (or maximal) image element for each domain element in dom. + * If empty is not NULL, then set *empty to those elements in dom + * that do not have an image element. + * + * We first make sure the basic sets in dom are disjoint and then + * simply collect the results over each of the basic sets separately. + * We could probably improve the efficiency a bit by moving the union + * domain down into the parametric integer programming. + */ +static __isl_give TYPE *SF(basic_map_partial_lexopt,SUFFIX)( + __isl_take isl_basic_map *bmap, __isl_take isl_set *dom, + __isl_give isl_set **empty, int max) +{ + int i; + TYPE *res; + isl_set *all_empty; + + dom = isl_set_make_disjoint(dom); + if (!dom) + goto error; + + if (isl_set_plain_is_empty(dom)) { + isl_space *space = isl_basic_map_get_space(bmap); + if (empty) + *empty = dom; + else + isl_set_free(dom); + isl_basic_map_free(bmap); + return EMPTY(space); + } + + res = SF(isl_basic_map_partial_lexopt,SUFFIX)(isl_basic_map_copy(bmap), + isl_basic_set_copy(dom->p[0]), empty, max); + + if (empty) + all_empty = *empty; + for (i = 1; i < dom->n; ++i) { + TYPE *res_i; + + res_i = SF(isl_basic_map_partial_lexopt,SUFFIX)( + isl_basic_map_copy(bmap), + isl_basic_set_copy(dom->p[i]), empty, max); + + res = ADD(res, res_i); + if (empty) + all_empty = isl_set_union_disjoint(all_empty, *empty); + } + + if (empty) + *empty = all_empty; + isl_set_free(dom); + isl_basic_map_free(bmap); + return res; +error: + if (empty) + *empty = NULL; + isl_set_free(dom); + isl_basic_map_free(bmap); + return NULL; +} + +static __isl_give TYPE *SF(isl_map_partial_lexopt_aligned,SUFFIX)( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty, int max); + +/* Given a map "map", compute the lexicographically minimal + * (or maximal) image element for each domain element in dom. + * Set *empty to those elements in dom that do not have an image element. + * + * Align parameters if needed and then call isl_map_partial_lexopt_aligned. + */ +static __isl_give TYPE *SF(isl_map_partial_lexopt,SUFFIX)( + __isl_take isl_map *map, __isl_take isl_set *dom, + __isl_give isl_set **empty, int max) +{ + if (!map || !dom) + goto error; + if (isl_space_match(map->dim, isl_dim_param, dom->dim, isl_dim_param)) + return SF(isl_map_partial_lexopt_aligned,SUFFIX)(map, dom, + empty, max); + if (!isl_space_has_named_params(map->dim) || + !isl_space_has_named_params(dom->dim)) + isl_die(map->ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + map = isl_map_align_params(map, isl_map_get_space(dom)); + dom = isl_map_align_params(dom, isl_map_get_space(map)); + return SF(isl_map_partial_lexopt_aligned,SUFFIX)(map, dom, empty, max); +error: + if (empty) + *empty = NULL; + isl_set_free(dom); + isl_map_free(map); + return NULL; +} + +__isl_give TYPE *SF(isl_map_lexopt,SUFFIX)(__isl_take isl_map *map, int max) +{ + isl_set *dom = NULL; + isl_space *dom_space; + + if (!map) + goto error; + dom_space = isl_space_domain(isl_space_copy(map->dim)); + dom = isl_set_universe(dom_space); + return SF(isl_map_partial_lexopt,SUFFIX)(map, dom, NULL, max); +error: + isl_map_free(map); + return NULL; +} + +__isl_give TYPE *SF(isl_map_lexmin,SUFFIX)(__isl_take isl_map *map) +{ + return SF(isl_map_lexopt,SUFFIX)(map, 0); +} + +__isl_give TYPE *SF(isl_map_lexmax,SUFFIX)(__isl_take isl_map *map) +{ + return SF(isl_map_lexopt,SUFFIX)(map, 1); +} + +__isl_give TYPE *SF(isl_set_lexmin,SUFFIX)(__isl_take isl_set *set) +{ + return SF(isl_map_lexmin,SUFFIX)(set); +} + +__isl_give TYPE *SF(isl_set_lexmax,SUFFIX)(__isl_take isl_set *set) +{ + return SF(isl_map_lexmax,SUFFIX)(set); +} Index: lib/Analysis/isl/isl_map_list.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map_list.c @@ -0,0 +1,32 @@ +#include +#include + +#undef EL +#define EL isl_basic_map + +#include + +#undef BASE +#define BASE basic_map + +#include + +#undef EL +#define EL isl_map + +#include + +#undef BASE +#define BASE map + +#include + +#undef EL +#define EL isl_union_map + +#include + +#undef BASE +#define BASE union_map + +#include Index: lib/Analysis/isl/isl_map_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map_private.h @@ -0,0 +1,508 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_MAP_PRIVATE_H +#define ISL_MAP_PRIVATE_H + +#define isl_basic_set isl_basic_map +#define isl_maybe_isl_basic_set isl_maybe_isl_basic_map +#define isl_set isl_map +#define isl_basic_set_list isl_basic_map_list +#define isl_set_list isl_map_list +#include +#include +#include +#include +#include +#include +#include + +/* A "basic map" is a relation between two sets of variables, + * called the "in" and "out" variables. + * A "basic set" is a basic map with a zero-dimensional + * domain. + * + * It is implemented as a set with two extra fields: + * n_in is the number of in variables + * n_out is the number of out variables + * n_in + n_out should be equal to set.dim + */ +struct isl_basic_map { + int ref; +#define ISL_BASIC_MAP_FINAL (1 << 0) +#define ISL_BASIC_MAP_EMPTY (1 << 1) +#define ISL_BASIC_MAP_NO_IMPLICIT (1 << 2) +#define ISL_BASIC_MAP_NO_REDUNDANT (1 << 3) +#define ISL_BASIC_MAP_RATIONAL (1 << 4) +#define ISL_BASIC_MAP_NORMALIZED (1 << 5) +#define ISL_BASIC_MAP_NORMALIZED_DIVS (1 << 6) +#define ISL_BASIC_MAP_ALL_EQUALITIES (1 << 7) +#define ISL_BASIC_MAP_REDUCED_COEFFICIENTS (1 << 8) +#define ISL_BASIC_SET_FINAL (1 << 0) +#define ISL_BASIC_SET_EMPTY (1 << 1) +#define ISL_BASIC_SET_NO_IMPLICIT (1 << 2) +#define ISL_BASIC_SET_NO_REDUNDANT (1 << 3) +#define ISL_BASIC_SET_RATIONAL (1 << 4) +#define ISL_BASIC_SET_NORMALIZED (1 << 5) +#define ISL_BASIC_SET_NORMALIZED_DIVS (1 << 6) +#define ISL_BASIC_SET_ALL_EQUALITIES (1 << 7) +#define ISL_BASIC_SET_REDUCED_COEFFICIENTS (1 << 8) + unsigned flags; + + struct isl_ctx *ctx; + + isl_space *dim; + unsigned extra; + + unsigned n_eq; + unsigned n_ineq; + + size_t c_size; + isl_int **eq; + isl_int **ineq; + + unsigned n_div; + + isl_int **div; + + struct isl_vec *sample; + + struct isl_blk block; + struct isl_blk block2; +}; + +#undef EL +#define EL isl_basic_set + +#include + +/* A "map" is a (possibly disjoint) union of basic maps. + * A "set" is a (possibly disjoint) union of basic sets. + * + * Currently, the isl_set structure is identical to the isl_map structure + * and the library depends on this correspondence internally. + * However, users should not depend on this correspondence. + * + * "cached_simple_hull" contains copies of the unshifted and shifted + * simple hulls, if they have already been computed. Otherwise, + * the entries are NULL. + */ +struct isl_map { + int ref; +#define ISL_MAP_DISJOINT (1 << 0) +#define ISL_MAP_NORMALIZED (1 << 1) +#define ISL_SET_DISJOINT (1 << 0) +#define ISL_SET_NORMALIZED (1 << 1) + unsigned flags; + isl_basic_map *cached_simple_hull[2]; + + struct isl_ctx *ctx; + + isl_space *dim; + + int n; + + size_t size; + struct isl_basic_map *p[1]; +}; + +#undef EL +#define EL isl_set + +#include + +__isl_give isl_basic_set *isl_basic_set_alloc(isl_ctx *ctx, + unsigned nparam, unsigned dim, unsigned extra, + unsigned n_eq, unsigned n_ineq); +__isl_give isl_basic_set *isl_basic_set_extend(__isl_take isl_basic_set *base, + unsigned nparam, unsigned dim, unsigned extra, + unsigned n_eq, unsigned n_ineq); +__isl_give isl_basic_set *isl_basic_set_extend_constraints( + __isl_take isl_basic_set *base, unsigned n_eq, unsigned n_ineq); +__isl_give isl_basic_set *isl_basic_set_finalize( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_dup(__isl_keep isl_basic_set *bset); +__isl_give isl_basic_set *isl_basic_set_simplify( + __isl_take isl_basic_set *bset); + +__isl_give isl_basic_map *isl_basic_map_alloc(isl_ctx *ctx, + unsigned nparam, unsigned in, unsigned out, unsigned extra, + unsigned n_eq, unsigned n_ineq); +__isl_give isl_basic_map *isl_basic_map_mark_final( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_finalize( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_extend(__isl_take isl_basic_map *base, + unsigned nparam, unsigned n_in, unsigned n_out, unsigned extra, + unsigned n_eq, unsigned n_ineq); +__isl_give isl_basic_map *isl_basic_map_extend_constraints( + __isl_take isl_basic_map *base, unsigned n_eq, unsigned n_ineq); +__isl_give isl_basic_map *isl_basic_map_simplify( + __isl_take isl_basic_map *bmap); + +__isl_give isl_set *isl_set_alloc(isl_ctx *ctx, + unsigned nparam, unsigned dim, int n, unsigned flags); +__isl_give isl_set *isl_set_add_basic_set(__isl_take isl_set *set, + __isl_take isl_basic_set *bset); +__isl_give isl_set *isl_set_finalize(__isl_take isl_set *set); +__isl_give isl_set *isl_set_dup(__isl_keep isl_set *set); + +__isl_give isl_map *isl_map_alloc(isl_ctx *ctx, + unsigned nparam, unsigned in, unsigned out, int n, unsigned flags); +__isl_give isl_map *isl_map_add_basic_map(__isl_take isl_map *map, + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_dup(__isl_keep isl_map *map); +__isl_give isl_map *isl_map_finalize(__isl_take isl_map *map); + +__isl_give isl_basic_set *isl_basic_set_from_underlying_set( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *like); +__isl_give isl_set *isl_set_from_underlying_set( + __isl_take isl_set *set, __isl_take isl_basic_set *like); +__isl_give isl_set *isl_set_to_underlying_set(__isl_take isl_set *set); + +__isl_give isl_map *isl_map_realign(__isl_take isl_map *map, + __isl_take isl_reordering *r); +__isl_give isl_set *isl_set_realign(__isl_take isl_set *set, + __isl_take isl_reordering *r); + +__isl_give isl_map *isl_map_reset(__isl_take isl_map *map, + enum isl_dim_type type); + +__isl_give isl_basic_set *isl_basic_set_reset_space( + __isl_take isl_basic_set *bset, __isl_take isl_space *dim); +__isl_give isl_basic_map *isl_basic_map_reset_space( + __isl_take isl_basic_map *bmap, __isl_take isl_space *dim); +__isl_give isl_map *isl_map_reset_space(__isl_take isl_map *map, + __isl_take isl_space *dim); + +unsigned isl_basic_map_offset(struct isl_basic_map *bmap, + enum isl_dim_type type); +unsigned isl_basic_set_offset(struct isl_basic_set *bset, + enum isl_dim_type type); + +int isl_basic_map_may_be_set(__isl_keep isl_basic_map *bmap); +int isl_map_may_be_set(__isl_keep isl_map *map); +int isl_map_compatible_domain(struct isl_map *map, struct isl_set *set); +int isl_basic_map_compatible_domain(struct isl_basic_map *bmap, + struct isl_basic_set *bset); +int isl_basic_map_compatible_range(struct isl_basic_map *bmap, + struct isl_basic_set *bset); + +struct isl_basic_map *isl_basic_map_extend_space(struct isl_basic_map *base, + __isl_take isl_space *dim, unsigned extra, + unsigned n_eq, unsigned n_ineq); +struct isl_basic_set *isl_basic_set_extend_space(struct isl_basic_set *base, + __isl_take isl_space *dim, unsigned extra, + unsigned n_eq, unsigned n_ineq); +struct isl_basic_set *isl_basic_set_add_constraints(struct isl_basic_set *bset1, + struct isl_basic_set *bset2, unsigned pos); + +struct isl_map *isl_map_grow(struct isl_map *map, int n); +struct isl_set *isl_set_grow(struct isl_set *set, int n); + +isl_bool isl_basic_set_contains(__isl_keep isl_basic_set *bset, + __isl_keep isl_vec *vec); +isl_bool isl_basic_map_contains(__isl_keep isl_basic_map *bmap, + __isl_keep isl_vec *vec); + +__isl_give isl_basic_set *isl_basic_set_alloc_space(__isl_take isl_space *dim, + unsigned extra, unsigned n_eq, unsigned n_ineq); +__isl_give isl_set *isl_set_alloc_space(__isl_take isl_space *dim, int n, + unsigned flags); +__isl_give isl_basic_map *isl_basic_map_alloc_space(__isl_take isl_space *dim, + unsigned extra, unsigned n_eq, unsigned n_ineq); +__isl_give isl_map *isl_map_alloc_space(__isl_take isl_space *dim, int n, + unsigned flags); + +unsigned isl_basic_map_total_dim(const struct isl_basic_map *bmap); + +int isl_basic_map_alloc_equality(struct isl_basic_map *bmap); +int isl_basic_set_alloc_equality(struct isl_basic_set *bset); +int isl_basic_set_free_inequality(struct isl_basic_set *bset, unsigned n); +int isl_basic_map_free_equality(struct isl_basic_map *bmap, unsigned n); +int isl_basic_set_free_equality(struct isl_basic_set *bset, unsigned n); +int isl_basic_set_alloc_inequality(struct isl_basic_set *bset); +int isl_basic_map_alloc_inequality(struct isl_basic_map *bmap); +int isl_basic_map_free_inequality(struct isl_basic_map *bmap, unsigned n); +int isl_basic_map_alloc_div(struct isl_basic_map *bmap); +int isl_basic_set_alloc_div(struct isl_basic_set *bset); +int isl_basic_map_free_div(struct isl_basic_map *bmap, unsigned n); +int isl_basic_set_free_div(struct isl_basic_set *bset, unsigned n); +void isl_basic_map_inequality_to_equality( + struct isl_basic_map *bmap, unsigned pos); +int isl_basic_map_drop_equality(struct isl_basic_map *bmap, unsigned pos); +int isl_basic_set_drop_equality(struct isl_basic_set *bset, unsigned pos); +int isl_basic_set_drop_inequality(struct isl_basic_set *bset, unsigned pos); +int isl_basic_map_drop_inequality(struct isl_basic_map *bmap, unsigned pos); +__isl_give isl_basic_set *isl_basic_set_add_eq(__isl_take isl_basic_set *bset, + isl_int *eq); +__isl_give isl_basic_map *isl_basic_map_add_eq(__isl_take isl_basic_map *bmap, + isl_int *eq); +__isl_give isl_basic_set *isl_basic_set_add_ineq(__isl_take isl_basic_set *bset, + isl_int *ineq); +__isl_give isl_basic_map *isl_basic_map_add_ineq(__isl_take isl_basic_map *bmap, + isl_int *ineq); + +int isl_inequality_negate(struct isl_basic_map *bmap, unsigned pos); + +struct isl_basic_set *isl_basic_set_cow(struct isl_basic_set *bset); +struct isl_basic_map *isl_basic_map_cow(struct isl_basic_map *bmap); +struct isl_set *isl_set_cow(struct isl_set *set); +struct isl_map *isl_map_cow(struct isl_map *map); + +uint32_t isl_basic_map_get_hash(__isl_keep isl_basic_map *bmap); + +struct isl_basic_map *isl_basic_map_set_to_empty(struct isl_basic_map *bmap); +struct isl_basic_set *isl_basic_set_set_to_empty(struct isl_basic_set *bset); +struct isl_basic_set *isl_basic_set_order_divs(struct isl_basic_set *bset); +void isl_basic_map_swap_div(struct isl_basic_map *bmap, int a, int b); +struct isl_basic_map *isl_basic_map_order_divs(struct isl_basic_map *bmap); +__isl_give isl_map *isl_map_order_divs(__isl_take isl_map *map); +struct isl_basic_map *isl_basic_map_align_divs( + struct isl_basic_map *dst, struct isl_basic_map *src); +struct isl_basic_set *isl_basic_set_align_divs( + struct isl_basic_set *dst, struct isl_basic_set *src); +__isl_give isl_map *isl_map_align_divs_to_basic_map_list( + __isl_take isl_map *map, __isl_keep isl_basic_map_list *list); +__isl_give isl_basic_map_list *isl_basic_map_list_align_divs_to_basic_map( + __isl_take isl_basic_map_list *list, __isl_keep isl_basic_map *bmap); +__isl_give isl_basic_map *isl_basic_map_sort_divs( + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_sort_divs(__isl_take isl_map *map); +struct isl_basic_map *isl_basic_map_gauss( + struct isl_basic_map *bmap, int *progress); +struct isl_basic_set *isl_basic_set_gauss( + struct isl_basic_set *bset, int *progress); +int isl_basic_map_constraint_cmp(__isl_keep isl_basic_map *bmap, + isl_int *c1, isl_int *c2); +__isl_give isl_basic_map *isl_basic_map_sort_constraints( + __isl_take isl_basic_map *bmap); +__isl_give isl_basic_set *isl_basic_set_sort_constraints( + __isl_take isl_basic_set *bset); +int isl_basic_map_plain_cmp(const __isl_keep isl_basic_map *bmap1, + const __isl_keep isl_basic_map *bmap2); +isl_bool isl_basic_map_plain_is_equal(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2); +struct isl_basic_map *isl_basic_map_normalize_constraints( + struct isl_basic_map *bmap); +struct isl_basic_set *isl_basic_set_normalize_constraints( + struct isl_basic_set *bset); +struct isl_basic_map *isl_basic_map_implicit_equalities( + struct isl_basic_map *bmap); +struct isl_basic_set *isl_basic_map_underlying_set(struct isl_basic_map *bmap); +__isl_give isl_basic_set *isl_basic_set_underlying_set( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_set_list *isl_basic_map_list_underlying_set( + __isl_take isl_basic_map_list *list); +struct isl_set *isl_map_underlying_set(struct isl_map *map); +struct isl_basic_map *isl_basic_map_overlying_set(struct isl_basic_set *bset, + struct isl_basic_map *like); +__isl_give isl_basic_map *isl_basic_map_drop_constraint_involving_unknown_divs( + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_drop_constraint_involving_unknown_divs( + __isl_take isl_map *map); +__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving( + __isl_take isl_basic_set *bset, unsigned first, unsigned n); +__isl_give isl_basic_set *isl_basic_set_drop(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n); +struct isl_basic_map *isl_basic_map_drop(struct isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n); +struct isl_set *isl_set_drop(struct isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); +struct isl_basic_set *isl_basic_set_drop_dims( + struct isl_basic_set *bset, unsigned first, unsigned n); +struct isl_set *isl_set_drop_dims( + struct isl_set *set, unsigned first, unsigned n); +struct isl_map *isl_map_drop_inputs( + struct isl_map *map, unsigned first, unsigned n); +struct isl_map *isl_map_drop(struct isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_basic_map *isl_basic_map_drop_unrelated_constraints( + __isl_take isl_basic_map *bmap, __isl_take int *group); + +__isl_give isl_basic_map *isl_basic_map_remove_duplicate_constraints( + __isl_take isl_basic_map *bmap, int *progress, int detect_divs); +__isl_give isl_basic_map *isl_basic_map_detect_inequality_pairs( + __isl_take isl_basic_map *bmap, int *progress); + +struct isl_map *isl_map_remove_empty_parts(struct isl_map *map); +struct isl_set *isl_set_remove_empty_parts(struct isl_set *set); +__isl_give isl_map *isl_map_remove_obvious_duplicates(__isl_take isl_map *map); + +struct isl_set *isl_set_normalize(struct isl_set *set); + +struct isl_set *isl_set_drop_vars( + struct isl_set *set, unsigned first, unsigned n); + +struct isl_basic_map *isl_basic_map_eliminate_vars( + struct isl_basic_map *bmap, unsigned pos, unsigned n); +struct isl_basic_set *isl_basic_set_eliminate_vars( + struct isl_basic_set *bset, unsigned pos, unsigned n); + +__isl_give isl_map *isl_map_eliminate(__isl_take isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_set *isl_set_eliminate(__isl_take isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n); + +int isl_basic_set_constraint_is_redundant(struct isl_basic_set **bset, + isl_int *c, isl_int *opt_n, isl_int *opt_d); + +int isl_basic_map_add_div_constraint(__isl_keep isl_basic_map *bmap, + unsigned div, int sign); +int isl_basic_map_add_div_constraints(struct isl_basic_map *bmap, unsigned div); +__isl_give isl_basic_map *isl_basic_map_add_known_div_constraints( + __isl_take isl_basic_map *bmap); +struct isl_basic_map *isl_basic_map_drop_redundant_divs( + struct isl_basic_map *bmap); +struct isl_basic_set *isl_basic_set_drop_redundant_divs( + struct isl_basic_set *bset); + +struct isl_basic_set *isl_basic_set_recession_cone(struct isl_basic_set *bset); +struct isl_basic_set *isl_basic_set_lineality_space(struct isl_basic_set *bset); + +struct isl_basic_set *isl_basic_set_set_rational(struct isl_basic_set *bset); +__isl_give isl_set *isl_set_set_rational(__isl_take isl_set *set); +__isl_give isl_basic_map *isl_basic_map_set_rational( + __isl_take isl_basic_map *bmap); +__isl_give isl_map *isl_map_set_rational(__isl_take isl_map *map); + +int isl_map_has_rational(__isl_keep isl_map *map); +int isl_set_has_rational(__isl_keep isl_set *set); + +struct isl_mat; + +struct isl_basic_set *isl_basic_set_preimage(struct isl_basic_set *bset, + struct isl_mat *mat); +struct isl_set *isl_set_preimage(struct isl_set *set, struct isl_mat *mat); + +__isl_give isl_basic_map *isl_basic_map_transform_dims( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, unsigned first, + __isl_take isl_mat *trans); +__isl_give isl_basic_set *isl_basic_set_transform_dims( + __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned first, + __isl_take isl_mat *trans); + +isl_int *isl_set_wrap_facet(__isl_keep isl_set *set, + isl_int *facet, isl_int *ridge); + +isl_bool isl_basic_map_contains_point(__isl_keep isl_basic_map *bmap, + __isl_keep isl_point *point); +isl_bool isl_set_contains_point(__isl_keep isl_set *set, + __isl_keep isl_point *point); + +int isl_basic_set_vars_get_sign(__isl_keep isl_basic_set *bset, + unsigned first, unsigned n, int *signs); +int isl_set_foreach_orthant(__isl_keep isl_set *set, + int (*fn)(__isl_take isl_set *orthant, int *signs, void *user), + void *user); + +int isl_basic_map_add_div_constraints_var(__isl_keep isl_basic_map *bmap, + unsigned pos, isl_int *div); +int isl_basic_set_add_div_constraints_var(__isl_keep isl_basic_set *bset, + unsigned pos, isl_int *div); +int isl_basic_map_is_div_constraint(__isl_keep isl_basic_map *bmap, + isl_int *constraint, unsigned div); +int isl_basic_set_is_div_constraint(__isl_keep isl_basic_set *bset, + isl_int *constraint, unsigned div); + +__isl_give isl_basic_set *isl_basic_set_from_local_space( + __isl_take isl_local_space *ls); +__isl_give isl_basic_map *isl_basic_map_from_local_space( + __isl_take isl_local_space *ls); +__isl_give isl_basic_set *isl_basic_set_expand_divs( + __isl_take isl_basic_set *bset, __isl_take isl_mat *div, int *exp); +__isl_give isl_basic_map *isl_basic_map_expand_divs( + __isl_take isl_basic_set *bmap, __isl_take isl_mat *div, int *exp); + +__isl_give isl_basic_map *isl_basic_map_mark_div_unknown( + __isl_take isl_basic_map *bmap, int div); +isl_bool isl_basic_map_div_is_known(__isl_keep isl_basic_map *bmap, int div); +int isl_basic_map_first_unknown_div(__isl_keep isl_basic_map *bmap); +isl_bool isl_basic_map_divs_known(__isl_keep isl_basic_map *bmap); +isl_bool isl_map_divs_known(__isl_keep isl_map *map); +__isl_give isl_mat *isl_basic_set_get_divs(__isl_keep isl_basic_set *bset); +__isl_give isl_mat *isl_basic_map_get_divs(__isl_keep isl_basic_map *bmap); + +__isl_give isl_map *isl_map_inline_foreach_basic_map(__isl_take isl_map *map, + __isl_give isl_basic_map *(*fn)(__isl_take isl_basic_map *bmap)); + +__isl_give isl_map *isl_map_align_params_map_map_and( + __isl_take isl_map *map1, __isl_take isl_map *map2, + __isl_give isl_map *(*fn)(__isl_take isl_map *map1, + __isl_take isl_map *map2)); +isl_bool isl_map_align_params_map_map_and_test(__isl_keep isl_map *map1, + __isl_keep isl_map *map2, + isl_bool (*fn)(__isl_keep isl_map *map1, __isl_keep isl_map *map2)); + +int isl_basic_map_foreach_lexopt(__isl_keep isl_basic_map *bmap, int max, + int (*fn)(__isl_take isl_basic_set *dom, __isl_take isl_aff_list *list, + void *user), + void *user); +int isl_basic_set_foreach_lexopt(__isl_keep isl_basic_set *bset, int max, + int (*fn)(__isl_take isl_basic_set *dom, __isl_take isl_aff_list *list, + void *user), + void *user); + +__isl_give isl_set *isl_set_substitute(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs); + +__isl_give isl_set *isl_set_gist_params_basic_set(__isl_take isl_set *set, + __isl_take isl_basic_set *context); + +int isl_map_compatible_range(__isl_keep isl_map *map, __isl_keep isl_set *set); + +isl_bool isl_basic_map_plain_is_non_empty(__isl_keep isl_basic_map *bmap); +isl_bool isl_basic_map_plain_is_single_valued(__isl_keep isl_basic_map *bmap); + +int isl_map_is_set(__isl_keep isl_map *map); + +int isl_basic_set_plain_dim_is_fixed(__isl_keep isl_basic_set *bset, + unsigned dim, isl_int *val); + +__isl_give isl_map *isl_map_plain_gist_basic_map(__isl_take isl_map *map, + __isl_take isl_basic_map *context); + +__isl_give isl_basic_set *isl_basic_set_plain_affine_hull( + __isl_take isl_basic_set *bset); +__isl_give isl_basic_map *isl_basic_map_plain_affine_hull( + __isl_take isl_basic_map *bmap); + +int isl_basic_set_dim_residue_class(struct isl_basic_set *bset, + int pos, isl_int *modulo, isl_int *residue); +int isl_set_dim_residue_class(struct isl_set *set, + int pos, isl_int *modulo, isl_int *residue); + +__isl_give isl_basic_set *isl_basic_set_fix(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned pos, isl_int value); +__isl_give isl_basic_map *isl_basic_map_fix(__isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned pos, isl_int value); +__isl_give isl_set *isl_set_fix(__isl_take isl_set *set, + enum isl_dim_type type, unsigned pos, isl_int value); +int isl_map_plain_is_fixed(__isl_keep isl_map *map, + enum isl_dim_type type, unsigned pos, isl_int *val); + +int isl_basic_map_output_defining_equality(__isl_keep isl_basic_map *bmap, + int pos, int *div, int *ineq); + +__isl_give isl_basic_map *isl_basic_map_reduce_coefficients( + __isl_take isl_basic_map *bmap); + +__isl_give isl_basic_map *isl_basic_map_shift_div( + __isl_take isl_basic_map *bmap, int div, int pos, isl_int shift); + +__isl_give isl_basic_map_list *isl_map_get_basic_map_list( + __isl_keep isl_map *map); + +__isl_give isl_map *isl_map_fixed_power(__isl_take isl_map *map, isl_int exp); + +int isl_basic_set_count_upto(__isl_keep isl_basic_set *bset, + isl_int max, isl_int *count); +int isl_set_count_upto(__isl_keep isl_set *set, isl_int max, isl_int *count); + +#endif Index: lib/Analysis/isl/isl_map_simplify.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map_simplify.c @@ -0,0 +1,5337 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2012-2013 Ecole Normale Superieure + * Copyright 2014-2015 INRIA Rocquencourt + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include "isl_equalities.h" +#include +#include +#include "isl_tab.h" +#include +#include +#include + +static void swap_equality(struct isl_basic_map *bmap, int a, int b) +{ + isl_int *t = bmap->eq[a]; + bmap->eq[a] = bmap->eq[b]; + bmap->eq[b] = t; +} + +static void swap_inequality(struct isl_basic_map *bmap, int a, int b) +{ + if (a != b) { + isl_int *t = bmap->ineq[a]; + bmap->ineq[a] = bmap->ineq[b]; + bmap->ineq[b] = t; + } +} + +static void constraint_drop_vars(isl_int *c, unsigned n, unsigned rem) +{ + isl_seq_cpy(c, c + n, rem); + isl_seq_clr(c + rem, n); +} + +/* Drop n dimensions starting at first. + * + * In principle, this frees up some extra variables as the number + * of columns remains constant, but we would have to extend + * the div array too as the number of rows in this array is assumed + * to be equal to extra. + */ +struct isl_basic_set *isl_basic_set_drop_dims( + struct isl_basic_set *bset, unsigned first, unsigned n) +{ + int i; + + if (!bset) + goto error; + + isl_assert(bset->ctx, first + n <= bset->dim->n_out, goto error); + + if (n == 0 && !isl_space_get_tuple_name(bset->dim, isl_dim_set)) + return bset; + + bset = isl_basic_set_cow(bset); + if (!bset) + return NULL; + + for (i = 0; i < bset->n_eq; ++i) + constraint_drop_vars(bset->eq[i]+1+bset->dim->nparam+first, n, + (bset->dim->n_out-first-n)+bset->extra); + + for (i = 0; i < bset->n_ineq; ++i) + constraint_drop_vars(bset->ineq[i]+1+bset->dim->nparam+first, n, + (bset->dim->n_out-first-n)+bset->extra); + + for (i = 0; i < bset->n_div; ++i) + constraint_drop_vars(bset->div[i]+1+1+bset->dim->nparam+first, n, + (bset->dim->n_out-first-n)+bset->extra); + + bset->dim = isl_space_drop_outputs(bset->dim, first, n); + if (!bset->dim) + goto error; + + ISL_F_CLR(bset, ISL_BASIC_SET_NORMALIZED); + bset = isl_basic_set_simplify(bset); + return isl_basic_set_finalize(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +struct isl_set *isl_set_drop_dims( + struct isl_set *set, unsigned first, unsigned n) +{ + int i; + + if (!set) + goto error; + + isl_assert(set->ctx, first + n <= set->dim->n_out, goto error); + + if (n == 0 && !isl_space_get_tuple_name(set->dim, isl_dim_set)) + return set; + set = isl_set_cow(set); + if (!set) + goto error; + set->dim = isl_space_drop_outputs(set->dim, first, n); + if (!set->dim) + goto error; + + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_drop_dims(set->p[i], first, n); + if (!set->p[i]) + goto error; + } + + ISL_F_CLR(set, ISL_SET_NORMALIZED); + return set; +error: + isl_set_free(set); + return NULL; +} + +/* Move "n" divs starting at "first" to the end of the list of divs. + */ +static struct isl_basic_map *move_divs_last(struct isl_basic_map *bmap, + unsigned first, unsigned n) +{ + isl_int **div; + int i; + + if (first + n == bmap->n_div) + return bmap; + + div = isl_alloc_array(bmap->ctx, isl_int *, n); + if (!div) + goto error; + for (i = 0; i < n; ++i) + div[i] = bmap->div[first + i]; + for (i = 0; i < bmap->n_div - first - n; ++i) + bmap->div[first + i] = bmap->div[first + n + i]; + for (i = 0; i < n; ++i) + bmap->div[bmap->n_div - n + i] = div[i]; + free(div); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Drop "n" dimensions of type "type" starting at "first". + * + * In principle, this frees up some extra variables as the number + * of columns remains constant, but we would have to extend + * the div array too as the number of rows in this array is assumed + * to be equal to extra. + */ +struct isl_basic_map *isl_basic_map_drop(struct isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + unsigned dim; + unsigned offset; + unsigned left; + + if (!bmap) + goto error; + + dim = isl_basic_map_dim(bmap, type); + isl_assert(bmap->ctx, first + n <= dim, goto error); + + if (n == 0 && !isl_space_is_named_or_nested(bmap->dim, type)) + return bmap; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + + offset = isl_basic_map_offset(bmap, type) + first; + left = isl_basic_map_total_dim(bmap) - (offset - 1) - n; + for (i = 0; i < bmap->n_eq; ++i) + constraint_drop_vars(bmap->eq[i]+offset, n, left); + + for (i = 0; i < bmap->n_ineq; ++i) + constraint_drop_vars(bmap->ineq[i]+offset, n, left); + + for (i = 0; i < bmap->n_div; ++i) + constraint_drop_vars(bmap->div[i]+1+offset, n, left); + + if (type == isl_dim_div) { + bmap = move_divs_last(bmap, first, n); + if (!bmap) + goto error; + isl_basic_map_free_div(bmap, n); + } else + bmap->dim = isl_space_drop_dims(bmap->dim, type, first, n); + if (!bmap->dim) + goto error; + + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + bmap = isl_basic_map_simplify(bmap); + return isl_basic_map_finalize(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_drop(__isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_basic_set *)isl_basic_map_drop((isl_basic_map *)bset, + type, first, n); +} + +struct isl_basic_map *isl_basic_map_drop_inputs( + struct isl_basic_map *bmap, unsigned first, unsigned n) +{ + return isl_basic_map_drop(bmap, isl_dim_in, first, n); +} + +struct isl_map *isl_map_drop(struct isl_map *map, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!map) + goto error; + + isl_assert(map->ctx, first + n <= isl_map_dim(map, type), goto error); + + if (n == 0 && !isl_space_get_tuple_name(map->dim, type)) + return map; + map = isl_map_cow(map); + if (!map) + goto error; + map->dim = isl_space_drop_dims(map->dim, type, first, n); + if (!map->dim) + goto error; + + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_drop(map->p[i], type, first, n); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + + return map; +error: + isl_map_free(map); + return NULL; +} + +struct isl_set *isl_set_drop(struct isl_set *set, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return (isl_set *)isl_map_drop((isl_map *)set, type, first, n); +} + +struct isl_map *isl_map_drop_inputs( + struct isl_map *map, unsigned first, unsigned n) +{ + return isl_map_drop(map, isl_dim_in, first, n); +} + +/* + * We don't cow, as the div is assumed to be redundant. + */ +static struct isl_basic_map *isl_basic_map_drop_div( + struct isl_basic_map *bmap, unsigned div) +{ + int i; + unsigned pos; + + if (!bmap) + goto error; + + pos = 1 + isl_space_dim(bmap->dim, isl_dim_all) + div; + + isl_assert(bmap->ctx, div < bmap->n_div, goto error); + + for (i = 0; i < bmap->n_eq; ++i) + constraint_drop_vars(bmap->eq[i]+pos, 1, bmap->extra-div-1); + + for (i = 0; i < bmap->n_ineq; ++i) { + if (!isl_int_is_zero(bmap->ineq[i][pos])) { + isl_basic_map_drop_inequality(bmap, i); + --i; + continue; + } + constraint_drop_vars(bmap->ineq[i]+pos, 1, bmap->extra-div-1); + } + + for (i = 0; i < bmap->n_div; ++i) + constraint_drop_vars(bmap->div[i]+1+pos, 1, bmap->extra-div-1); + + if (div != bmap->n_div - 1) { + int j; + isl_int *t = bmap->div[div]; + + for (j = div; j < bmap->n_div - 1; ++j) + bmap->div[j] = bmap->div[j+1]; + + bmap->div[bmap->n_div - 1] = t; + } + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + isl_basic_map_free_div(bmap, 1); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_map *isl_basic_map_normalize_constraints( + struct isl_basic_map *bmap) +{ + int i; + isl_int gcd; + unsigned total = isl_basic_map_total_dim(bmap); + + if (!bmap) + return NULL; + + isl_int_init(gcd); + for (i = bmap->n_eq - 1; i >= 0; --i) { + isl_seq_gcd(bmap->eq[i]+1, total, &gcd); + if (isl_int_is_zero(gcd)) { + if (!isl_int_is_zero(bmap->eq[i][0])) { + bmap = isl_basic_map_set_to_empty(bmap); + break; + } + isl_basic_map_drop_equality(bmap, i); + continue; + } + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) + isl_int_gcd(gcd, gcd, bmap->eq[i][0]); + if (isl_int_is_one(gcd)) + continue; + if (!isl_int_is_divisible_by(bmap->eq[i][0], gcd)) { + bmap = isl_basic_map_set_to_empty(bmap); + break; + } + isl_seq_scale_down(bmap->eq[i], bmap->eq[i], gcd, 1+total); + } + + for (i = bmap->n_ineq - 1; i >= 0; --i) { + isl_seq_gcd(bmap->ineq[i]+1, total, &gcd); + if (isl_int_is_zero(gcd)) { + if (isl_int_is_neg(bmap->ineq[i][0])) { + bmap = isl_basic_map_set_to_empty(bmap); + break; + } + isl_basic_map_drop_inequality(bmap, i); + continue; + } + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) + isl_int_gcd(gcd, gcd, bmap->ineq[i][0]); + if (isl_int_is_one(gcd)) + continue; + isl_int_fdiv_q(bmap->ineq[i][0], bmap->ineq[i][0], gcd); + isl_seq_scale_down(bmap->ineq[i]+1, bmap->ineq[i]+1, gcd, total); + } + isl_int_clear(gcd); + + return bmap; +} + +struct isl_basic_set *isl_basic_set_normalize_constraints( + struct isl_basic_set *bset) +{ + return (struct isl_basic_set *)isl_basic_map_normalize_constraints( + (struct isl_basic_map *)bset); +} + +/* Assuming the variable at position "pos" has an integer coefficient + * in integer division "div", extract it from this integer division. + * "pos" is as determined by isl_basic_map_offset, i.e., pos == 0 + * corresponds to the constant term. + * + * That is, the integer division is of the form + * + * floor((... + c * d * x_pos + ...)/d) + * + * Replace it by + * + * floor((... + 0 * x_pos + ...)/d) + c * x_pos + */ +static __isl_give isl_basic_map *remove_var_from_div( + __isl_take isl_basic_map *bmap, int div, int pos) +{ + isl_int shift; + + isl_int_init(shift); + isl_int_divexact(shift, bmap->div[div][1 + pos], bmap->div[div][0]); + isl_int_neg(shift, shift); + bmap = isl_basic_map_shift_div(bmap, div, pos, shift); + isl_int_clear(shift); + + return bmap; +} + +/* Check if integer division "div" has any integral coefficient + * (or constant term). If so, extract them from the integer division. + */ +static __isl_give isl_basic_map *remove_independent_vars_from_div( + __isl_take isl_basic_map *bmap, int div) +{ + int i; + unsigned total = 1 + isl_basic_map_total_dim(bmap); + + for (i = 0; i < total; ++i) { + if (isl_int_is_zero(bmap->div[div][1 + i])) + continue; + if (!isl_int_is_divisible_by(bmap->div[div][1 + i], + bmap->div[div][0])) + continue; + bmap = remove_var_from_div(bmap, div, i); + if (!bmap) + break; + } + + return bmap; +} + +/* Check if any known integer division has any integral coefficient + * (or constant term). If so, extract them from the integer division. + */ +static __isl_give isl_basic_map *remove_independent_vars_from_divs( + __isl_take isl_basic_map *bmap) +{ + int i; + + if (!bmap) + return NULL; + if (bmap->n_div == 0) + return bmap; + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + bmap = remove_independent_vars_from_div(bmap, i); + if (!bmap) + break; + } + + return bmap; +} + +/* Remove any common factor in numerator and denominator of the div expression, + * not taking into account the constant term. + * That is, if the div is of the form + * + * floor((a + m f(x))/(m d)) + * + * then replace it by + * + * floor((floor(a/m) + f(x))/d) + * + * The difference {a/m}/d in the argument satisfies 0 <= {a/m}/d < 1/d + * and can therefore not influence the result of the floor. + */ +static void normalize_div_expression(__isl_keep isl_basic_map *bmap, int div) +{ + unsigned total = isl_basic_map_total_dim(bmap); + isl_ctx *ctx = bmap->ctx; + + if (isl_int_is_zero(bmap->div[div][0])) + return; + isl_seq_gcd(bmap->div[div] + 2, total, &ctx->normalize_gcd); + isl_int_gcd(ctx->normalize_gcd, ctx->normalize_gcd, bmap->div[div][0]); + if (isl_int_is_one(ctx->normalize_gcd)) + return; + isl_int_fdiv_q(bmap->div[div][1], bmap->div[div][1], + ctx->normalize_gcd); + isl_int_divexact(bmap->div[div][0], bmap->div[div][0], + ctx->normalize_gcd); + isl_seq_scale_down(bmap->div[div] + 2, bmap->div[div] + 2, + ctx->normalize_gcd, total); +} + +/* Remove any common factor in numerator and denominator of a div expression, + * not taking into account the constant term. + * That is, look for any div of the form + * + * floor((a + m f(x))/(m d)) + * + * and replace it by + * + * floor((floor(a/m) + f(x))/d) + * + * The difference {a/m}/d in the argument satisfies 0 <= {a/m}/d < 1/d + * and can therefore not influence the result of the floor. + */ +static __isl_give isl_basic_map *normalize_div_expressions( + __isl_take isl_basic_map *bmap) +{ + int i; + + if (!bmap) + return NULL; + if (bmap->n_div == 0) + return bmap; + + for (i = 0; i < bmap->n_div; ++i) + normalize_div_expression(bmap, i); + + return bmap; +} + +/* Assumes divs have been ordered if keep_divs is set. + */ +static void eliminate_var_using_equality(struct isl_basic_map *bmap, + unsigned pos, isl_int *eq, int keep_divs, int *progress) +{ + unsigned total; + unsigned space_total; + int k; + int last_div; + + total = isl_basic_map_total_dim(bmap); + space_total = isl_space_dim(bmap->dim, isl_dim_all); + last_div = isl_seq_last_non_zero(eq + 1 + space_total, bmap->n_div); + for (k = 0; k < bmap->n_eq; ++k) { + if (bmap->eq[k] == eq) + continue; + if (isl_int_is_zero(bmap->eq[k][1+pos])) + continue; + if (progress) + *progress = 1; + isl_seq_elim(bmap->eq[k], eq, 1+pos, 1+total, NULL); + isl_seq_normalize(bmap->ctx, bmap->eq[k], 1 + total); + } + + for (k = 0; k < bmap->n_ineq; ++k) { + if (isl_int_is_zero(bmap->ineq[k][1+pos])) + continue; + if (progress) + *progress = 1; + isl_seq_elim(bmap->ineq[k], eq, 1+pos, 1+total, NULL); + isl_seq_normalize(bmap->ctx, bmap->ineq[k], 1 + total); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + } + + for (k = 0; k < bmap->n_div; ++k) { + if (isl_int_is_zero(bmap->div[k][0])) + continue; + if (isl_int_is_zero(bmap->div[k][1+1+pos])) + continue; + if (progress) + *progress = 1; + /* We need to be careful about circular definitions, + * so for now we just remove the definition of div k + * if the equality contains any divs. + * If keep_divs is set, then the divs have been ordered + * and we can keep the definition as long as the result + * is still ordered. + */ + if (last_div == -1 || (keep_divs && last_div < k)) { + isl_seq_elim(bmap->div[k]+1, eq, + 1+pos, 1+total, &bmap->div[k][0]); + normalize_div_expression(bmap, k); + } else + isl_seq_clr(bmap->div[k], 1 + total); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + } +} + +/* Assumes divs have been ordered if keep_divs is set. + */ +static __isl_give isl_basic_map *eliminate_div(__isl_take isl_basic_map *bmap, + isl_int *eq, unsigned div, int keep_divs) +{ + unsigned pos = isl_space_dim(bmap->dim, isl_dim_all) + div; + + eliminate_var_using_equality(bmap, pos, eq, keep_divs, NULL); + + bmap = isl_basic_map_drop_div(bmap, div); + + return bmap; +} + +/* Check if elimination of div "div" using equality "eq" would not + * result in a div depending on a later div. + */ +static int ok_to_eliminate_div(struct isl_basic_map *bmap, isl_int *eq, + unsigned div) +{ + int k; + int last_div; + unsigned space_total = isl_space_dim(bmap->dim, isl_dim_all); + unsigned pos = space_total + div; + + last_div = isl_seq_last_non_zero(eq + 1 + space_total, bmap->n_div); + if (last_div < 0 || last_div <= div) + return 1; + + for (k = 0; k <= last_div; ++k) { + if (isl_int_is_zero(bmap->div[k][0])) + return 1; + if (!isl_int_is_zero(bmap->div[k][1 + 1 + pos])) + return 0; + } + + return 1; +} + +/* Elimininate divs based on equalities + */ +static struct isl_basic_map *eliminate_divs_eq( + struct isl_basic_map *bmap, int *progress) +{ + int d; + int i; + int modified = 0; + unsigned off; + + bmap = isl_basic_map_order_divs(bmap); + + if (!bmap) + return NULL; + + off = 1 + isl_space_dim(bmap->dim, isl_dim_all); + + for (d = bmap->n_div - 1; d >= 0 ; --d) { + for (i = 0; i < bmap->n_eq; ++i) { + if (!isl_int_is_one(bmap->eq[i][off + d]) && + !isl_int_is_negone(bmap->eq[i][off + d])) + continue; + if (!ok_to_eliminate_div(bmap, bmap->eq[i], d)) + continue; + modified = 1; + *progress = 1; + bmap = eliminate_div(bmap, bmap->eq[i], d, 1); + if (isl_basic_map_drop_equality(bmap, i) < 0) + return isl_basic_map_free(bmap); + break; + } + } + if (modified) + return eliminate_divs_eq(bmap, progress); + return bmap; +} + +/* Elimininate divs based on inequalities + */ +static struct isl_basic_map *eliminate_divs_ineq( + struct isl_basic_map *bmap, int *progress) +{ + int d; + int i; + unsigned off; + struct isl_ctx *ctx; + + if (!bmap) + return NULL; + + ctx = bmap->ctx; + off = 1 + isl_space_dim(bmap->dim, isl_dim_all); + + for (d = bmap->n_div - 1; d >= 0 ; --d) { + for (i = 0; i < bmap->n_eq; ++i) + if (!isl_int_is_zero(bmap->eq[i][off + d])) + break; + if (i < bmap->n_eq) + continue; + for (i = 0; i < bmap->n_ineq; ++i) + if (isl_int_abs_gt(bmap->ineq[i][off + d], ctx->one)) + break; + if (i < bmap->n_ineq) + continue; + *progress = 1; + bmap = isl_basic_map_eliminate_vars(bmap, (off-1)+d, 1); + if (!bmap || ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + break; + bmap = isl_basic_map_drop_div(bmap, d); + if (!bmap) + break; + } + return bmap; +} + +struct isl_basic_map *isl_basic_map_gauss( + struct isl_basic_map *bmap, int *progress) +{ + int k; + int done; + int last_var; + unsigned total_var; + unsigned total; + + bmap = isl_basic_map_order_divs(bmap); + + if (!bmap) + return NULL; + + total = isl_basic_map_total_dim(bmap); + total_var = total - bmap->n_div; + + last_var = total - 1; + for (done = 0; done < bmap->n_eq; ++done) { + for (; last_var >= 0; --last_var) { + for (k = done; k < bmap->n_eq; ++k) + if (!isl_int_is_zero(bmap->eq[k][1+last_var])) + break; + if (k < bmap->n_eq) + break; + } + if (last_var < 0) + break; + if (k != done) + swap_equality(bmap, k, done); + if (isl_int_is_neg(bmap->eq[done][1+last_var])) + isl_seq_neg(bmap->eq[done], bmap->eq[done], 1+total); + + eliminate_var_using_equality(bmap, last_var, bmap->eq[done], 1, + progress); + + if (last_var >= total_var && + isl_int_is_zero(bmap->div[last_var - total_var][0])) { + unsigned div = last_var - total_var; + isl_seq_neg(bmap->div[div]+1, bmap->eq[done], 1+total); + isl_int_set_si(bmap->div[div][1+1+last_var], 0); + isl_int_set(bmap->div[div][0], + bmap->eq[done][1+last_var]); + if (progress) + *progress = 1; + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + } + } + if (done == bmap->n_eq) + return bmap; + for (k = done; k < bmap->n_eq; ++k) { + if (isl_int_is_zero(bmap->eq[k][0])) + continue; + return isl_basic_map_set_to_empty(bmap); + } + isl_basic_map_free_equality(bmap, bmap->n_eq-done); + return bmap; +} + +struct isl_basic_set *isl_basic_set_gauss( + struct isl_basic_set *bset, int *progress) +{ + return (struct isl_basic_set*)isl_basic_map_gauss( + (struct isl_basic_map *)bset, progress); +} + + +static unsigned int round_up(unsigned int v) +{ + int old_v = v; + + while (v) { + old_v = v; + v ^= v & -v; + } + return old_v << 1; +} + +/* Hash table of inequalities in a basic map. + * "index" is an array of addresses of inequalities in the basic map, some + * of which are NULL. The inequalities are hashed on the coefficients + * except the constant term. + * "size" is the number of elements in the array and is always a power of two + * "bits" is the number of bits need to represent an index into the array. + * "total" is the total dimension of the basic map. + */ +struct isl_constraint_index { + unsigned int size; + int bits; + isl_int ***index; + unsigned total; +}; + +/* Fill in the "ci" data structure for holding the inequalities of "bmap". + */ +static isl_stat create_constraint_index(struct isl_constraint_index *ci, + __isl_keep isl_basic_map *bmap) +{ + isl_ctx *ctx; + + ci->index = NULL; + if (!bmap) + return isl_stat_error; + ci->total = isl_basic_set_total_dim(bmap); + if (bmap->n_ineq == 0) + return isl_stat_ok; + ci->size = round_up(4 * (bmap->n_ineq + 1) / 3 - 1); + ci->bits = ffs(ci->size) - 1; + ctx = isl_basic_map_get_ctx(bmap); + ci->index = isl_calloc_array(ctx, isl_int **, ci->size); + if (!ci->index) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Free the memory allocated by create_constraint_index. + */ +static void constraint_index_free(struct isl_constraint_index *ci) +{ + free(ci->index); +} + +/* Return the position in ci->index that contains the address of + * an inequality that is equal to *ineq up to the constant term, + * provided this address is not identical to "ineq". + * If there is no such inequality, then return the position where + * such an inequality should be inserted. + */ +static int hash_index_ineq(struct isl_constraint_index *ci, isl_int **ineq) +{ + int h; + uint32_t hash = isl_seq_get_hash_bits((*ineq) + 1, ci->total, ci->bits); + for (h = hash; ci->index[h]; h = (h+1) % ci->size) + if (ineq != ci->index[h] && + isl_seq_eq((*ineq) + 1, ci->index[h][0]+1, ci->total)) + break; + return h; +} + +/* Return the position in ci->index that contains the address of + * an inequality that is equal to the k'th inequality of "bmap" + * up to the constant term, provided it does not point to the very + * same inequality. + * If there is no such inequality, then return the position where + * such an inequality should be inserted. + */ +static int hash_index(struct isl_constraint_index *ci, + __isl_keep isl_basic_map *bmap, int k) +{ + return hash_index_ineq(ci, &bmap->ineq[k]); +} + +static int set_hash_index(struct isl_constraint_index *ci, + struct isl_basic_set *bset, int k) +{ + return hash_index(ci, bset, k); +} + +/* Fill in the "ci" data structure with the inequalities of "bset". + */ +static isl_stat setup_constraint_index(struct isl_constraint_index *ci, + __isl_keep isl_basic_set *bset) +{ + int k, h; + + if (create_constraint_index(ci, bset) < 0) + return isl_stat_error; + + for (k = 0; k < bset->n_ineq; ++k) { + h = set_hash_index(ci, bset, k); + ci->index[h] = &bset->ineq[k]; + } + + return isl_stat_ok; +} + +/* Is the inequality ineq (obviously) redundant with respect + * to the constraints in "ci"? + * + * Look for an inequality in "ci" with the same coefficients and then + * check if the contant term of "ineq" is greater than or equal + * to the constant term of that inequality. If so, "ineq" is clearly + * redundant. + * + * Note that hash_index_ineq ignores a stored constraint if it has + * the same address as the passed inequality. It is ok to pass + * the address of a local variable here since it will never be + * the same as the address of a constraint in "ci". + */ +static isl_bool constraint_index_is_redundant(struct isl_constraint_index *ci, + isl_int *ineq) +{ + int h; + + h = hash_index_ineq(ci, &ineq); + if (!ci->index[h]) + return isl_bool_false; + return isl_int_ge(ineq[0], (*ci->index[h])[0]); +} + +/* If we can eliminate more than one div, then we need to make + * sure we do it from last div to first div, in order not to + * change the position of the other divs that still need to + * be removed. + */ +static struct isl_basic_map *remove_duplicate_divs( + struct isl_basic_map *bmap, int *progress) +{ + unsigned int size; + int *index; + int *elim_for; + int k, l, h; + int bits; + struct isl_blk eq; + unsigned total_var; + unsigned total; + struct isl_ctx *ctx; + + bmap = isl_basic_map_order_divs(bmap); + if (!bmap || bmap->n_div <= 1) + return bmap; + + total_var = isl_space_dim(bmap->dim, isl_dim_all); + total = total_var + bmap->n_div; + + ctx = bmap->ctx; + for (k = bmap->n_div - 1; k >= 0; --k) + if (!isl_int_is_zero(bmap->div[k][0])) + break; + if (k <= 0) + return bmap; + + size = round_up(4 * bmap->n_div / 3 - 1); + if (size == 0) + return bmap; + elim_for = isl_calloc_array(ctx, int, bmap->n_div); + bits = ffs(size) - 1; + index = isl_calloc_array(ctx, int, size); + if (!elim_for || !index) + goto out; + eq = isl_blk_alloc(ctx, 1+total); + if (isl_blk_is_error(eq)) + goto out; + + isl_seq_clr(eq.data, 1+total); + index[isl_seq_get_hash_bits(bmap->div[k], 2+total, bits)] = k + 1; + for (--k; k >= 0; --k) { + uint32_t hash; + + if (isl_int_is_zero(bmap->div[k][0])) + continue; + + hash = isl_seq_get_hash_bits(bmap->div[k], 2+total, bits); + for (h = hash; index[h]; h = (h+1) % size) + if (isl_seq_eq(bmap->div[k], + bmap->div[index[h]-1], 2+total)) + break; + if (index[h]) { + *progress = 1; + l = index[h] - 1; + elim_for[l] = k + 1; + } + index[h] = k+1; + } + for (l = bmap->n_div - 1; l >= 0; --l) { + if (!elim_for[l]) + continue; + k = elim_for[l] - 1; + isl_int_set_si(eq.data[1+total_var+k], -1); + isl_int_set_si(eq.data[1+total_var+l], 1); + bmap = eliminate_div(bmap, eq.data, l, 1); + if (!bmap) + break; + isl_int_set_si(eq.data[1+total_var+k], 0); + isl_int_set_si(eq.data[1+total_var+l], 0); + } + + isl_blk_free(ctx, eq); +out: + free(index); + free(elim_for); + return bmap; +} + +static int n_pure_div_eq(struct isl_basic_map *bmap) +{ + int i, j; + unsigned total; + + total = isl_space_dim(bmap->dim, isl_dim_all); + for (i = 0, j = bmap->n_div-1; i < bmap->n_eq; ++i) { + while (j >= 0 && isl_int_is_zero(bmap->eq[i][1 + total + j])) + --j; + if (j < 0) + break; + if (isl_seq_first_non_zero(bmap->eq[i] + 1 + total, j) != -1) + return 0; + } + return i; +} + +/* Normalize divs that appear in equalities. + * + * In particular, we assume that bmap contains some equalities + * of the form + * + * a x = m * e_i + * + * and we want to replace the set of e_i by a minimal set and + * such that the new e_i have a canonical representation in terms + * of the vector x. + * If any of the equalities involves more than one divs, then + * we currently simply bail out. + * + * Let us first additionally assume that all equalities involve + * a div. The equalities then express modulo constraints on the + * remaining variables and we can use "parameter compression" + * to find a minimal set of constraints. The result is a transformation + * + * x = T(x') = x_0 + G x' + * + * with G a lower-triangular matrix with all elements below the diagonal + * non-negative and smaller than the diagonal element on the same row. + * We first normalize x_0 by making the same property hold in the affine + * T matrix. + * The rows i of G with a 1 on the diagonal do not impose any modulo + * constraint and simply express x_i = x'_i. + * For each of the remaining rows i, we introduce a div and a corresponding + * equality. In particular + * + * g_ii e_j = x_i - g_i(x') + * + * where each x'_k is replaced either by x_k (if g_kk = 1) or the + * corresponding div (if g_kk != 1). + * + * If there are any equalities not involving any div, then we + * first apply a variable compression on the variables x: + * + * x = C x'' x'' = C_2 x + * + * and perform the above parameter compression on A C instead of on A. + * The resulting compression is then of the form + * + * x'' = T(x') = x_0 + G x' + * + * and in constructing the new divs and the corresponding equalities, + * we have to replace each x'', i.e., the x'_k with (g_kk = 1), + * by the corresponding row from C_2. + */ +static struct isl_basic_map *normalize_divs( + struct isl_basic_map *bmap, int *progress) +{ + int i, j, k; + int total; + int div_eq; + struct isl_mat *B; + struct isl_vec *d; + struct isl_mat *T = NULL; + struct isl_mat *C = NULL; + struct isl_mat *C2 = NULL; + isl_int v; + int *pos; + int dropped, needed; + + if (!bmap) + return NULL; + + if (bmap->n_div == 0) + return bmap; + + if (bmap->n_eq == 0) + return bmap; + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS)) + return bmap; + + total = isl_space_dim(bmap->dim, isl_dim_all); + div_eq = n_pure_div_eq(bmap); + if (div_eq == 0) + return bmap; + + if (div_eq < bmap->n_eq) { + B = isl_mat_sub_alloc6(bmap->ctx, bmap->eq, div_eq, + bmap->n_eq - div_eq, 0, 1 + total); + C = isl_mat_variable_compression(B, &C2); + if (!C || !C2) + goto error; + if (C->n_col == 0) { + bmap = isl_basic_map_set_to_empty(bmap); + isl_mat_free(C); + isl_mat_free(C2); + goto done; + } + } + + d = isl_vec_alloc(bmap->ctx, div_eq); + if (!d) + goto error; + for (i = 0, j = bmap->n_div-1; i < div_eq; ++i) { + while (j >= 0 && isl_int_is_zero(bmap->eq[i][1 + total + j])) + --j; + isl_int_set(d->block.data[i], bmap->eq[i][1 + total + j]); + } + B = isl_mat_sub_alloc6(bmap->ctx, bmap->eq, 0, div_eq, 0, 1 + total); + + if (C) { + B = isl_mat_product(B, C); + C = NULL; + } + + T = isl_mat_parameter_compression(B, d); + if (!T) + goto error; + if (T->n_col == 0) { + bmap = isl_basic_map_set_to_empty(bmap); + isl_mat_free(C2); + isl_mat_free(T); + goto done; + } + isl_int_init(v); + for (i = 0; i < T->n_row - 1; ++i) { + isl_int_fdiv_q(v, T->row[1 + i][0], T->row[1 + i][1 + i]); + if (isl_int_is_zero(v)) + continue; + isl_mat_col_submul(T, 0, v, 1 + i); + } + isl_int_clear(v); + pos = isl_alloc_array(bmap->ctx, int, T->n_row); + if (!pos) + goto error; + /* We have to be careful because dropping equalities may reorder them */ + dropped = 0; + for (j = bmap->n_div - 1; j >= 0; --j) { + for (i = 0; i < bmap->n_eq; ++i) + if (!isl_int_is_zero(bmap->eq[i][1 + total + j])) + break; + if (i < bmap->n_eq) { + bmap = isl_basic_map_drop_div(bmap, j); + isl_basic_map_drop_equality(bmap, i); + ++dropped; + } + } + pos[0] = 0; + needed = 0; + for (i = 1; i < T->n_row; ++i) { + if (isl_int_is_one(T->row[i][i])) + pos[i] = i; + else + needed++; + } + if (needed > dropped) { + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), + needed, needed, 0); + if (!bmap) + goto error; + } + for (i = 1; i < T->n_row; ++i) { + if (isl_int_is_one(T->row[i][i])) + continue; + k = isl_basic_map_alloc_div(bmap); + pos[i] = 1 + total + k; + isl_seq_clr(bmap->div[k] + 1, 1 + total + bmap->n_div); + isl_int_set(bmap->div[k][0], T->row[i][i]); + if (C2) + isl_seq_cpy(bmap->div[k] + 1, C2->row[i], 1 + total); + else + isl_int_set_si(bmap->div[k][1 + i], 1); + for (j = 0; j < i; ++j) { + if (isl_int_is_zero(T->row[i][j])) + continue; + if (pos[j] < T->n_row && C2) + isl_seq_submul(bmap->div[k] + 1, T->row[i][j], + C2->row[pos[j]], 1 + total); + else + isl_int_neg(bmap->div[k][1 + pos[j]], + T->row[i][j]); + } + j = isl_basic_map_alloc_equality(bmap); + isl_seq_neg(bmap->eq[j], bmap->div[k]+1, 1+total+bmap->n_div); + isl_int_set(bmap->eq[j][pos[i]], bmap->div[k][0]); + } + free(pos); + isl_mat_free(C2); + isl_mat_free(T); + + if (progress) + *progress = 1; +done: + ISL_F_SET(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS); + + return bmap; +error: + isl_mat_free(C); + isl_mat_free(C2); + isl_mat_free(T); + return bmap; +} + +static struct isl_basic_map *set_div_from_lower_bound( + struct isl_basic_map *bmap, int div, int ineq) +{ + unsigned total = 1 + isl_space_dim(bmap->dim, isl_dim_all); + + isl_seq_neg(bmap->div[div] + 1, bmap->ineq[ineq], total + bmap->n_div); + isl_int_set(bmap->div[div][0], bmap->ineq[ineq][total + div]); + isl_int_add(bmap->div[div][1], bmap->div[div][1], bmap->div[div][0]); + isl_int_sub_ui(bmap->div[div][1], bmap->div[div][1], 1); + isl_int_set_si(bmap->div[div][1 + total + div], 0); + + return bmap; +} + +/* Check whether it is ok to define a div based on an inequality. + * To avoid the introduction of circular definitions of divs, we + * do not allow such a definition if the resulting expression would refer to + * any other undefined divs or if any known div is defined in + * terms of the unknown div. + */ +static int ok_to_set_div_from_bound(struct isl_basic_map *bmap, + int div, int ineq) +{ + int j; + unsigned total = 1 + isl_space_dim(bmap->dim, isl_dim_all); + + /* Not defined in terms of unknown divs */ + for (j = 0; j < bmap->n_div; ++j) { + if (div == j) + continue; + if (isl_int_is_zero(bmap->ineq[ineq][total + j])) + continue; + if (isl_int_is_zero(bmap->div[j][0])) + return 0; + } + + /* No other div defined in terms of this one => avoid loops */ + for (j = 0; j < bmap->n_div; ++j) { + if (div == j) + continue; + if (isl_int_is_zero(bmap->div[j][0])) + continue; + if (!isl_int_is_zero(bmap->div[j][1 + total + div])) + return 0; + } + + return 1; +} + +/* Would an expression for div "div" based on inequality "ineq" of "bmap" + * be a better expression than the current one? + * + * If we do not have any expression yet, then any expression would be better. + * Otherwise we check if the last variable involved in the inequality + * (disregarding the div that it would define) is in an earlier position + * than the last variable involved in the current div expression. + */ +static int better_div_constraint(__isl_keep isl_basic_map *bmap, + int div, int ineq) +{ + unsigned total = 1 + isl_space_dim(bmap->dim, isl_dim_all); + int last_div; + int last_ineq; + + if (isl_int_is_zero(bmap->div[div][0])) + return 1; + + if (isl_seq_last_non_zero(bmap->ineq[ineq] + total + div + 1, + bmap->n_div - (div + 1)) >= 0) + return 0; + + last_ineq = isl_seq_last_non_zero(bmap->ineq[ineq], total + div); + last_div = isl_seq_last_non_zero(bmap->div[div] + 1, + total + bmap->n_div); + + return last_ineq < last_div; +} + +/* Given two constraints "k" and "l" that are opposite to each other, + * except for the constant term, check if we can use them + * to obtain an expression for one of the hitherto unknown divs or + * a "better" expression for a div for which we already have an expression. + * "sum" is the sum of the constant terms of the constraints. + * If this sum is strictly smaller than the coefficient of one + * of the divs, then this pair can be used define the div. + * To avoid the introduction of circular definitions of divs, we + * do not use the pair if the resulting expression would refer to + * any other undefined divs or if any known div is defined in + * terms of the unknown div. + */ +static struct isl_basic_map *check_for_div_constraints( + struct isl_basic_map *bmap, int k, int l, isl_int sum, int *progress) +{ + int i; + unsigned total = 1 + isl_space_dim(bmap->dim, isl_dim_all); + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->ineq[k][total + i])) + continue; + if (isl_int_abs_ge(sum, bmap->ineq[k][total + i])) + continue; + if (!better_div_constraint(bmap, i, k)) + continue; + if (!ok_to_set_div_from_bound(bmap, i, k)) + break; + if (isl_int_is_pos(bmap->ineq[k][total + i])) + bmap = set_div_from_lower_bound(bmap, i, k); + else + bmap = set_div_from_lower_bound(bmap, i, l); + if (progress) + *progress = 1; + break; + } + return bmap; +} + +__isl_give isl_basic_map *isl_basic_map_remove_duplicate_constraints( + __isl_take isl_basic_map *bmap, int *progress, int detect_divs) +{ + struct isl_constraint_index ci; + int k, l, h; + unsigned total = isl_basic_map_total_dim(bmap); + isl_int sum; + + if (!bmap || bmap->n_ineq <= 1) + return bmap; + + if (create_constraint_index(&ci, bmap) < 0) + return bmap; + + h = isl_seq_get_hash_bits(bmap->ineq[0] + 1, total, ci.bits); + ci.index[h] = &bmap->ineq[0]; + for (k = 1; k < bmap->n_ineq; ++k) { + h = hash_index(&ci, bmap, k); + if (!ci.index[h]) { + ci.index[h] = &bmap->ineq[k]; + continue; + } + if (progress) + *progress = 1; + l = ci.index[h] - &bmap->ineq[0]; + if (isl_int_lt(bmap->ineq[k][0], bmap->ineq[l][0])) + swap_inequality(bmap, k, l); + isl_basic_map_drop_inequality(bmap, k); + --k; + } + isl_int_init(sum); + for (k = 0; k < bmap->n_ineq-1; ++k) { + isl_seq_neg(bmap->ineq[k]+1, bmap->ineq[k]+1, total); + h = hash_index(&ci, bmap, k); + isl_seq_neg(bmap->ineq[k]+1, bmap->ineq[k]+1, total); + if (!ci.index[h]) + continue; + l = ci.index[h] - &bmap->ineq[0]; + isl_int_add(sum, bmap->ineq[k][0], bmap->ineq[l][0]); + if (isl_int_is_pos(sum)) { + if (detect_divs) + bmap = check_for_div_constraints(bmap, k, l, + sum, progress); + continue; + } + if (isl_int_is_zero(sum)) { + /* We need to break out of the loop after these + * changes since the contents of the hash + * will no longer be valid. + * Plus, we probably we want to regauss first. + */ + if (progress) + *progress = 1; + isl_basic_map_drop_inequality(bmap, l); + isl_basic_map_inequality_to_equality(bmap, k); + } else + bmap = isl_basic_map_set_to_empty(bmap); + break; + } + isl_int_clear(sum); + + constraint_index_free(&ci); + return bmap; +} + +/* Detect all pairs of inequalities that form an equality. + * + * isl_basic_map_remove_duplicate_constraints detects at most one such pair. + * Call it repeatedly while it is making progress. + */ +__isl_give isl_basic_map *isl_basic_map_detect_inequality_pairs( + __isl_take isl_basic_map *bmap, int *progress) +{ + int duplicate; + + do { + duplicate = 0; + bmap = isl_basic_map_remove_duplicate_constraints(bmap, + &duplicate, 0); + if (progress && duplicate) + *progress = 1; + } while (duplicate); + + return bmap; +} + +/* Eliminate knowns divs from constraints where they appear with + * a (positive or negative) unit coefficient. + * + * That is, replace + * + * floor(e/m) + f >= 0 + * + * by + * + * e + m f >= 0 + * + * and + * + * -floor(e/m) + f >= 0 + * + * by + * + * -e + m f + m - 1 >= 0 + * + * The first conversion is valid because floor(e/m) >= -f is equivalent + * to e/m >= -f because -f is an integral expression. + * The second conversion follows from the fact that + * + * -floor(e/m) = ceil(-e/m) = floor((-e + m - 1)/m) + * + * + * Note that one of the div constraints may have been eliminated + * due to being redundant with respect to the constraint that is + * being modified by this function. The modified constraint may + * no longer imply this div constraint, so we add it back to make + * sure we do not lose any information. + * + * We skip integral divs, i.e., those with denominator 1, as we would + * risk eliminating the div from the div constraints. We do not need + * to handle those divs here anyway since the div constraints will turn + * out to form an equality and this equality can then be use to eliminate + * the div from all constraints. + */ +static __isl_give isl_basic_map *eliminate_unit_divs( + __isl_take isl_basic_map *bmap, int *progress) +{ + int i, j; + isl_ctx *ctx; + unsigned total; + + if (!bmap) + return NULL; + + ctx = isl_basic_map_get_ctx(bmap); + total = 1 + isl_space_dim(bmap->dim, isl_dim_all); + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (isl_int_is_one(bmap->div[i][0])) + continue; + for (j = 0; j < bmap->n_ineq; ++j) { + int s; + + if (!isl_int_is_one(bmap->ineq[j][total + i]) && + !isl_int_is_negone(bmap->ineq[j][total + i])) + continue; + + *progress = 1; + + s = isl_int_sgn(bmap->ineq[j][total + i]); + isl_int_set_si(bmap->ineq[j][total + i], 0); + if (s < 0) + isl_seq_combine(bmap->ineq[j], + ctx->negone, bmap->div[i] + 1, + bmap->div[i][0], bmap->ineq[j], + total + bmap->n_div); + else + isl_seq_combine(bmap->ineq[j], + ctx->one, bmap->div[i] + 1, + bmap->div[i][0], bmap->ineq[j], + total + bmap->n_div); + if (s < 0) { + isl_int_add(bmap->ineq[j][0], + bmap->ineq[j][0], bmap->div[i][0]); + isl_int_sub_ui(bmap->ineq[j][0], + bmap->ineq[j][0], 1); + } + + bmap = isl_basic_map_extend_constraints(bmap, 0, 1); + if (isl_basic_map_add_div_constraint(bmap, i, s) < 0) + return isl_basic_map_free(bmap); + } + } + + return bmap; +} + +struct isl_basic_map *isl_basic_map_simplify(struct isl_basic_map *bmap) +{ + int progress = 1; + if (!bmap) + return NULL; + while (progress) { + progress = 0; + if (!bmap) + break; + if (isl_basic_map_plain_is_empty(bmap)) + break; + bmap = isl_basic_map_normalize_constraints(bmap); + bmap = remove_independent_vars_from_divs(bmap); + bmap = normalize_div_expressions(bmap); + bmap = remove_duplicate_divs(bmap, &progress); + bmap = eliminate_unit_divs(bmap, &progress); + bmap = eliminate_divs_eq(bmap, &progress); + bmap = eliminate_divs_ineq(bmap, &progress); + bmap = isl_basic_map_gauss(bmap, &progress); + /* requires equalities in normal form */ + bmap = normalize_divs(bmap, &progress); + bmap = isl_basic_map_remove_duplicate_constraints(bmap, + &progress, 1); + if (bmap && progress) + ISL_F_CLR(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS); + } + return bmap; +} + +struct isl_basic_set *isl_basic_set_simplify(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_simplify((struct isl_basic_map *)bset); +} + + +int isl_basic_map_is_div_constraint(__isl_keep isl_basic_map *bmap, + isl_int *constraint, unsigned div) +{ + unsigned pos; + + if (!bmap) + return -1; + + pos = 1 + isl_space_dim(bmap->dim, isl_dim_all) + div; + + if (isl_int_eq(constraint[pos], bmap->div[div][0])) { + int neg; + isl_int_sub(bmap->div[div][1], + bmap->div[div][1], bmap->div[div][0]); + isl_int_add_ui(bmap->div[div][1], bmap->div[div][1], 1); + neg = isl_seq_is_neg(constraint, bmap->div[div]+1, pos); + isl_int_sub_ui(bmap->div[div][1], bmap->div[div][1], 1); + isl_int_add(bmap->div[div][1], + bmap->div[div][1], bmap->div[div][0]); + if (!neg) + return 0; + if (isl_seq_first_non_zero(constraint+pos+1, + bmap->n_div-div-1) != -1) + return 0; + } else if (isl_int_abs_eq(constraint[pos], bmap->div[div][0])) { + if (!isl_seq_eq(constraint, bmap->div[div]+1, pos)) + return 0; + if (isl_seq_first_non_zero(constraint+pos+1, + bmap->n_div-div-1) != -1) + return 0; + } else + return 0; + + return 1; +} + +int isl_basic_set_is_div_constraint(__isl_keep isl_basic_set *bset, + isl_int *constraint, unsigned div) +{ + return isl_basic_map_is_div_constraint(bset, constraint, div); +} + + +/* If the only constraints a div d=floor(f/m) + * appears in are its two defining constraints + * + * f - m d >=0 + * -(f - (m - 1)) + m d >= 0 + * + * then it can safely be removed. + */ +static int div_is_redundant(struct isl_basic_map *bmap, int div) +{ + int i; + unsigned pos = 1 + isl_space_dim(bmap->dim, isl_dim_all) + div; + + for (i = 0; i < bmap->n_eq; ++i) + if (!isl_int_is_zero(bmap->eq[i][pos])) + return 0; + + for (i = 0; i < bmap->n_ineq; ++i) { + if (isl_int_is_zero(bmap->ineq[i][pos])) + continue; + if (!isl_basic_map_is_div_constraint(bmap, bmap->ineq[i], div)) + return 0; + } + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (!isl_int_is_zero(bmap->div[i][1+pos])) + return 0; + } + + return 1; +} + +/* + * Remove divs that don't occur in any of the constraints or other divs. + * These can arise when dropping constraints from a basic map or + * when the divs of a basic map have been temporarily aligned + * with the divs of another basic map. + */ +static struct isl_basic_map *remove_redundant_divs(struct isl_basic_map *bmap) +{ + int i; + + if (!bmap) + return NULL; + + for (i = bmap->n_div-1; i >= 0; --i) { + if (!div_is_redundant(bmap, i)) + continue; + bmap = isl_basic_map_drop_div(bmap, i); + } + return bmap; +} + +/* Mark "bmap" as final, without checking for obviously redundant + * integer divisions. This function should be used when "bmap" + * is known not to involve any such integer divisions. + */ +__isl_give isl_basic_map *isl_basic_map_mark_final( + __isl_take isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + ISL_F_SET(bmap, ISL_BASIC_SET_FINAL); + return bmap; +} + +/* Mark "bmap" as final, after removing obviously redundant integer divisions. + */ +struct isl_basic_map *isl_basic_map_finalize(struct isl_basic_map *bmap) +{ + bmap = remove_redundant_divs(bmap); + bmap = isl_basic_map_mark_final(bmap); + return bmap; +} + +struct isl_basic_set *isl_basic_set_finalize(struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_finalize((struct isl_basic_map *)bset); +} + +struct isl_set *isl_set_finalize(struct isl_set *set) +{ + int i; + + if (!set) + return NULL; + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_finalize(set->p[i]); + if (!set->p[i]) + goto error; + } + return set; +error: + isl_set_free(set); + return NULL; +} + +struct isl_map *isl_map_finalize(struct isl_map *map) +{ + int i; + + if (!map) + return NULL; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_finalize(map->p[i]); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + + +/* Remove definition of any div that is defined in terms of the given variable. + * The div itself is not removed. Functions such as + * eliminate_divs_ineq depend on the other divs remaining in place. + */ +static struct isl_basic_map *remove_dependent_vars(struct isl_basic_map *bmap, + int pos) +{ + int i; + + if (!bmap) + return NULL; + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (isl_int_is_zero(bmap->div[i][1+1+pos])) + continue; + bmap = isl_basic_map_mark_div_unknown(bmap, i); + if (!bmap) + return NULL; + } + return bmap; +} + +/* Eliminate the specified variables from the constraints using + * Fourier-Motzkin. The variables themselves are not removed. + */ +struct isl_basic_map *isl_basic_map_eliminate_vars( + struct isl_basic_map *bmap, unsigned pos, unsigned n) +{ + int d; + int i, j, k; + unsigned total; + int need_gauss = 0; + + if (n == 0) + return bmap; + if (!bmap) + return NULL; + total = isl_basic_map_total_dim(bmap); + + bmap = isl_basic_map_cow(bmap); + for (d = pos + n - 1; d >= 0 && d >= pos; --d) + bmap = remove_dependent_vars(bmap, d); + if (!bmap) + return NULL; + + for (d = pos + n - 1; + d >= 0 && d >= total - bmap->n_div && d >= pos; --d) + isl_seq_clr(bmap->div[d-(total-bmap->n_div)], 2+total); + for (d = pos + n - 1; d >= 0 && d >= pos; --d) { + int n_lower, n_upper; + if (!bmap) + return NULL; + for (i = 0; i < bmap->n_eq; ++i) { + if (isl_int_is_zero(bmap->eq[i][1+d])) + continue; + eliminate_var_using_equality(bmap, d, bmap->eq[i], 0, NULL); + isl_basic_map_drop_equality(bmap, i); + need_gauss = 1; + break; + } + if (i < bmap->n_eq) + continue; + n_lower = 0; + n_upper = 0; + for (i = 0; i < bmap->n_ineq; ++i) { + if (isl_int_is_pos(bmap->ineq[i][1+d])) + n_lower++; + else if (isl_int_is_neg(bmap->ineq[i][1+d])) + n_upper++; + } + bmap = isl_basic_map_extend_constraints(bmap, + 0, n_lower * n_upper); + if (!bmap) + goto error; + for (i = bmap->n_ineq - 1; i >= 0; --i) { + int last; + if (isl_int_is_zero(bmap->ineq[i][1+d])) + continue; + last = -1; + for (j = 0; j < i; ++j) { + if (isl_int_is_zero(bmap->ineq[j][1+d])) + continue; + last = j; + if (isl_int_sgn(bmap->ineq[i][1+d]) == + isl_int_sgn(bmap->ineq[j][1+d])) + continue; + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->ineq[k], bmap->ineq[i], + 1+total); + isl_seq_elim(bmap->ineq[k], bmap->ineq[j], + 1+d, 1+total, NULL); + } + isl_basic_map_drop_inequality(bmap, i); + i = last + 1; + } + if (n_lower > 0 && n_upper > 0) { + bmap = isl_basic_map_normalize_constraints(bmap); + bmap = isl_basic_map_remove_duplicate_constraints(bmap, + NULL, 0); + bmap = isl_basic_map_gauss(bmap, NULL); + bmap = isl_basic_map_remove_redundancies(bmap); + need_gauss = 0; + if (!bmap) + goto error; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + break; + } + } + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + if (need_gauss) + bmap = isl_basic_map_gauss(bmap, NULL); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_basic_set *isl_basic_set_eliminate_vars( + struct isl_basic_set *bset, unsigned pos, unsigned n) +{ + return (struct isl_basic_set *)isl_basic_map_eliminate_vars( + (struct isl_basic_map *)bset, pos, n); +} + +/* Eliminate the specified n dimensions starting at first from the + * constraints, without removing the dimensions from the space. + * If the set is rational, the dimensions are eliminated using Fourier-Motzkin. + * Otherwise, they are projected out and the original space is restored. + */ +__isl_give isl_basic_map *isl_basic_map_eliminate( + __isl_take isl_basic_map *bmap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_space *space; + + if (!bmap) + return NULL; + if (n == 0) + return bmap; + + if (first + n > isl_basic_map_dim(bmap, type) || first + n < first) + isl_die(bmap->ctx, isl_error_invalid, + "index out of bounds", goto error); + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) { + first += isl_basic_map_offset(bmap, type) - 1; + bmap = isl_basic_map_eliminate_vars(bmap, first, n); + return isl_basic_map_finalize(bmap); + } + + space = isl_basic_map_get_space(bmap); + bmap = isl_basic_map_project_out(bmap, type, first, n); + bmap = isl_basic_map_insert_dims(bmap, type, first, n); + bmap = isl_basic_map_reset_space(bmap, space); + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_eliminate( + __isl_take isl_basic_set *bset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_basic_map_eliminate(bset, type, first, n); +} + +/* Remove all constraints from "bmap" that reference any unknown local + * variables (directly or indirectly). + * + * Dropping all constraints on a local variable will make it redundant, + * so it will get removed implicitly by + * isl_basic_map_drop_constraints_involving_dims. Some other local + * variables may also end up becoming redundant if they only appear + * in constraints together with the unknown local variable. + * Therefore, start over after calling + * isl_basic_map_drop_constraints_involving_dims. + */ +__isl_give isl_basic_map *isl_basic_map_drop_constraint_involving_unknown_divs( + __isl_take isl_basic_map *bmap) +{ + isl_bool known; + int i, n_div, o_div; + + known = isl_basic_map_divs_known(bmap); + if (known < 0) + return isl_basic_map_free(bmap); + if (known) + return bmap; + + n_div = isl_basic_map_dim(bmap, isl_dim_div); + o_div = isl_basic_map_offset(bmap, isl_dim_div) - 1; + + for (i = 0; i < n_div; ++i) { + known = isl_basic_map_div_is_known(bmap, i); + if (known < 0) + return isl_basic_map_free(bmap); + if (known) + continue; + bmap = remove_dependent_vars(bmap, o_div + i); + bmap = isl_basic_map_drop_constraints_involving_dims(bmap, + isl_dim_div, i, 1); + if (!bmap) + return NULL; + n_div = isl_basic_map_dim(bmap, isl_dim_div); + i = -1; + } + + return bmap; +} + +/* Remove all constraints from "map" that reference any unknown local + * variables (directly or indirectly). + * + * Since constraints may get dropped from the basic maps, + * they may no longer be disjoint from each other. + */ +__isl_give isl_map *isl_map_drop_constraint_involving_unknown_divs( + __isl_take isl_map *map) +{ + int i; + isl_bool known; + + known = isl_map_divs_known(map); + if (known < 0) + return isl_map_free(map); + if (known) + return map; + + map = isl_map_cow(map); + if (!map) + return NULL; + + for (i = 0; i < map->n; ++i) { + map->p[i] = + isl_basic_map_drop_constraint_involving_unknown_divs( + map->p[i]); + if (!map->p[i]) + return isl_map_free(map); + } + + if (map->n > 1) + ISL_F_CLR(map, ISL_MAP_DISJOINT); + + return map; +} + +/* Don't assume equalities are in order, because align_divs + * may have changed the order of the divs. + */ +static void compute_elimination_index(struct isl_basic_map *bmap, int *elim) +{ + int d, i; + unsigned total; + + total = isl_space_dim(bmap->dim, isl_dim_all); + for (d = 0; d < total; ++d) + elim[d] = -1; + for (i = 0; i < bmap->n_eq; ++i) { + for (d = total - 1; d >= 0; --d) { + if (isl_int_is_zero(bmap->eq[i][1+d])) + continue; + elim[d] = i; + break; + } + } +} + +static void set_compute_elimination_index(struct isl_basic_set *bset, int *elim) +{ + compute_elimination_index((struct isl_basic_map *)bset, elim); +} + +static int reduced_using_equalities(isl_int *dst, isl_int *src, + struct isl_basic_map *bmap, int *elim) +{ + int d; + int copied = 0; + unsigned total; + + total = isl_space_dim(bmap->dim, isl_dim_all); + for (d = total - 1; d >= 0; --d) { + if (isl_int_is_zero(src[1+d])) + continue; + if (elim[d] == -1) + continue; + if (!copied) { + isl_seq_cpy(dst, src, 1 + total); + copied = 1; + } + isl_seq_elim(dst, bmap->eq[elim[d]], 1 + d, 1 + total, NULL); + } + return copied; +} + +static int set_reduced_using_equalities(isl_int *dst, isl_int *src, + struct isl_basic_set *bset, int *elim) +{ + return reduced_using_equalities(dst, src, + (struct isl_basic_map *)bset, elim); +} + +static struct isl_basic_set *isl_basic_set_reduce_using_equalities( + struct isl_basic_set *bset, struct isl_basic_set *context) +{ + int i; + int *elim; + + if (!bset || !context) + goto error; + + if (context->n_eq == 0) { + isl_basic_set_free(context); + return bset; + } + + bset = isl_basic_set_cow(bset); + if (!bset) + goto error; + + elim = isl_alloc_array(bset->ctx, int, isl_basic_set_n_dim(bset)); + if (!elim) + goto error; + set_compute_elimination_index(context, elim); + for (i = 0; i < bset->n_eq; ++i) + set_reduced_using_equalities(bset->eq[i], bset->eq[i], + context, elim); + for (i = 0; i < bset->n_ineq; ++i) + set_reduced_using_equalities(bset->ineq[i], bset->ineq[i], + context, elim); + isl_basic_set_free(context); + free(elim); + bset = isl_basic_set_simplify(bset); + bset = isl_basic_set_finalize(bset); + return bset; +error: + isl_basic_set_free(bset); + isl_basic_set_free(context); + return NULL; +} + +/* For each inequality in "ineq" that is a shifted (more relaxed) + * copy of an inequality in "context", mark the corresponding entry + * in "row" with -1. + * If an inequality only has a non-negative constant term, then + * mark it as well. + */ +static isl_stat mark_shifted_constraints(__isl_keep isl_mat *ineq, + __isl_keep isl_basic_set *context, int *row) +{ + struct isl_constraint_index ci; + int n_ineq; + unsigned total; + int k; + + if (!ineq || !context) + return isl_stat_error; + if (context->n_ineq == 0) + return isl_stat_ok; + if (setup_constraint_index(&ci, context) < 0) + return isl_stat_error; + + n_ineq = isl_mat_rows(ineq); + total = isl_mat_cols(ineq) - 1; + for (k = 0; k < n_ineq; ++k) { + int l; + isl_bool redundant; + + l = isl_seq_first_non_zero(ineq->row[k] + 1, total); + if (l < 0 && isl_int_is_nonneg(ineq->row[k][0])) { + row[k] = -1; + continue; + } + redundant = constraint_index_is_redundant(&ci, ineq->row[k]); + if (redundant < 0) + goto error; + if (!redundant) + continue; + row[k] = -1; + } + constraint_index_free(&ci); + return isl_stat_ok; +error: + constraint_index_free(&ci); + return isl_stat_error; +} + +static struct isl_basic_set *remove_shifted_constraints( + struct isl_basic_set *bset, struct isl_basic_set *context) +{ + struct isl_constraint_index ci; + int k; + + if (!bset || !context) + return bset; + + if (context->n_ineq == 0) + return bset; + if (setup_constraint_index(&ci, context) < 0) + return bset; + + for (k = 0; k < bset->n_ineq; ++k) { + isl_bool redundant; + + redundant = constraint_index_is_redundant(&ci, bset->ineq[k]); + if (redundant < 0) + goto error; + if (!redundant) + continue; + bset = isl_basic_set_cow(bset); + if (!bset) + goto error; + isl_basic_set_drop_inequality(bset, k); + --k; + } + constraint_index_free(&ci); + return bset; +error: + constraint_index_free(&ci); + return bset; +} + +/* Remove constraints from "bmap" that are identical to constraints + * in "context" or that are more relaxed (greater constant term). + * + * We perform the test for shifted copies on the pure constraints + * in remove_shifted_constraints. + */ +static __isl_give isl_basic_map *isl_basic_map_remove_shifted_constraints( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_map *context) +{ + isl_basic_set *bset, *bset_context; + + if (!bmap || !context) + goto error; + + if (bmap->n_ineq == 0 || context->n_ineq == 0) { + isl_basic_map_free(context); + return bmap; + } + + context = isl_basic_map_align_divs(context, bmap); + bmap = isl_basic_map_align_divs(bmap, context); + + bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap)); + bset_context = isl_basic_map_underlying_set(context); + bset = remove_shifted_constraints(bset, bset_context); + isl_basic_set_free(bset_context); + + bmap = isl_basic_map_overlying_set(bset, bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + isl_basic_map_free(context); + return NULL; +} + +/* Does the (linear part of a) constraint "c" involve any of the "len" + * "relevant" dimensions? + */ +static int is_related(isl_int *c, int len, int *relevant) +{ + int i; + + for (i = 0; i < len; ++i) { + if (!relevant[i]) + continue; + if (!isl_int_is_zero(c[i])) + return 1; + } + + return 0; +} + +/* Drop constraints from "bmap" that do not involve any of + * the dimensions marked "relevant". + */ +static __isl_give isl_basic_map *drop_unrelated_constraints( + __isl_take isl_basic_map *bmap, int *relevant) +{ + int i, dim; + + dim = isl_basic_map_dim(bmap, isl_dim_all); + for (i = 0; i < dim; ++i) + if (!relevant[i]) + break; + if (i >= dim) + return bmap; + + for (i = bmap->n_eq - 1; i >= 0; --i) + if (!is_related(bmap->eq[i] + 1, dim, relevant)) { + bmap = isl_basic_map_cow(bmap); + if (isl_basic_map_drop_equality(bmap, i) < 0) + return isl_basic_map_free(bmap); + } + + for (i = bmap->n_ineq - 1; i >= 0; --i) + if (!is_related(bmap->ineq[i] + 1, dim, relevant)) { + bmap = isl_basic_map_cow(bmap); + if (isl_basic_map_drop_inequality(bmap, i) < 0) + return isl_basic_map_free(bmap); + } + + return bmap; +} + +/* Update the groups in "group" based on the (linear part of a) constraint "c". + * + * In particular, for any variable involved in the constraint, + * find the actual group id from before and replace the group + * of the corresponding variable by the minimal group of all + * the variables involved in the constraint considered so far + * (if this minimum is smaller) or replace the minimum by this group + * (if the minimum is larger). + * + * At the end, all the variables in "c" will (indirectly) point + * to the minimal of the groups that they referred to originally. + */ +static void update_groups(int dim, int *group, isl_int *c) +{ + int j; + int min = dim; + + for (j = 0; j < dim; ++j) { + if (isl_int_is_zero(c[j])) + continue; + while (group[j] >= 0 && group[group[j]] != group[j]) + group[j] = group[group[j]]; + if (group[j] == min) + continue; + if (group[j] < min) { + if (min >= 0 && min < dim) + group[min] = group[j]; + min = group[j]; + } else + group[group[j]] = min; + } +} + +/* Allocate an array of groups of variables, one for each variable + * in "context", initialized to zero. + */ +static int *alloc_groups(__isl_keep isl_basic_set *context) +{ + isl_ctx *ctx; + int dim; + + dim = isl_basic_set_dim(context, isl_dim_set); + ctx = isl_basic_set_get_ctx(context); + return isl_calloc_array(ctx, int, dim); +} + +/* Drop constraints from "bmap" that only involve variables that are + * not related to any of the variables marked with a "-1" in "group". + * + * We construct groups of variables that collect variables that + * (indirectly) appear in some common constraint of "bmap". + * Each group is identified by the first variable in the group, + * except for the special group of variables that was already identified + * in the input as -1 (or are related to those variables). + * If group[i] is equal to i (or -1), then the group of i is i (or -1), + * otherwise the group of i is the group of group[i]. + * + * We first initialize groups for the remaining variables. + * Then we iterate over the constraints of "bmap" and update the + * group of the variables in the constraint by the smallest group. + * Finally, we resolve indirect references to groups by running over + * the variables. + * + * After computing the groups, we drop constraints that do not involve + * any variables in the -1 group. + */ +__isl_give isl_basic_map *isl_basic_map_drop_unrelated_constraints( + __isl_take isl_basic_map *bmap, __isl_take int *group) +{ + int dim; + int i; + int last; + + if (!bmap) + return NULL; + + dim = isl_basic_map_dim(bmap, isl_dim_all); + + last = -1; + for (i = 0; i < dim; ++i) + if (group[i] >= 0) + last = group[i] = i; + if (last < 0) { + free(group); + return bmap; + } + + for (i = 0; i < bmap->n_eq; ++i) + update_groups(dim, group, bmap->eq[i] + 1); + for (i = 0; i < bmap->n_ineq; ++i) + update_groups(dim, group, bmap->ineq[i] + 1); + + for (i = 0; i < dim; ++i) + if (group[i] >= 0) + group[i] = group[group[i]]; + + for (i = 0; i < dim; ++i) + group[i] = group[i] == -1; + + bmap = drop_unrelated_constraints(bmap, group); + + free(group); + return bmap; +} + +/* Drop constraints from "context" that are irrelevant for computing + * the gist of "bset". + * + * In particular, drop constraints in variables that are not related + * to any of the variables involved in the constraints of "bset" + * in the sense that there is no sequence of constraints that connects them. + * + * We first mark all variables that appear in "bset" as belonging + * to a "-1" group and then continue with group_and_drop_irrelevant_constraints. + */ +static __isl_give isl_basic_set *drop_irrelevant_constraints( + __isl_take isl_basic_set *context, __isl_keep isl_basic_set *bset) +{ + int *group; + int dim; + int i, j; + + if (!context || !bset) + return isl_basic_set_free(context); + + group = alloc_groups(context); + + if (!group) + return isl_basic_set_free(context); + + dim = isl_basic_set_dim(bset, isl_dim_set); + for (i = 0; i < dim; ++i) { + for (j = 0; j < bset->n_eq; ++j) + if (!isl_int_is_zero(bset->eq[j][1 + i])) + break; + if (j < bset->n_eq) { + group[i] = -1; + continue; + } + for (j = 0; j < bset->n_ineq; ++j) + if (!isl_int_is_zero(bset->ineq[j][1 + i])) + break; + if (j < bset->n_ineq) + group[i] = -1; + } + + return isl_basic_map_drop_unrelated_constraints(context, group); +} + +/* Drop constraints from "context" that are irrelevant for computing + * the gist of the inequalities "ineq". + * Inequalities in "ineq" for which the corresponding element of row + * is set to -1 have already been marked for removal and should be ignored. + * + * In particular, drop constraints in variables that are not related + * to any of the variables involved in "ineq" + * in the sense that there is no sequence of constraints that connects them. + * + * We first mark all variables that appear in "bset" as belonging + * to a "-1" group and then continue with group_and_drop_irrelevant_constraints. + */ +static __isl_give isl_basic_set *drop_irrelevant_constraints_marked( + __isl_take isl_basic_set *context, __isl_keep isl_mat *ineq, int *row) +{ + int *group; + int dim; + int i, j, n; + + if (!context || !ineq) + return isl_basic_set_free(context); + + group = alloc_groups(context); + + if (!group) + return isl_basic_set_free(context); + + dim = isl_basic_set_dim(context, isl_dim_set); + n = isl_mat_rows(ineq); + for (i = 0; i < dim; ++i) { + for (j = 0; j < n; ++j) { + if (row[j] < 0) + continue; + if (!isl_int_is_zero(ineq->row[j][1 + i])) + break; + } + if (j < n) + group[i] = -1; + } + + return isl_basic_map_drop_unrelated_constraints(context, group); +} + +/* Do all "n" entries of "row" contain a negative value? + */ +static int all_neg(int *row, int n) +{ + int i; + + for (i = 0; i < n; ++i) + if (row[i] >= 0) + return 0; + + return 1; +} + +/* Update the inequalities in "bset" based on the information in "row" + * and "tab". + * + * In particular, the array "row" contains either -1, meaning that + * the corresponding inequality of "bset" is redundant, or the index + * of an inequality in "tab". + * + * If the row entry is -1, then drop the inequality. + * Otherwise, if the constraint is marked redundant in the tableau, + * then drop the inequality. Similarly, if it is marked as an equality + * in the tableau, then turn the inequality into an equality and + * perform Gaussian elimination. + */ +static __isl_give isl_basic_set *update_ineq(__isl_take isl_basic_set *bset, + __isl_keep int *row, struct isl_tab *tab) +{ + int i; + unsigned n_ineq; + unsigned n_eq; + int found_equality = 0; + + if (!bset) + return NULL; + if (tab && tab->empty) + return isl_basic_set_set_to_empty(bset); + + n_ineq = bset->n_ineq; + for (i = n_ineq - 1; i >= 0; --i) { + if (row[i] < 0) { + if (isl_basic_set_drop_inequality(bset, i) < 0) + return isl_basic_set_free(bset); + continue; + } + if (!tab) + continue; + n_eq = tab->n_eq; + if (isl_tab_is_equality(tab, n_eq + row[i])) { + isl_basic_map_inequality_to_equality(bset, i); + found_equality = 1; + } else if (isl_tab_is_redundant(tab, n_eq + row[i])) { + if (isl_basic_set_drop_inequality(bset, i) < 0) + return isl_basic_set_free(bset); + } + } + + if (found_equality) + bset = isl_basic_set_gauss(bset, NULL); + bset = isl_basic_set_finalize(bset); + return bset; +} + +/* Update the inequalities in "bset" based on the information in "row" + * and "tab" and free all arguments (other than "bset"). + */ +static __isl_give isl_basic_set *update_ineq_free( + __isl_take isl_basic_set *bset, __isl_take isl_mat *ineq, + __isl_take isl_basic_set *context, __isl_take int *row, + struct isl_tab *tab) +{ + isl_mat_free(ineq); + isl_basic_set_free(context); + + bset = update_ineq(bset, row, tab); + + free(row); + isl_tab_free(tab); + return bset; +} + +/* Remove all information from bset that is redundant in the context + * of context. + * "ineq" contains the (possibly transformed) inequalities of "bset", + * in the same order. + * The (explicit) equalities of "bset" are assumed to have been taken + * into account by the transformation such that only the inequalities + * are relevant. + * "context" is assumed not to be empty. + * + * "row" keeps track of the constraint index of a "bset" inequality in "tab". + * A value of -1 means that the inequality is obviously redundant and may + * not even appear in "tab". + * + * We first mark the inequalities of "bset" + * that are obviously redundant with respect to some inequality in "context". + * Then we remove those constraints from "context" that have become + * irrelevant for computing the gist of "bset". + * Note that this removal of constraints cannot be replaced by + * a factorization because factors in "bset" may still be connected + * to each other through constraints in "context". + * + * If there are any inequalities left, we construct a tableau for + * the context and then add the inequalities of "bset". + * Before adding these inequalities, we freeze all constraints such that + * they won't be considered redundant in terms of the constraints of "bset". + * Then we detect all redundant constraints (among the + * constraints that weren't frozen), first by checking for redundancy in the + * the tableau and then by checking if replacing a constraint by its negation + * would lead to an empty set. This last step is fairly expensive + * and could be optimized by more reuse of the tableau. + * Finally, we update bset according to the results. + */ +static __isl_give isl_basic_set *uset_gist_full(__isl_take isl_basic_set *bset, + __isl_take isl_mat *ineq, __isl_take isl_basic_set *context) +{ + int i, r; + int *row = NULL; + isl_ctx *ctx; + isl_basic_set *combined = NULL; + struct isl_tab *tab = NULL; + unsigned n_eq, context_ineq; + unsigned total; + + if (!bset || !ineq || !context) + goto error; + + if (bset->n_ineq == 0 || isl_basic_set_plain_is_universe(context)) { + isl_basic_set_free(context); + isl_mat_free(ineq); + return bset; + } + + ctx = isl_basic_set_get_ctx(context); + row = isl_calloc_array(ctx, int, bset->n_ineq); + if (!row) + goto error; + + if (mark_shifted_constraints(ineq, context, row) < 0) + goto error; + if (all_neg(row, bset->n_ineq)) + return update_ineq_free(bset, ineq, context, row, NULL); + + context = drop_irrelevant_constraints_marked(context, ineq, row); + if (!context) + goto error; + if (isl_basic_set_plain_is_universe(context)) + return update_ineq_free(bset, ineq, context, row, NULL); + + n_eq = context->n_eq; + context_ineq = context->n_ineq; + combined = isl_basic_set_cow(isl_basic_set_copy(context)); + combined = isl_basic_set_extend_constraints(combined, 0, bset->n_ineq); + tab = isl_tab_from_basic_set(combined, 0); + for (i = 0; i < context_ineq; ++i) + if (isl_tab_freeze_constraint(tab, n_eq + i) < 0) + goto error; + if (isl_tab_extend_cons(tab, bset->n_ineq) < 0) + goto error; + r = context_ineq; + for (i = 0; i < bset->n_ineq; ++i) { + if (row[i] < 0) + continue; + combined = isl_basic_set_add_ineq(combined, ineq->row[i]); + if (isl_tab_add_ineq(tab, ineq->row[i]) < 0) + goto error; + row[i] = r++; + } + if (isl_tab_detect_implicit_equalities(tab) < 0) + goto error; + if (isl_tab_detect_redundant(tab) < 0) + goto error; + total = isl_basic_set_total_dim(bset); + for (i = bset->n_ineq - 1; i >= 0; --i) { + isl_basic_set *test; + int is_empty; + + if (row[i] < 0) + continue; + r = row[i]; + if (tab->con[n_eq + r].is_redundant) + continue; + test = isl_basic_set_dup(combined); + if (isl_inequality_negate(test, r) < 0) + test = isl_basic_set_free(test); + test = isl_basic_set_update_from_tab(test, tab); + is_empty = isl_basic_set_is_empty(test); + isl_basic_set_free(test); + if (is_empty < 0) + goto error; + if (is_empty) + tab->con[n_eq + r].is_redundant = 1; + } + bset = update_ineq_free(bset, ineq, context, row, tab); + if (bset) { + ISL_F_SET(bset, ISL_BASIC_SET_NO_IMPLICIT); + ISL_F_SET(bset, ISL_BASIC_SET_NO_REDUNDANT); + } + + isl_basic_set_free(combined); + return bset; +error: + free(row); + isl_mat_free(ineq); + isl_tab_free(tab); + isl_basic_set_free(combined); + isl_basic_set_free(context); + isl_basic_set_free(bset); + return NULL; +} + +/* Extract the inequalities of "bset" as an isl_mat. + */ +static __isl_give isl_mat *extract_ineq(__isl_keep isl_basic_set *bset) +{ + unsigned total; + isl_ctx *ctx; + isl_mat *ineq; + + if (!bset) + return NULL; + + ctx = isl_basic_set_get_ctx(bset); + total = isl_basic_set_total_dim(bset); + ineq = isl_mat_sub_alloc6(ctx, bset->ineq, 0, bset->n_ineq, + 0, 1 + total); + + return ineq; +} + +/* Remove all information from "bset" that is redundant in the context + * of "context", for the case where both "bset" and "context" are + * full-dimensional. + */ +static __isl_give isl_basic_set *uset_gist_uncompressed( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *context) +{ + isl_mat *ineq; + + ineq = extract_ineq(bset); + return uset_gist_full(bset, ineq, context); +} + +/* Remove all information from "bset" that is redundant in the context + * of "context", for the case where the combined equalities of + * "bset" and "context" allow for a compression that can be obtained + * by preapplication of "T". + * + * "bset" itself is not transformed by "T". Instead, the inequalities + * are extracted from "bset" and those are transformed by "T". + * uset_gist_full then determines which of the transformed inequalities + * are redundant with respect to the transformed "context" and removes + * the corresponding inequalities from "bset". + * + * After preapplying "T" to the inequalities, any common factor is + * removed from the coefficients. If this results in a tightening + * of the constant term, then the same tightening is applied to + * the corresponding untransformed inequality in "bset". + * That is, if after plugging in T, a constraint f(x) >= 0 is of the form + * + * g f'(x) + r >= 0 + * + * with 0 <= r < g, then it is equivalent to + * + * f'(x) >= 0 + * + * This means that f(x) >= 0 is equivalent to f(x) - r >= 0 in the affine + * subspace compressed by T since the latter would be transformed to + * + * g f'(x) >= 0 + */ +static __isl_give isl_basic_set *uset_gist_compressed( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *context, + __isl_take isl_mat *T) +{ + isl_ctx *ctx; + isl_mat *ineq; + int i, n_row, n_col; + isl_int rem; + + ineq = extract_ineq(bset); + ineq = isl_mat_product(ineq, isl_mat_copy(T)); + context = isl_basic_set_preimage(context, T); + + if (!ineq || !context) + goto error; + if (isl_basic_set_plain_is_empty(context)) { + isl_mat_free(ineq); + isl_basic_set_free(context); + return isl_basic_set_set_to_empty(bset); + } + + ctx = isl_mat_get_ctx(ineq); + n_row = isl_mat_rows(ineq); + n_col = isl_mat_cols(ineq); + isl_int_init(rem); + for (i = 0; i < n_row; ++i) { + isl_seq_gcd(ineq->row[i] + 1, n_col - 1, &ctx->normalize_gcd); + if (isl_int_is_zero(ctx->normalize_gcd)) + continue; + if (isl_int_is_one(ctx->normalize_gcd)) + continue; + isl_seq_scale_down(ineq->row[i] + 1, ineq->row[i] + 1, + ctx->normalize_gcd, n_col - 1); + isl_int_fdiv_r(rem, ineq->row[i][0], ctx->normalize_gcd); + isl_int_fdiv_q(ineq->row[i][0], + ineq->row[i][0], ctx->normalize_gcd); + if (isl_int_is_zero(rem)) + continue; + bset = isl_basic_set_cow(bset); + if (!bset) + break; + isl_int_sub(bset->ineq[i][0], bset->ineq[i][0], rem); + } + isl_int_clear(rem); + + return uset_gist_full(bset, ineq, context); +error: + isl_mat_free(ineq); + isl_basic_set_free(context); + isl_basic_set_free(bset); + return NULL; +} + +/* Project "bset" onto the variables that are involved in "template". + */ +static __isl_give isl_basic_set *project_onto_involved( + __isl_take isl_basic_set *bset, __isl_keep isl_basic_set *template) +{ + int i, n; + + if (!bset || !template) + return isl_basic_set_free(bset); + + n = isl_basic_set_dim(template, isl_dim_set); + + for (i = 0; i < n; ++i) { + isl_bool involved; + + involved = isl_basic_set_involves_dims(template, + isl_dim_set, i, 1); + if (involved < 0) + return isl_basic_set_free(bset); + if (involved) + continue; + bset = isl_basic_set_eliminate_vars(bset, i, 1); + } + + return bset; +} + +/* Remove all information from bset that is redundant in the context + * of context. In particular, equalities that are linear combinations + * of those in context are removed. Then the inequalities that are + * redundant in the context of the equalities and inequalities of + * context are removed. + * + * First of all, we drop those constraints from "context" + * that are irrelevant for computing the gist of "bset". + * Alternatively, we could factorize the intersection of "context" and "bset". + * + * We first compute the intersection of the integer affine hulls + * of "bset" and "context", + * compute the gist inside this intersection and then reduce + * the constraints with respect to the equalities of the context + * that only involve variables already involved in the input. + * + * If two constraints are mutually redundant, then uset_gist_full + * will remove the second of those constraints. We therefore first + * sort the constraints so that constraints not involving existentially + * quantified variables are given precedence over those that do. + * We have to perform this sorting before the variable compression, + * because that may effect the order of the variables. + */ +static __isl_give isl_basic_set *uset_gist(__isl_take isl_basic_set *bset, + __isl_take isl_basic_set *context) +{ + isl_mat *eq; + isl_mat *T; + isl_basic_set *aff; + isl_basic_set *aff_context; + unsigned total; + + if (!bset || !context) + goto error; + + context = drop_irrelevant_constraints(context, bset); + + bset = isl_basic_set_detect_equalities(bset); + aff = isl_basic_set_copy(bset); + aff = isl_basic_set_plain_affine_hull(aff); + context = isl_basic_set_detect_equalities(context); + aff_context = isl_basic_set_copy(context); + aff_context = isl_basic_set_plain_affine_hull(aff_context); + aff = isl_basic_set_intersect(aff, aff_context); + if (!aff) + goto error; + if (isl_basic_set_plain_is_empty(aff)) { + isl_basic_set_free(bset); + isl_basic_set_free(context); + return aff; + } + bset = isl_basic_set_sort_constraints(bset); + if (aff->n_eq == 0) { + isl_basic_set_free(aff); + return uset_gist_uncompressed(bset, context); + } + total = isl_basic_set_total_dim(bset); + eq = isl_mat_sub_alloc6(bset->ctx, aff->eq, 0, aff->n_eq, 0, 1 + total); + eq = isl_mat_cow(eq); + T = isl_mat_variable_compression(eq, NULL); + isl_basic_set_free(aff); + if (T && T->n_col == 0) { + isl_mat_free(T); + isl_basic_set_free(context); + return isl_basic_set_set_to_empty(bset); + } + + aff_context = isl_basic_set_affine_hull(isl_basic_set_copy(context)); + aff_context = project_onto_involved(aff_context, bset); + + bset = uset_gist_compressed(bset, context, T); + bset = isl_basic_set_reduce_using_equalities(bset, aff_context); + + if (bset) { + ISL_F_SET(bset, ISL_BASIC_SET_NO_IMPLICIT); + ISL_F_SET(bset, ISL_BASIC_SET_NO_REDUNDANT); + } + + return bset; +error: + isl_basic_set_free(bset); + isl_basic_set_free(context); + return NULL; +} + +/* Return the number of equality constraints in "bmap" that involve + * local variables. This function assumes that Gaussian elimination + * has been applied to the equality constraints. + */ +static int n_div_eq(__isl_keep isl_basic_map *bmap) +{ + int i; + int total, n_div; + + if (!bmap) + return -1; + + if (bmap->n_eq == 0) + return 0; + + total = isl_basic_map_dim(bmap, isl_dim_all); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + total -= n_div; + + for (i = 0; i < bmap->n_eq; ++i) + if (isl_seq_first_non_zero(bmap->eq[i] + 1 + total, + n_div) == -1) + return i; + + return bmap->n_eq; +} + +/* Construct a basic map in "space" defined by the equality constraints in "eq". + * The constraints are assumed not to involve any local variables. + */ +static __isl_give isl_basic_map *basic_map_from_equalities( + __isl_take isl_space *space, __isl_take isl_mat *eq) +{ + int i, k; + isl_basic_map *bmap = NULL; + + if (!space || !eq) + goto error; + + if (1 + isl_space_dim(space, isl_dim_all) != eq->n_col) + isl_die(isl_space_get_ctx(space), isl_error_internal, + "unexpected number of columns", goto error); + + bmap = isl_basic_map_alloc_space(isl_space_copy(space), + 0, eq->n_row, 0); + for (i = 0; i < eq->n_row; ++i) { + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->eq[k], eq->row[i], eq->n_col); + } + + isl_space_free(space); + isl_mat_free(eq); + return bmap; +error: + isl_space_free(space); + isl_mat_free(eq); + isl_basic_map_free(bmap); + return NULL; +} + +/* Construct and return a variable compression based on the equality + * constraints in "bmap1" and "bmap2" that do not involve the local variables. + * "n1" is the number of (initial) equality constraints in "bmap1" + * that do involve local variables. + * "n2" is the number of (initial) equality constraints in "bmap2" + * that do involve local variables. + * "total" is the total number of other variables. + * This function assumes that Gaussian elimination + * has been applied to the equality constraints in both "bmap1" and "bmap2" + * such that the equality constraints not involving local variables + * are those that start at "n1" or "n2". + * + * If either of "bmap1" and "bmap2" does not have such equality constraints, + * then simply compute the compression based on the equality constraints + * in the other basic map. + * Otherwise, combine the equality constraints from both into a new + * basic map such that Gaussian elimination can be applied to this combination + * and then construct a variable compression from the resulting + * equality constraints. + */ +static __isl_give isl_mat *combined_variable_compression( + __isl_keep isl_basic_map *bmap1, int n1, + __isl_keep isl_basic_map *bmap2, int n2, int total) +{ + isl_ctx *ctx; + isl_mat *E1, *E2, *V; + isl_basic_map *bmap; + + ctx = isl_basic_map_get_ctx(bmap1); + if (bmap1->n_eq == n1) { + E2 = isl_mat_sub_alloc6(ctx, bmap2->eq, + n2, bmap2->n_eq - n2, 0, 1 + total); + return isl_mat_variable_compression(E2, NULL); + } + if (bmap2->n_eq == n2) { + E1 = isl_mat_sub_alloc6(ctx, bmap1->eq, + n1, bmap1->n_eq - n1, 0, 1 + total); + return isl_mat_variable_compression(E1, NULL); + } + E1 = isl_mat_sub_alloc6(ctx, bmap1->eq, + n1, bmap1->n_eq - n1, 0, 1 + total); + E2 = isl_mat_sub_alloc6(ctx, bmap2->eq, + n2, bmap2->n_eq - n2, 0, 1 + total); + E1 = isl_mat_concat(E1, E2); + bmap = basic_map_from_equalities(isl_basic_map_get_space(bmap1), E1); + bmap = isl_basic_map_gauss(bmap, NULL); + if (!bmap) + return NULL; + E1 = isl_mat_sub_alloc6(ctx, bmap->eq, 0, bmap->n_eq, 0, 1 + total); + V = isl_mat_variable_compression(E1, NULL); + isl_basic_map_free(bmap); + + return V; +} + +/* Extract the stride constraints from "bmap", compressed + * with respect to both the stride constraints in "context" and + * the remaining equality constraints in both "bmap" and "context". + * "bmap_n_eq" is the number of (initial) stride constraints in "bmap". + * "context_n_eq" is the number of (initial) stride constraints in "context". + * + * Let x be all variables in "bmap" (and "context") other than the local + * variables. First compute a variable compression + * + * x = V x' + * + * based on the non-stride equality constraints in "bmap" and "context". + * Consider the stride constraints of "context", + * + * A(x) + B(y) = 0 + * + * with y the local variables and plug in the variable compression, + * resulting in + * + * A(V x') + B(y) = 0 + * + * Use these constraints to compute a parameter compression on x' + * + * x' = T x'' + * + * Now consider the stride constraints of "bmap" + * + * C(x) + D(y) = 0 + * + * and plug in x = V*T x''. + * That is, return A = [C*V*T D]. + */ +static __isl_give isl_mat *extract_compressed_stride_constraints( + __isl_keep isl_basic_map *bmap, int bmap_n_eq, + __isl_keep isl_basic_map *context, int context_n_eq) +{ + int total, n_div; + isl_ctx *ctx; + isl_mat *A, *B, *T, *V; + + total = isl_basic_map_dim(context, isl_dim_all); + n_div = isl_basic_map_dim(context, isl_dim_div); + total -= n_div; + + ctx = isl_basic_map_get_ctx(bmap); + + V = combined_variable_compression(bmap, bmap_n_eq, + context, context_n_eq, total); + + A = isl_mat_sub_alloc6(ctx, context->eq, 0, context_n_eq, 0, 1 + total); + B = isl_mat_sub_alloc6(ctx, context->eq, + 0, context_n_eq, 1 + total, n_div); + A = isl_mat_product(A, isl_mat_copy(V)); + T = isl_mat_parameter_compression_ext(A, B); + T = isl_mat_product(V, T); + + n_div = isl_basic_map_dim(bmap, isl_dim_div); + T = isl_mat_diagonal(T, isl_mat_identity(ctx, n_div)); + + A = isl_mat_sub_alloc6(ctx, bmap->eq, + 0, bmap_n_eq, 0, 1 + total + n_div); + A = isl_mat_product(A, T); + + return A; +} + +/* Remove the prime factors from *g that have an exponent that + * is strictly smaller than the exponent in "c". + * All exponents in *g are known to be smaller than or equal + * to those in "c". + * + * That is, if *g is equal to + * + * p_1^{e_1} p_2^{e_2} ... p_n^{e_n} + * + * and "c" is equal to + * + * p_1^{f_1} p_2^{f_2} ... p_n^{f_n} + * + * then update *g to + * + * p_1^{e_1 * (e_1 = f_1)} p_2^{e_2 * (e_2 = f_2)} ... + * p_n^{e_n * (e_n = f_n)} + * + * If e_i = f_i, then c / *g does not have any p_i factors and therefore + * neither does the gcd of *g and c / *g. + * If e_i < f_i, then the gcd of *g and c / *g has a positive + * power min(e_i, s_i) of p_i with s_i = f_i - e_i among its factors. + * Dividing *g by this gcd therefore strictly reduces the exponent + * of the prime factors that need to be removed, while leaving the + * other prime factors untouched. + * Repeating this process until gcd(*g, c / *g) = 1 therefore + * removes all undesired factors, without removing any others. + */ +static void remove_incomplete_powers(isl_int *g, isl_int c) +{ + isl_int t; + + isl_int_init(t); + for (;;) { + isl_int_divexact(t, c, *g); + isl_int_gcd(t, t, *g); + if (isl_int_is_one(t)) + break; + isl_int_divexact(*g, *g, t); + } + isl_int_clear(t); +} + +/* Reduce the "n" stride constraints in "bmap" based on a copy "A" + * of the same stride constraints in a compressed space that exploits + * all equalities in the context and the other equalities in "bmap". + * + * If the stride constraints of "bmap" are of the form + * + * C(x) + D(y) = 0 + * + * then A is of the form + * + * B(x') + D(y) = 0 + * + * If any of these constraints involves only a single local variable y, + * then the constraint appears as + * + * f(x) + m y_i = 0 + * + * in "bmap" and as + * + * h(x') + m y_i = 0 + * + * in "A". + * + * Let g be the gcd of m and the coefficients of h. + * Then, in particular, g is a divisor of the coefficients of h and + * + * f(x) = h(x') + * + * is known to be a multiple of g. + * If some prime factor in m appears with the same exponent in g, + * then it can be removed from m because f(x) is already known + * to be a multiple of g and therefore in particular of this power + * of the prime factors. + * Prime factors that appear with a smaller exponent in g cannot + * be removed from m. + * Let g' be the divisor of g containing all prime factors that + * appear with the same exponent in m and g, then + * + * f(x) + m y_i = 0 + * + * can be replaced by + * + * f(x) + m/g' y_i' = 0 + * + * Note that (if g' != 1) this changes the explicit representation + * of y_i to that of y_i', so the integer division at position i + * is marked unknown and later recomputed by a call to + * isl_basic_map_gauss. + */ +static __isl_give isl_basic_map *reduce_stride_constraints( + __isl_take isl_basic_map *bmap, int n, __isl_keep isl_mat *A) +{ + int i; + int total, n_div; + int any = 0; + isl_int gcd; + + if (!bmap || !A) + return isl_basic_map_free(bmap); + + total = isl_basic_map_dim(bmap, isl_dim_all); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + total -= n_div; + + isl_int_init(gcd); + for (i = 0; i < n; ++i) { + int div; + + div = isl_seq_first_non_zero(bmap->eq[i] + 1 + total, n_div); + if (div < 0) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_internal, + "equality constraints modified unexpectedly", + goto error); + if (isl_seq_first_non_zero(bmap->eq[i] + 1 + total + div + 1, + n_div - div - 1) != -1) + continue; + if (isl_mat_row_gcd(A, i, &gcd) < 0) + goto error; + if (isl_int_is_one(gcd)) + continue; + remove_incomplete_powers(&gcd, bmap->eq[i][1 + total + div]); + if (isl_int_is_one(gcd)) + continue; + isl_int_divexact(bmap->eq[i][1 + total + div], + bmap->eq[i][1 + total + div], gcd); + bmap = isl_basic_map_mark_div_unknown(bmap, div); + if (!bmap) + goto error; + any = 1; + } + isl_int_clear(gcd); + + if (any) + bmap = isl_basic_map_gauss(bmap, NULL); + + return bmap; +error: + isl_int_clear(gcd); + isl_basic_map_free(bmap); + return NULL; +} + +/* Simplify the stride constraints in "bmap" based on + * the remaining equality constraints in "bmap" and all equality + * constraints in "context". + * Only do this if both "bmap" and "context" have stride constraints. + * + * First extract a copy of the stride constraints in "bmap" in a compressed + * space exploiting all the other equality constraints and then + * use this compressed copy to simplify the original stride constraints. + */ +static __isl_give isl_basic_map *gist_strides(__isl_take isl_basic_map *bmap, + __isl_keep isl_basic_map *context) +{ + int bmap_n_eq, context_n_eq; + isl_mat *A; + + if (!bmap || !context) + return isl_basic_map_free(bmap); + + bmap_n_eq = n_div_eq(bmap); + context_n_eq = n_div_eq(context); + + if (bmap_n_eq < 0 || context_n_eq < 0) + return isl_basic_map_free(bmap); + if (bmap_n_eq == 0 || context_n_eq == 0) + return bmap; + + A = extract_compressed_stride_constraints(bmap, bmap_n_eq, + context, context_n_eq); + bmap = reduce_stride_constraints(bmap, bmap_n_eq, A); + + isl_mat_free(A); + + return bmap; +} + +/* Return a basic map that has the same intersection with "context" as "bmap" + * and that is as "simple" as possible. + * + * The core computation is performed on the pure constraints. + * When we add back the meaning of the integer divisions, we need + * to (re)introduce the div constraints. If we happen to have + * discovered that some of these integer divisions are equal to + * some affine combination of other variables, then these div + * constraints may end up getting simplified in terms of the equalities, + * resulting in extra inequalities on the other variables that + * may have been removed already or that may not even have been + * part of the input. We try and remove those constraints of + * this form that are most obviously redundant with respect to + * the context. We also remove those div constraints that are + * redundant with respect to the other constraints in the result. + * + * The stride constraints among the equality constraints in "bmap" are + * also simplified with respecting to the other equality constraints + * in "bmap" and with respect to all equality constraints in "context". + */ +struct isl_basic_map *isl_basic_map_gist(struct isl_basic_map *bmap, + struct isl_basic_map *context) +{ + isl_basic_set *bset, *eq; + isl_basic_map *eq_bmap; + unsigned total, n_div, extra, n_eq, n_ineq; + + if (!bmap || !context) + goto error; + + if (isl_basic_map_plain_is_universe(bmap)) { + isl_basic_map_free(context); + return bmap; + } + if (isl_basic_map_plain_is_empty(context)) { + isl_space *space = isl_basic_map_get_space(bmap); + isl_basic_map_free(bmap); + isl_basic_map_free(context); + return isl_basic_map_universe(space); + } + if (isl_basic_map_plain_is_empty(bmap)) { + isl_basic_map_free(context); + return bmap; + } + + bmap = isl_basic_map_remove_redundancies(bmap); + context = isl_basic_map_remove_redundancies(context); + if (!context) + goto error; + + context = isl_basic_map_align_divs(context, bmap); + n_div = isl_basic_map_dim(context, isl_dim_div); + total = isl_basic_map_dim(bmap, isl_dim_all); + extra = n_div - isl_basic_map_dim(bmap, isl_dim_div); + + bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap)); + bset = isl_basic_set_add_dims(bset, isl_dim_set, extra); + bset = uset_gist(bset, + isl_basic_map_underlying_set(isl_basic_map_copy(context))); + bset = isl_basic_set_project_out(bset, isl_dim_set, total, extra); + + if (!bset || bset->n_eq == 0 || n_div == 0 || + isl_basic_set_plain_is_empty(bset)) { + isl_basic_map_free(context); + return isl_basic_map_overlying_set(bset, bmap); + } + + n_eq = bset->n_eq; + n_ineq = bset->n_ineq; + eq = isl_basic_set_copy(bset); + eq = isl_basic_set_cow(eq); + if (isl_basic_set_free_inequality(eq, n_ineq) < 0) + eq = isl_basic_set_free(eq); + if (isl_basic_set_free_equality(bset, n_eq) < 0) + bset = isl_basic_set_free(bset); + + eq_bmap = isl_basic_map_overlying_set(eq, isl_basic_map_copy(bmap)); + eq_bmap = gist_strides(eq_bmap, context); + eq_bmap = isl_basic_map_remove_shifted_constraints(eq_bmap, context); + bmap = isl_basic_map_overlying_set(bset, bmap); + bmap = isl_basic_map_intersect(bmap, eq_bmap); + bmap = isl_basic_map_remove_redundancies(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + isl_basic_map_free(context); + return NULL; +} + +/* + * Assumes context has no implicit divs. + */ +__isl_give isl_map *isl_map_gist_basic_map(__isl_take isl_map *map, + __isl_take isl_basic_map *context) +{ + int i; + + if (!map || !context) + goto error; + + if (isl_basic_map_plain_is_empty(context)) { + isl_space *space = isl_map_get_space(map); + isl_map_free(map); + isl_basic_map_free(context); + return isl_map_universe(space); + } + + context = isl_basic_map_remove_redundancies(context); + map = isl_map_cow(map); + if (!map || !context) + goto error; + isl_assert(map->ctx, isl_space_is_equal(map->dim, context->dim), goto error); + map = isl_map_compute_divs(map); + if (!map) + goto error; + for (i = map->n - 1; i >= 0; --i) { + map->p[i] = isl_basic_map_gist(map->p[i], + isl_basic_map_copy(context)); + if (!map->p[i]) + goto error; + if (isl_basic_map_plain_is_empty(map->p[i])) { + isl_basic_map_free(map->p[i]); + if (i != map->n - 1) + map->p[i] = map->p[map->n - 1]; + map->n--; + } + } + isl_basic_map_free(context); + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + isl_basic_map_free(context); + return NULL; +} + +/* Drop all inequalities from "bmap" that also appear in "context". + * "context" is assumed to have only known local variables and + * the initial local variables of "bmap" are assumed to be the same + * as those of "context". + * The constraints of both "bmap" and "context" are assumed + * to have been sorted using isl_basic_map_sort_constraints. + * + * Run through the inequality constraints of "bmap" and "context" + * in sorted order. + * If a constraint of "bmap" involves variables not in "context", + * then it cannot appear in "context". + * If a matching constraint is found, it is removed from "bmap". + */ +static __isl_give isl_basic_map *drop_inequalities( + __isl_take isl_basic_map *bmap, __isl_keep isl_basic_map *context) +{ + int i1, i2; + unsigned total, extra; + + if (!bmap || !context) + return isl_basic_map_free(bmap); + + total = isl_basic_map_total_dim(context); + extra = isl_basic_map_total_dim(bmap) - total; + + i1 = bmap->n_ineq - 1; + i2 = context->n_ineq - 1; + while (bmap && i1 >= 0 && i2 >= 0) { + int cmp; + + if (isl_seq_first_non_zero(bmap->ineq[i1] + 1 + total, + extra) != -1) { + --i1; + continue; + } + cmp = isl_basic_map_constraint_cmp(context, bmap->ineq[i1], + context->ineq[i2]); + if (cmp < 0) { + --i2; + continue; + } + if (cmp > 0) { + --i1; + continue; + } + if (isl_int_eq(bmap->ineq[i1][0], context->ineq[i2][0])) { + bmap = isl_basic_map_cow(bmap); + if (isl_basic_map_drop_inequality(bmap, i1) < 0) + bmap = isl_basic_map_free(bmap); + } + --i1; + --i2; + } + + return bmap; +} + +/* Drop all equalities from "bmap" that also appear in "context". + * "context" is assumed to have only known local variables and + * the initial local variables of "bmap" are assumed to be the same + * as those of "context". + * + * Run through the equality constraints of "bmap" and "context" + * in sorted order. + * If a constraint of "bmap" involves variables not in "context", + * then it cannot appear in "context". + * If a matching constraint is found, it is removed from "bmap". + */ +static __isl_give isl_basic_map *drop_equalities( + __isl_take isl_basic_map *bmap, __isl_keep isl_basic_map *context) +{ + int i1, i2; + unsigned total, extra; + + if (!bmap || !context) + return isl_basic_map_free(bmap); + + total = isl_basic_map_total_dim(context); + extra = isl_basic_map_total_dim(bmap) - total; + + i1 = bmap->n_eq - 1; + i2 = context->n_eq - 1; + + while (bmap && i1 >= 0 && i2 >= 0) { + int last1, last2; + + if (isl_seq_first_non_zero(bmap->eq[i1] + 1 + total, + extra) != -1) + break; + last1 = isl_seq_last_non_zero(bmap->eq[i1] + 1, total); + last2 = isl_seq_last_non_zero(context->eq[i2] + 1, total); + if (last1 > last2) { + --i2; + continue; + } + if (last1 < last2) { + --i1; + continue; + } + if (isl_seq_eq(bmap->eq[i1], context->eq[i2], 1 + total)) { + bmap = isl_basic_map_cow(bmap); + if (isl_basic_map_drop_equality(bmap, i1) < 0) + bmap = isl_basic_map_free(bmap); + } + --i1; + --i2; + } + + return bmap; +} + +/* Remove the constraints in "context" from "bmap". + * "context" is assumed to have explicit representations + * for all local variables. + * + * First align the divs of "bmap" to those of "context" and + * sort the constraints. Then drop all constraints from "bmap" + * that appear in "context". + */ +__isl_give isl_basic_map *isl_basic_map_plain_gist( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_map *context) +{ + isl_bool done, known; + + done = isl_basic_map_plain_is_universe(context); + if (done == isl_bool_false) + done = isl_basic_map_plain_is_universe(bmap); + if (done == isl_bool_false) + done = isl_basic_map_plain_is_empty(context); + if (done == isl_bool_false) + done = isl_basic_map_plain_is_empty(bmap); + if (done < 0) + goto error; + if (done) { + isl_basic_map_free(context); + return bmap; + } + known = isl_basic_map_divs_known(context); + if (known < 0) + goto error; + if (!known) + isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid, + "context has unknown divs", goto error); + + bmap = isl_basic_map_align_divs(bmap, context); + bmap = isl_basic_map_gauss(bmap, NULL); + bmap = isl_basic_map_sort_constraints(bmap); + context = isl_basic_map_sort_constraints(context); + + bmap = drop_inequalities(bmap, context); + bmap = drop_equalities(bmap, context); + + isl_basic_map_free(context); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_basic_map_free(bmap); + isl_basic_map_free(context); + return NULL; +} + +/* Replace "map" by the disjunct at position "pos" and free "context". + */ +static __isl_give isl_map *replace_by_disjunct(__isl_take isl_map *map, + int pos, __isl_take isl_basic_map *context) +{ + isl_basic_map *bmap; + + bmap = isl_basic_map_copy(map->p[pos]); + isl_map_free(map); + isl_basic_map_free(context); + return isl_map_from_basic_map(bmap); +} + +/* Remove the constraints in "context" from "map". + * If any of the disjuncts in the result turns out to be the universe, + * then return this universe. + * "context" is assumed to have explicit representations + * for all local variables. + */ +__isl_give isl_map *isl_map_plain_gist_basic_map(__isl_take isl_map *map, + __isl_take isl_basic_map *context) +{ + int i; + isl_bool univ, known; + + univ = isl_basic_map_plain_is_universe(context); + if (univ < 0) + goto error; + if (univ) { + isl_basic_map_free(context); + return map; + } + known = isl_basic_map_divs_known(context); + if (known < 0) + goto error; + if (!known) + isl_die(isl_map_get_ctx(map), isl_error_invalid, + "context has unknown divs", goto error); + + map = isl_map_cow(map); + if (!map) + goto error; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_plain_gist(map->p[i], + isl_basic_map_copy(context)); + univ = isl_basic_map_plain_is_universe(map->p[i]); + if (univ < 0) + goto error; + if (univ && map->n > 1) + return replace_by_disjunct(map, i, context); + } + + isl_basic_map_free(context); + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + if (map->n > 1) + ISL_F_CLR(map, ISL_MAP_DISJOINT); + return map; +error: + isl_map_free(map); + isl_basic_map_free(context); + return NULL; +} + +/* Replace "map" by a universe map in the same space and free "drop". + */ +static __isl_give isl_map *replace_by_universe(__isl_take isl_map *map, + __isl_take isl_map *drop) +{ + isl_map *res; + + res = isl_map_universe(isl_map_get_space(map)); + isl_map_free(map); + isl_map_free(drop); + return res; +} + +/* Return a map that has the same intersection with "context" as "map" + * and that is as "simple" as possible. + * + * If "map" is already the universe, then we cannot make it any simpler. + * Similarly, if "context" is the universe, then we cannot exploit it + * to simplify "map" + * If "map" and "context" are identical to each other, then we can + * return the corresponding universe. + * + * If either "map" or "context" consists of multiple disjuncts, + * then check if "context" happens to be a subset of "map", + * in which case all constraints can be removed. + * In case of multiple disjuncts, the standard procedure + * may not be able to detect that all constraints can be removed. + * + * If none of these cases apply, we have to work a bit harder. + * During this computation, we make use of a single disjunct context, + * so if the original context consists of more than one disjunct + * then we need to approximate the context by a single disjunct set. + * Simply taking the simple hull may drop constraints that are + * only implicitly available in each disjunct. We therefore also + * look for constraints among those defining "map" that are valid + * for the context. These can then be used to simplify away + * the corresponding constraints in "map". + */ +static __isl_give isl_map *map_gist(__isl_take isl_map *map, + __isl_take isl_map *context) +{ + int equal; + int is_universe; + int single_disjunct_map, single_disjunct_context; + isl_bool subset; + isl_basic_map *hull; + + is_universe = isl_map_plain_is_universe(map); + if (is_universe >= 0 && !is_universe) + is_universe = isl_map_plain_is_universe(context); + if (is_universe < 0) + goto error; + if (is_universe) { + isl_map_free(context); + return map; + } + + equal = isl_map_plain_is_equal(map, context); + if (equal < 0) + goto error; + if (equal) + return replace_by_universe(map, context); + + single_disjunct_map = isl_map_n_basic_map(map) == 1; + single_disjunct_context = isl_map_n_basic_map(context) == 1; + if (!single_disjunct_map || !single_disjunct_context) { + subset = isl_map_is_subset(context, map); + if (subset < 0) + goto error; + if (subset) + return replace_by_universe(map, context); + } + + context = isl_map_compute_divs(context); + if (!context) + goto error; + if (single_disjunct_context) { + hull = isl_map_simple_hull(context); + } else { + isl_ctx *ctx; + isl_map_list *list; + + ctx = isl_map_get_ctx(map); + list = isl_map_list_alloc(ctx, 2); + list = isl_map_list_add(list, isl_map_copy(context)); + list = isl_map_list_add(list, isl_map_copy(map)); + hull = isl_map_unshifted_simple_hull_from_map_list(context, + list); + } + return isl_map_gist_basic_map(map, hull); +error: + isl_map_free(map); + isl_map_free(context); + return NULL; +} + +__isl_give isl_map *isl_map_gist(__isl_take isl_map *map, + __isl_take isl_map *context) +{ + return isl_map_align_params_map_map_and(map, context, &map_gist); +} + +struct isl_basic_set *isl_basic_set_gist(struct isl_basic_set *bset, + struct isl_basic_set *context) +{ + return (struct isl_basic_set *)isl_basic_map_gist( + (struct isl_basic_map *)bset, (struct isl_basic_map *)context); +} + +__isl_give isl_set *isl_set_gist_basic_set(__isl_take isl_set *set, + __isl_take isl_basic_set *context) +{ + return (struct isl_set *)isl_map_gist_basic_map((struct isl_map *)set, + (struct isl_basic_map *)context); +} + +__isl_give isl_set *isl_set_gist_params_basic_set(__isl_take isl_set *set, + __isl_take isl_basic_set *context) +{ + isl_space *space = isl_set_get_space(set); + isl_basic_set *dom_context = isl_basic_set_universe(space); + dom_context = isl_basic_set_intersect_params(dom_context, context); + return isl_set_gist_basic_set(set, dom_context); +} + +__isl_give isl_set *isl_set_gist(__isl_take isl_set *set, + __isl_take isl_set *context) +{ + return (struct isl_set *)isl_map_gist((struct isl_map *)set, + (struct isl_map *)context); +} + +/* Compute the gist of "bmap" with respect to the constraints "context" + * on the domain. + */ +__isl_give isl_basic_map *isl_basic_map_gist_domain( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *context) +{ + isl_space *space = isl_basic_map_get_space(bmap); + isl_basic_map *bmap_context = isl_basic_map_universe(space); + + bmap_context = isl_basic_map_intersect_domain(bmap_context, context); + return isl_basic_map_gist(bmap, bmap_context); +} + +__isl_give isl_map *isl_map_gist_domain(__isl_take isl_map *map, + __isl_take isl_set *context) +{ + isl_map *map_context = isl_map_universe(isl_map_get_space(map)); + map_context = isl_map_intersect_domain(map_context, context); + return isl_map_gist(map, map_context); +} + +__isl_give isl_map *isl_map_gist_range(__isl_take isl_map *map, + __isl_take isl_set *context) +{ + isl_map *map_context = isl_map_universe(isl_map_get_space(map)); + map_context = isl_map_intersect_range(map_context, context); + return isl_map_gist(map, map_context); +} + +__isl_give isl_map *isl_map_gist_params(__isl_take isl_map *map, + __isl_take isl_set *context) +{ + isl_map *map_context = isl_map_universe(isl_map_get_space(map)); + map_context = isl_map_intersect_params(map_context, context); + return isl_map_gist(map, map_context); +} + +__isl_give isl_set *isl_set_gist_params(__isl_take isl_set *set, + __isl_take isl_set *context) +{ + return isl_map_gist_params(set, context); +} + +/* Quick check to see if two basic maps are disjoint. + * In particular, we reduce the equalities and inequalities of + * one basic map in the context of the equalities of the other + * basic map and check if we get a contradiction. + */ +isl_bool isl_basic_map_plain_is_disjoint(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2) +{ + struct isl_vec *v = NULL; + int *elim = NULL; + unsigned total; + int i; + + if (!bmap1 || !bmap2) + return isl_bool_error; + isl_assert(bmap1->ctx, isl_space_is_equal(bmap1->dim, bmap2->dim), + return isl_bool_error); + if (bmap1->n_div || bmap2->n_div) + return isl_bool_false; + if (!bmap1->n_eq && !bmap2->n_eq) + return isl_bool_false; + + total = isl_space_dim(bmap1->dim, isl_dim_all); + if (total == 0) + return isl_bool_false; + v = isl_vec_alloc(bmap1->ctx, 1 + total); + if (!v) + goto error; + elim = isl_alloc_array(bmap1->ctx, int, total); + if (!elim) + goto error; + compute_elimination_index(bmap1, elim); + for (i = 0; i < bmap2->n_eq; ++i) { + int reduced; + reduced = reduced_using_equalities(v->block.data, bmap2->eq[i], + bmap1, elim); + if (reduced && !isl_int_is_zero(v->block.data[0]) && + isl_seq_first_non_zero(v->block.data + 1, total) == -1) + goto disjoint; + } + for (i = 0; i < bmap2->n_ineq; ++i) { + int reduced; + reduced = reduced_using_equalities(v->block.data, + bmap2->ineq[i], bmap1, elim); + if (reduced && isl_int_is_neg(v->block.data[0]) && + isl_seq_first_non_zero(v->block.data + 1, total) == -1) + goto disjoint; + } + compute_elimination_index(bmap2, elim); + for (i = 0; i < bmap1->n_ineq; ++i) { + int reduced; + reduced = reduced_using_equalities(v->block.data, + bmap1->ineq[i], bmap2, elim); + if (reduced && isl_int_is_neg(v->block.data[0]) && + isl_seq_first_non_zero(v->block.data + 1, total) == -1) + goto disjoint; + } + isl_vec_free(v); + free(elim); + return isl_bool_false; +disjoint: + isl_vec_free(v); + free(elim); + return isl_bool_true; +error: + isl_vec_free(v); + free(elim); + return isl_bool_error; +} + +int isl_basic_set_plain_is_disjoint(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2) +{ + return isl_basic_map_plain_is_disjoint((struct isl_basic_map *)bset1, + (struct isl_basic_map *)bset2); +} + +/* Does "test" hold for all pairs of basic maps in "map1" and "map2"? + */ +static isl_bool all_pairs(__isl_keep isl_map *map1, __isl_keep isl_map *map2, + isl_bool (*test)(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2)) +{ + int i, j; + + if (!map1 || !map2) + return isl_bool_error; + + for (i = 0; i < map1->n; ++i) { + for (j = 0; j < map2->n; ++j) { + isl_bool d = test(map1->p[i], map2->p[j]); + if (d != isl_bool_true) + return d; + } + } + + return isl_bool_true; +} + +/* Are "map1" and "map2" obviously disjoint, based on information + * that can be derived without looking at the individual basic maps? + * + * In particular, if one of them is empty or if they live in different spaces + * (ignoring parameters), then they are clearly disjoint. + */ +static isl_bool isl_map_plain_is_disjoint_global(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + isl_bool disjoint; + isl_bool match; + + if (!map1 || !map2) + return isl_bool_error; + + disjoint = isl_map_plain_is_empty(map1); + if (disjoint < 0 || disjoint) + return disjoint; + + disjoint = isl_map_plain_is_empty(map2); + if (disjoint < 0 || disjoint) + return disjoint; + + match = isl_space_tuple_is_equal(map1->dim, isl_dim_in, + map2->dim, isl_dim_in); + if (match < 0 || !match) + return match < 0 ? isl_bool_error : isl_bool_true; + + match = isl_space_tuple_is_equal(map1->dim, isl_dim_out, + map2->dim, isl_dim_out); + if (match < 0 || !match) + return match < 0 ? isl_bool_error : isl_bool_true; + + return isl_bool_false; +} + +/* Are "map1" and "map2" obviously disjoint? + * + * If one of them is empty or if they live in different spaces (ignoring + * parameters), then they are clearly disjoint. + * This is checked by isl_map_plain_is_disjoint_global. + * + * If they have different parameters, then we skip any further tests. + * + * If they are obviously equal, but not obviously empty, then we will + * not be able to detect if they are disjoint. + * + * Otherwise we check if each basic map in "map1" is obviously disjoint + * from each basic map in "map2". + */ +isl_bool isl_map_plain_is_disjoint(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + isl_bool disjoint; + isl_bool intersect; + isl_bool match; + + disjoint = isl_map_plain_is_disjoint_global(map1, map2); + if (disjoint < 0 || disjoint) + return disjoint; + + match = isl_space_match(map1->dim, isl_dim_param, + map2->dim, isl_dim_param); + if (match < 0 || !match) + return match < 0 ? isl_bool_error : isl_bool_false; + + intersect = isl_map_plain_is_equal(map1, map2); + if (intersect < 0 || intersect) + return intersect < 0 ? isl_bool_error : isl_bool_false; + + return all_pairs(map1, map2, &isl_basic_map_plain_is_disjoint); +} + +/* Are "map1" and "map2" disjoint? + * + * They are disjoint if they are "obviously disjoint" or if one of them + * is empty. Otherwise, they are not disjoint if one of them is universal. + * If the two inputs are (obviously) equal and not empty, then they are + * not disjoint. + * If none of these cases apply, then check if all pairs of basic maps + * are disjoint. + */ +isl_bool isl_map_is_disjoint(__isl_keep isl_map *map1, __isl_keep isl_map *map2) +{ + isl_bool disjoint; + isl_bool intersect; + + disjoint = isl_map_plain_is_disjoint_global(map1, map2); + if (disjoint < 0 || disjoint) + return disjoint; + + disjoint = isl_map_is_empty(map1); + if (disjoint < 0 || disjoint) + return disjoint; + + disjoint = isl_map_is_empty(map2); + if (disjoint < 0 || disjoint) + return disjoint; + + intersect = isl_map_plain_is_universe(map1); + if (intersect < 0 || intersect) + return intersect < 0 ? isl_bool_error : isl_bool_false; + + intersect = isl_map_plain_is_universe(map2); + if (intersect < 0 || intersect) + return intersect < 0 ? isl_bool_error : isl_bool_false; + + intersect = isl_map_plain_is_equal(map1, map2); + if (intersect < 0 || intersect) + return isl_bool_not(intersect); + + return all_pairs(map1, map2, &isl_basic_map_is_disjoint); +} + +/* Are "bmap1" and "bmap2" disjoint? + * + * They are disjoint if they are "obviously disjoint" or if one of them + * is empty. Otherwise, they are not disjoint if one of them is universal. + * If none of these cases apply, we compute the intersection and see if + * the result is empty. + */ +isl_bool isl_basic_map_is_disjoint(__isl_keep isl_basic_map *bmap1, + __isl_keep isl_basic_map *bmap2) +{ + isl_bool disjoint; + isl_bool intersect; + isl_basic_map *test; + + disjoint = isl_basic_map_plain_is_disjoint(bmap1, bmap2); + if (disjoint < 0 || disjoint) + return disjoint; + + disjoint = isl_basic_map_is_empty(bmap1); + if (disjoint < 0 || disjoint) + return disjoint; + + disjoint = isl_basic_map_is_empty(bmap2); + if (disjoint < 0 || disjoint) + return disjoint; + + intersect = isl_basic_map_plain_is_universe(bmap1); + if (intersect < 0 || intersect) + return intersect < 0 ? isl_bool_error : isl_bool_false; + + intersect = isl_basic_map_plain_is_universe(bmap2); + if (intersect < 0 || intersect) + return intersect < 0 ? isl_bool_error : isl_bool_false; + + test = isl_basic_map_intersect(isl_basic_map_copy(bmap1), + isl_basic_map_copy(bmap2)); + disjoint = isl_basic_map_is_empty(test); + isl_basic_map_free(test); + + return disjoint; +} + +/* Are "bset1" and "bset2" disjoint? + */ +isl_bool isl_basic_set_is_disjoint(__isl_keep isl_basic_set *bset1, + __isl_keep isl_basic_set *bset2) +{ + return isl_basic_map_is_disjoint(bset1, bset2); +} + +isl_bool isl_set_plain_is_disjoint(__isl_keep isl_set *set1, + __isl_keep isl_set *set2) +{ + return isl_map_plain_is_disjoint((struct isl_map *)set1, + (struct isl_map *)set2); +} + +/* Are "set1" and "set2" disjoint? + */ +isl_bool isl_set_is_disjoint(__isl_keep isl_set *set1, __isl_keep isl_set *set2) +{ + return isl_map_is_disjoint(set1, set2); +} + +/* Is "v" equal to 0, 1 or -1? + */ +static int is_zero_or_one(isl_int v) +{ + return isl_int_is_zero(v) || isl_int_is_one(v) || isl_int_is_negone(v); +} + +/* Check if we can combine a given div with lower bound l and upper + * bound u with some other div and if so return that other div. + * Otherwise return -1. + * + * We first check that + * - the bounds are opposites of each other (except for the constant + * term) + * - the bounds do not reference any other div + * - no div is defined in terms of this div + * + * Let m be the size of the range allowed on the div by the bounds. + * That is, the bounds are of the form + * + * e <= a <= e + m - 1 + * + * with e some expression in the other variables. + * We look for another div b such that no third div is defined in terms + * of this second div b and such that in any constraint that contains + * a (except for the given lower and upper bound), also contains b + * with a coefficient that is m times that of b. + * That is, all constraints (execpt for the lower and upper bound) + * are of the form + * + * e + f (a + m b) >= 0 + * + * Furthermore, in the constraints that only contain b, the coefficient + * of b should be equal to 1 or -1. + * If so, we return b so that "a + m b" can be replaced by + * a single div "c = a + m b". + */ +static int div_find_coalesce(struct isl_basic_map *bmap, int *pairs, + unsigned div, unsigned l, unsigned u) +{ + int i, j; + unsigned dim; + int coalesce = -1; + + if (bmap->n_div <= 1) + return -1; + dim = isl_space_dim(bmap->dim, isl_dim_all); + if (isl_seq_first_non_zero(bmap->ineq[l] + 1 + dim, div) != -1) + return -1; + if (isl_seq_first_non_zero(bmap->ineq[l] + 1 + dim + div + 1, + bmap->n_div - div - 1) != -1) + return -1; + if (!isl_seq_is_neg(bmap->ineq[l] + 1, bmap->ineq[u] + 1, + dim + bmap->n_div)) + return -1; + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (!isl_int_is_zero(bmap->div[i][1 + 1 + dim + div])) + return -1; + } + + isl_int_add(bmap->ineq[l][0], bmap->ineq[l][0], bmap->ineq[u][0]); + if (isl_int_is_neg(bmap->ineq[l][0])) { + isl_int_sub(bmap->ineq[l][0], + bmap->ineq[l][0], bmap->ineq[u][0]); + bmap = isl_basic_map_copy(bmap); + bmap = isl_basic_map_set_to_empty(bmap); + isl_basic_map_free(bmap); + return -1; + } + isl_int_add_ui(bmap->ineq[l][0], bmap->ineq[l][0], 1); + for (i = 0; i < bmap->n_div; ++i) { + if (i == div) + continue; + if (!pairs[i]) + continue; + for (j = 0; j < bmap->n_div; ++j) { + if (isl_int_is_zero(bmap->div[j][0])) + continue; + if (!isl_int_is_zero(bmap->div[j][1 + 1 + dim + i])) + break; + } + if (j < bmap->n_div) + continue; + for (j = 0; j < bmap->n_ineq; ++j) { + int valid; + if (j == l || j == u) + continue; + if (isl_int_is_zero(bmap->ineq[j][1 + dim + div])) { + if (is_zero_or_one(bmap->ineq[j][1 + dim + i])) + continue; + break; + } + if (isl_int_is_zero(bmap->ineq[j][1 + dim + i])) + break; + isl_int_mul(bmap->ineq[j][1 + dim + div], + bmap->ineq[j][1 + dim + div], + bmap->ineq[l][0]); + valid = isl_int_eq(bmap->ineq[j][1 + dim + div], + bmap->ineq[j][1 + dim + i]); + isl_int_divexact(bmap->ineq[j][1 + dim + div], + bmap->ineq[j][1 + dim + div], + bmap->ineq[l][0]); + if (!valid) + break; + } + if (j < bmap->n_ineq) + continue; + coalesce = i; + break; + } + isl_int_sub_ui(bmap->ineq[l][0], bmap->ineq[l][0], 1); + isl_int_sub(bmap->ineq[l][0], bmap->ineq[l][0], bmap->ineq[u][0]); + return coalesce; +} + +/* Internal data structure used during the construction and/or evaluation of + * an inequality that ensures that a pair of bounds always allows + * for an integer value. + * + * "tab" is the tableau in which the inequality is evaluated. It may + * be NULL until it is actually needed. + * "v" contains the inequality coefficients. + * "g", "fl" and "fu" are temporary scalars used during the construction and + * evaluation. + */ +struct test_ineq_data { + struct isl_tab *tab; + isl_vec *v; + isl_int g; + isl_int fl; + isl_int fu; +}; + +/* Free all the memory allocated by the fields of "data". + */ +static void test_ineq_data_clear(struct test_ineq_data *data) +{ + isl_tab_free(data->tab); + isl_vec_free(data->v); + isl_int_clear(data->g); + isl_int_clear(data->fl); + isl_int_clear(data->fu); +} + +/* Is the inequality stored in data->v satisfied by "bmap"? + * That is, does it only attain non-negative values? + * data->tab is a tableau corresponding to "bmap". + */ +static isl_bool test_ineq_is_satisfied(__isl_keep isl_basic_map *bmap, + struct test_ineq_data *data) +{ + isl_ctx *ctx; + enum isl_lp_result res; + + ctx = isl_basic_map_get_ctx(bmap); + if (!data->tab) + data->tab = isl_tab_from_basic_map(bmap, 0); + res = isl_tab_min(data->tab, data->v->el, ctx->one, &data->g, NULL, 0); + if (res == isl_lp_error) + return isl_bool_error; + return res == isl_lp_ok && isl_int_is_nonneg(data->g); +} + +/* Given a lower and an upper bound on div i, do they always allow + * for an integer value of the given div? + * Determine this property by constructing an inequality + * such that the property is guaranteed when the inequality is nonnegative. + * The lower bound is inequality l, while the upper bound is inequality u. + * The constructed inequality is stored in data->v. + * + * Let the upper bound be + * + * -n_u a + e_u >= 0 + * + * and the lower bound + * + * n_l a + e_l >= 0 + * + * Let n_u = f_u g and n_l = f_l g, with g = gcd(n_u, n_l). + * We have + * + * - f_u e_l <= f_u f_l g a <= f_l e_u + * + * Since all variables are integer valued, this is equivalent to + * + * - f_u e_l - (f_u - 1) <= f_u f_l g a <= f_l e_u + (f_l - 1) + * + * If this interval is at least f_u f_l g, then it contains at least + * one integer value for a. + * That is, the test constraint is + * + * f_l e_u + f_u e_l + f_l - 1 + f_u - 1 + 1 >= f_u f_l g + * + * or + * + * f_l e_u + f_u e_l + f_l - 1 + f_u - 1 + 1 - f_u f_l g >= 0 + * + * If the coefficients of f_l e_u + f_u e_l have a common divisor g', + * then the constraint can be scaled down by a factor g', + * with the constant term replaced by + * floor((f_l e_{u,0} + f_u e_{l,0} + f_l - 1 + f_u - 1 + 1 - f_u f_l g)/g'). + * Note that the result of applying Fourier-Motzkin to this pair + * of constraints is + * + * f_l e_u + f_u e_l >= 0 + * + * If the constant term of the scaled down version of this constraint, + * i.e., floor((f_l e_{u,0} + f_u e_{l,0})/g') is equal to the constant + * term of the scaled down test constraint, then the test constraint + * is known to hold and no explicit evaluation is required. + * This is essentially the Omega test. + * + * If the test constraint consists of only a constant term, then + * it is sufficient to look at the sign of this constant term. + */ +static isl_bool int_between_bounds(__isl_keep isl_basic_map *bmap, int i, + int l, int u, struct test_ineq_data *data) +{ + unsigned offset, n_div; + offset = isl_basic_map_offset(bmap, isl_dim_div); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + + isl_int_gcd(data->g, + bmap->ineq[l][offset + i], bmap->ineq[u][offset + i]); + isl_int_divexact(data->fl, bmap->ineq[l][offset + i], data->g); + isl_int_divexact(data->fu, bmap->ineq[u][offset + i], data->g); + isl_int_neg(data->fu, data->fu); + isl_seq_combine(data->v->el, data->fl, bmap->ineq[u], + data->fu, bmap->ineq[l], offset + n_div); + isl_int_mul(data->g, data->g, data->fl); + isl_int_mul(data->g, data->g, data->fu); + isl_int_sub(data->g, data->g, data->fl); + isl_int_sub(data->g, data->g, data->fu); + isl_int_add_ui(data->g, data->g, 1); + isl_int_sub(data->fl, data->v->el[0], data->g); + + isl_seq_gcd(data->v->el + 1, offset - 1 + n_div, &data->g); + if (isl_int_is_zero(data->g)) + return isl_int_is_nonneg(data->fl); + if (isl_int_is_one(data->g)) { + isl_int_set(data->v->el[0], data->fl); + return test_ineq_is_satisfied(bmap, data); + } + isl_int_fdiv_q(data->fl, data->fl, data->g); + isl_int_fdiv_q(data->v->el[0], data->v->el[0], data->g); + if (isl_int_eq(data->fl, data->v->el[0])) + return isl_bool_true; + isl_int_set(data->v->el[0], data->fl); + isl_seq_scale_down(data->v->el + 1, data->v->el + 1, data->g, + offset - 1 + n_div); + + return test_ineq_is_satisfied(bmap, data); +} + +/* Remove more kinds of divs that are not strictly needed. + * In particular, if all pairs of lower and upper bounds on a div + * are such that they allow at least one integer value of the div, + * then we can eliminate the div using Fourier-Motzkin without + * introducing any spurious solutions. + * + * If at least one of the two constraints has a unit coefficient for the div, + * then the presence of such a value is guaranteed so there is no need to check. + * In particular, the value attained by the bound with unit coefficient + * can serve as this intermediate value. + */ +static struct isl_basic_map *drop_more_redundant_divs( + struct isl_basic_map *bmap, int *pairs, int n) +{ + isl_ctx *ctx; + struct test_ineq_data data = { NULL, NULL }; + unsigned off, n_div; + int remove = -1; + + isl_int_init(data.g); + isl_int_init(data.fl); + isl_int_init(data.fu); + + if (!bmap) + goto error; + + ctx = isl_basic_map_get_ctx(bmap); + off = isl_basic_map_offset(bmap, isl_dim_div); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + data.v = isl_vec_alloc(ctx, off + n_div); + if (!data.v) + goto error; + + while (n > 0) { + int i, l, u; + int best = -1; + isl_bool has_int; + + for (i = 0; i < n_div; ++i) { + if (!pairs[i]) + continue; + if (best >= 0 && pairs[best] <= pairs[i]) + continue; + best = i; + } + + i = best; + for (l = 0; l < bmap->n_ineq; ++l) { + if (!isl_int_is_pos(bmap->ineq[l][off + i])) + continue; + if (isl_int_is_one(bmap->ineq[l][off + i])) + continue; + for (u = 0; u < bmap->n_ineq; ++u) { + if (!isl_int_is_neg(bmap->ineq[u][off + i])) + continue; + if (isl_int_is_negone(bmap->ineq[u][off + i])) + continue; + has_int = int_between_bounds(bmap, i, l, u, + &data); + if (has_int < 0) + goto error; + if (data.tab && data.tab->empty) + break; + if (!has_int) + break; + } + if (u < bmap->n_ineq) + break; + } + if (data.tab && data.tab->empty) { + bmap = isl_basic_map_set_to_empty(bmap); + break; + } + if (l == bmap->n_ineq) { + remove = i; + break; + } + pairs[i] = 0; + --n; + } + + test_ineq_data_clear(&data); + + free(pairs); + + if (remove < 0) + return bmap; + + bmap = isl_basic_map_remove_dims(bmap, isl_dim_div, remove, 1); + return isl_basic_map_drop_redundant_divs(bmap); +error: + free(pairs); + isl_basic_map_free(bmap); + test_ineq_data_clear(&data); + return NULL; +} + +/* Given a pair of divs div1 and div2 such that, except for the lower bound l + * and the upper bound u, div1 always occurs together with div2 in the form + * (div1 + m div2), where m is the constant range on the variable div1 + * allowed by l and u, replace the pair div1 and div2 by a single + * div that is equal to div1 + m div2. + * + * The new div will appear in the location that contains div2. + * We need to modify all constraints that contain + * div2 = (div - div1) / m + * The coefficient of div2 is known to be equal to 1 or -1. + * (If a constraint does not contain div2, it will also not contain div1.) + * If the constraint also contains div1, then we know they appear + * as f (div1 + m div2) and we can simply replace (div1 + m div2) by div, + * i.e., the coefficient of div is f. + * + * Otherwise, we first need to introduce div1 into the constraint. + * Let the l be + * + * div1 + f >=0 + * + * and u + * + * -div1 + f' >= 0 + * + * A lower bound on div2 + * + * div2 + t >= 0 + * + * can be replaced by + * + * m div2 + div1 + m t + f >= 0 + * + * An upper bound + * + * -div2 + t >= 0 + * + * can be replaced by + * + * -(m div2 + div1) + m t + f' >= 0 + * + * These constraint are those that we would obtain from eliminating + * div1 using Fourier-Motzkin. + * + * After all constraints have been modified, we drop the lower and upper + * bound and then drop div1. + */ +static struct isl_basic_map *coalesce_divs(struct isl_basic_map *bmap, + unsigned div1, unsigned div2, unsigned l, unsigned u) +{ + isl_ctx *ctx; + isl_int m; + unsigned dim, total; + int i; + + ctx = isl_basic_map_get_ctx(bmap); + + dim = isl_space_dim(bmap->dim, isl_dim_all); + total = 1 + dim + bmap->n_div; + + isl_int_init(m); + isl_int_add(m, bmap->ineq[l][0], bmap->ineq[u][0]); + isl_int_add_ui(m, m, 1); + + for (i = 0; i < bmap->n_ineq; ++i) { + if (i == l || i == u) + continue; + if (isl_int_is_zero(bmap->ineq[i][1 + dim + div2])) + continue; + if (isl_int_is_zero(bmap->ineq[i][1 + dim + div1])) { + if (isl_int_is_pos(bmap->ineq[i][1 + dim + div2])) + isl_seq_combine(bmap->ineq[i], m, bmap->ineq[i], + ctx->one, bmap->ineq[l], total); + else + isl_seq_combine(bmap->ineq[i], m, bmap->ineq[i], + ctx->one, bmap->ineq[u], total); + } + isl_int_set(bmap->ineq[i][1 + dim + div2], + bmap->ineq[i][1 + dim + div1]); + isl_int_set_si(bmap->ineq[i][1 + dim + div1], 0); + } + + isl_int_clear(m); + if (l > u) { + isl_basic_map_drop_inequality(bmap, l); + isl_basic_map_drop_inequality(bmap, u); + } else { + isl_basic_map_drop_inequality(bmap, u); + isl_basic_map_drop_inequality(bmap, l); + } + bmap = isl_basic_map_drop_div(bmap, div1); + return bmap; +} + +/* First check if we can coalesce any pair of divs and + * then continue with dropping more redundant divs. + * + * We loop over all pairs of lower and upper bounds on a div + * with coefficient 1 and -1, respectively, check if there + * is any other div "c" with which we can coalesce the div + * and if so, perform the coalescing. + */ +static struct isl_basic_map *coalesce_or_drop_more_redundant_divs( + struct isl_basic_map *bmap, int *pairs, int n) +{ + int i, l, u; + unsigned dim; + + dim = isl_space_dim(bmap->dim, isl_dim_all); + + for (i = 0; i < bmap->n_div; ++i) { + if (!pairs[i]) + continue; + for (l = 0; l < bmap->n_ineq; ++l) { + if (!isl_int_is_one(bmap->ineq[l][1 + dim + i])) + continue; + for (u = 0; u < bmap->n_ineq; ++u) { + int c; + + if (!isl_int_is_negone(bmap->ineq[u][1+dim+i])) + continue; + c = div_find_coalesce(bmap, pairs, i, l, u); + if (c < 0) + continue; + free(pairs); + bmap = coalesce_divs(bmap, i, c, l, u); + return isl_basic_map_drop_redundant_divs(bmap); + } + } + } + + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) + return bmap; + + return drop_more_redundant_divs(bmap, pairs, n); +} + +/* Are the "n" coefficients starting at "first" of inequality constraints + * "i" and "j" of "bmap" equal to each other? + */ +static int is_parallel_part(__isl_keep isl_basic_map *bmap, int i, int j, + int first, int n) +{ + return isl_seq_eq(bmap->ineq[i] + first, bmap->ineq[j] + first, n); +} + +/* Are the "n" coefficients starting at "first" of inequality constraints + * "i" and "j" of "bmap" opposite to each other? + */ +static int is_opposite_part(__isl_keep isl_basic_map *bmap, int i, int j, + int first, int n) +{ + return isl_seq_is_neg(bmap->ineq[i] + first, bmap->ineq[j] + first, n); +} + +/* Are inequality constraints "i" and "j" of "bmap" opposite to each other, + * apart from the constant term? + */ +static int is_opposite(__isl_keep isl_basic_map *bmap, int i, int j) +{ + unsigned total; + + total = isl_basic_map_dim(bmap, isl_dim_all); + return is_opposite_part(bmap, i, j, 1, total); +} + +/* Are inequality constraints "i" and "j" of "bmap" equal to each other, + * apart from the constant term and the coefficient at position "pos"? + */ +static int is_parallel_except(__isl_keep isl_basic_map *bmap, int i, int j, + int pos) +{ + unsigned total; + + total = isl_basic_map_dim(bmap, isl_dim_all); + return is_parallel_part(bmap, i, j, 1, pos - 1) && + is_parallel_part(bmap, i, j, pos + 1, total - pos); +} + +/* Are inequality constraints "i" and "j" of "bmap" opposite to each other, + * apart from the constant term and the coefficient at position "pos"? + */ +static int is_opposite_except(__isl_keep isl_basic_map *bmap, int i, int j, + int pos) +{ + unsigned total; + + total = isl_basic_map_dim(bmap, isl_dim_all); + return is_opposite_part(bmap, i, j, 1, pos - 1) && + is_opposite_part(bmap, i, j, pos + 1, total - pos); +} + +/* Restart isl_basic_map_drop_redundant_divs after "bmap" has + * been modified, simplying it if "simplify" is set. + * Free the temporary data structure "pairs" that was associated + * to the old version of "bmap". + */ +static __isl_give isl_basic_map *drop_redundant_divs_again( + __isl_take isl_basic_map *bmap, __isl_take int *pairs, int simplify) +{ + if (simplify) + bmap = isl_basic_map_simplify(bmap); + free(pairs); + return isl_basic_map_drop_redundant_divs(bmap); +} + +/* Is "div" the single unknown existentially quantified variable + * in inequality constraint "ineq" of "bmap"? + * "div" is known to have a non-zero coefficient in "ineq". + */ +static int single_unknown(__isl_keep isl_basic_map *bmap, int ineq, int div) +{ + int i; + unsigned n_div, o_div; + + if (isl_basic_map_div_is_known(bmap, div)) + return 0; + n_div = isl_basic_map_dim(bmap, isl_dim_div); + if (n_div == 1) + return 1; + o_div = isl_basic_map_offset(bmap, isl_dim_div); + for (i = 0; i < n_div; ++i) { + if (i == div) + continue; + if (isl_int_is_zero(bmap->ineq[ineq][o_div + i])) + continue; + if (!isl_basic_map_div_is_known(bmap, i)) + return 0; + } + + return 1; +} + +/* Does integer division "div" have coefficient 1 in inequality constraint + * "ineq" of "map"? + */ +static int has_coef_one(__isl_keep isl_basic_map *bmap, int div, int ineq) +{ + unsigned o_div; + + o_div = isl_basic_map_offset(bmap, isl_dim_div); + if (isl_int_is_one(bmap->ineq[ineq][o_div + div])) + return 1; + + return 0; +} + +/* Turn inequality constraint "ineq" of "bmap" into an equality and + * then try and drop redundant divs again, + * freeing the temporary data structure "pairs" that was associated + * to the old version of "bmap". + */ +static __isl_give isl_basic_map *set_eq_and_try_again( + __isl_take isl_basic_map *bmap, int ineq, __isl_take int *pairs) +{ + bmap = isl_basic_map_cow(bmap); + isl_basic_map_inequality_to_equality(bmap, ineq); + return drop_redundant_divs_again(bmap, pairs, 1); +} + +/* Drop the integer division at position "div", along with the two + * inequality constraints "ineq1" and "ineq2" in which it appears + * from "bmap" and then try and drop redundant divs again, + * freeing the temporary data structure "pairs" that was associated + * to the old version of "bmap". + */ +static __isl_give isl_basic_map *drop_div_and_try_again( + __isl_take isl_basic_map *bmap, int div, int ineq1, int ineq2, + __isl_take int *pairs) +{ + if (ineq1 > ineq2) { + isl_basic_map_drop_inequality(bmap, ineq1); + isl_basic_map_drop_inequality(bmap, ineq2); + } else { + isl_basic_map_drop_inequality(bmap, ineq2); + isl_basic_map_drop_inequality(bmap, ineq1); + } + bmap = isl_basic_map_drop_div(bmap, div); + return drop_redundant_divs_again(bmap, pairs, 0); +} + +/* Given two inequality constraints + * + * f(x) + n d + c >= 0, (ineq) + * + * with d the variable at position "pos", and + * + * f(x) + c0 >= 0, (lower) + * + * compute the maximal value of the lower bound ceil((-f(x) - c)/n) + * determined by the first constraint. + * That is, store + * + * ceil((c0 - c)/n) + * + * in *l. + */ +static void lower_bound_from_parallel(__isl_keep isl_basic_map *bmap, + int ineq, int lower, int pos, isl_int *l) +{ + isl_int_neg(*l, bmap->ineq[ineq][0]); + isl_int_add(*l, *l, bmap->ineq[lower][0]); + isl_int_cdiv_q(*l, *l, bmap->ineq[ineq][pos]); +} + +/* Given two inequality constraints + * + * f(x) + n d + c >= 0, (ineq) + * + * with d the variable at position "pos", and + * + * -f(x) - c0 >= 0, (upper) + * + * compute the minimal value of the lower bound ceil((-f(x) - c)/n) + * determined by the first constraint. + * That is, store + * + * ceil((-c1 - c)/n) + * + * in *u. + */ +static void lower_bound_from_opposite(__isl_keep isl_basic_map *bmap, + int ineq, int upper, int pos, isl_int *u) +{ + isl_int_neg(*u, bmap->ineq[ineq][0]); + isl_int_sub(*u, *u, bmap->ineq[upper][0]); + isl_int_cdiv_q(*u, *u, bmap->ineq[ineq][pos]); +} + +/* Given a lower bound constraint "ineq" on "div" in "bmap", + * does the corresponding lower bound have a fixed value in "bmap"? + * + * In particular, "ineq" is of the form + * + * f(x) + n d + c >= 0 + * + * with n > 0, c the constant term and + * d the existentially quantified variable "div". + * That is, the lower bound is + * + * ceil((-f(x) - c)/n) + * + * Look for a pair of constraints + * + * f(x) + c0 >= 0 + * -f(x) + c1 >= 0 + * + * i.e., -c1 <= -f(x) <= c0, that fix ceil((-f(x) - c)/n) to a constant value. + * That is, check that + * + * ceil((-c1 - c)/n) = ceil((c0 - c)/n) + * + * If so, return the index of inequality f(x) + c0 >= 0. + * Otherwise, return -1. + */ +static int lower_bound_is_cst(__isl_keep isl_basic_map *bmap, int div, int ineq) +{ + int i; + int lower = -1, upper = -1; + unsigned o_div, n_div; + isl_int l, u; + int equal; + + n_div = isl_basic_map_dim(bmap, isl_dim_div); + o_div = isl_basic_map_offset(bmap, isl_dim_div); + for (i = 0; i < bmap->n_ineq && (lower < 0 || upper < 0); ++i) { + if (i == ineq) + continue; + if (!isl_int_is_zero(bmap->ineq[i][o_div + div])) + continue; + if (lower < 0 && + is_parallel_except(bmap, ineq, i, o_div + div)) { + lower = i; + continue; + } + if (upper < 0 && + is_opposite_except(bmap, ineq, i, o_div + div)) { + upper = i; + } + } + + if (lower < 0 || upper < 0) + return -1; + + isl_int_init(l); + isl_int_init(u); + + lower_bound_from_parallel(bmap, ineq, lower, o_div + div, &l); + lower_bound_from_opposite(bmap, ineq, upper, o_div + div, &u); + + equal = isl_int_eq(l, u); + + isl_int_clear(l); + isl_int_clear(u); + + return equal ? lower : -1; +} + +/* Given a lower bound constraint "ineq" on the existentially quantified + * variable "div", such that the corresponding lower bound has + * a fixed value in "bmap", assign this fixed value to the variable and + * then try and drop redundant divs again, + * freeing the temporary data structure "pairs" that was associated + * to the old version of "bmap". + * "lower" determines the constant value for the lower bound. + * + * In particular, "ineq" is of the form + * + * f(x) + n d + c >= 0, + * + * while "lower" is of the form + * + * f(x) + c0 >= 0 + * + * The lower bound is ceil((-f(x) - c)/n) and its constant value + * is ceil((c0 - c)/n). + */ +static __isl_give isl_basic_map *fix_cst_lower(__isl_take isl_basic_map *bmap, + int div, int ineq, int lower, int *pairs) +{ + isl_int c; + unsigned o_div; + + isl_int_init(c); + + o_div = isl_basic_map_offset(bmap, isl_dim_div); + lower_bound_from_parallel(bmap, ineq, lower, o_div + div, &c); + bmap = isl_basic_map_fix(bmap, isl_dim_div, div, c); + free(pairs); + + isl_int_clear(c); + + return isl_basic_map_drop_redundant_divs(bmap); +} + +/* Remove divs that are not strictly needed based on the inequality + * constraints. + * In particular, if a div only occurs positively (or negatively) + * in constraints, then it can simply be dropped. + * Also, if a div occurs in only two constraints and if moreover + * those two constraints are opposite to each other, except for the constant + * term and if the sum of the constant terms is such that for any value + * of the other values, there is always at least one integer value of the + * div, i.e., if one plus this sum is greater than or equal to + * the (absolute value) of the coefficient of the div in the constraints, + * then we can also simply drop the div. + * + * If an existentially quantified variable does not have an explicit + * representation, appears in only a single lower bound that does not + * involve any other such existentially quantified variables and appears + * in this lower bound with coefficient 1, + * then fix the variable to the value of the lower bound. That is, + * turn the inequality into an equality. + * If for any value of the other variables, there is any value + * for the existentially quantified variable satisfying the constraints, + * then this lower bound also satisfies the constraints. + * It is therefore safe to pick this lower bound. + * + * The same reasoning holds even if the coefficient is not one. + * However, fixing the variable to the value of the lower bound may + * in general introduce an extra integer division, in which case + * it may be better to pick another value. + * If this integer division has a known constant value, then plugging + * in this constant value removes the existentially quantified variable + * completely. In particular, if the lower bound is of the form + * ceil((-f(x) - c)/n) and there are two constraints, f(x) + c0 >= 0 and + * -f(x) + c1 >= 0 such that ceil((-c1 - c)/n) = ceil((c0 - c)/n), + * then the existentially quantified variable can be assigned this + * shared value. + * + * We skip divs that appear in equalities or in the definition of other divs. + * Divs that appear in the definition of other divs usually occur in at least + * 4 constraints, but the constraints may have been simplified. + * + * If any divs are left after these simple checks then we move on + * to more complicated cases in drop_more_redundant_divs. + */ +static __isl_give isl_basic_map *isl_basic_map_drop_redundant_divs_ineq( + __isl_take isl_basic_map *bmap) +{ + int i, j; + unsigned off; + int *pairs = NULL; + int n = 0; + + if (!bmap) + goto error; + if (bmap->n_div == 0) + return bmap; + + off = isl_space_dim(bmap->dim, isl_dim_all); + pairs = isl_calloc_array(bmap->ctx, int, bmap->n_div); + if (!pairs) + goto error; + + for (i = 0; i < bmap->n_div; ++i) { + int pos, neg; + int last_pos, last_neg; + int redundant; + int defined; + + defined = !isl_int_is_zero(bmap->div[i][0]); + for (j = i; j < bmap->n_div; ++j) + if (!isl_int_is_zero(bmap->div[j][1 + 1 + off + i])) + break; + if (j < bmap->n_div) + continue; + for (j = 0; j < bmap->n_eq; ++j) + if (!isl_int_is_zero(bmap->eq[j][1 + off + i])) + break; + if (j < bmap->n_eq) + continue; + ++n; + pos = neg = 0; + for (j = 0; j < bmap->n_ineq; ++j) { + if (isl_int_is_pos(bmap->ineq[j][1 + off + i])) { + last_pos = j; + ++pos; + } + if (isl_int_is_neg(bmap->ineq[j][1 + off + i])) { + last_neg = j; + ++neg; + } + } + pairs[i] = pos * neg; + if (pairs[i] == 0) { + for (j = bmap->n_ineq - 1; j >= 0; --j) + if (!isl_int_is_zero(bmap->ineq[j][1+off+i])) + isl_basic_map_drop_inequality(bmap, j); + bmap = isl_basic_map_drop_div(bmap, i); + return drop_redundant_divs_again(bmap, pairs, 0); + } + if (pairs[i] != 1 || !is_opposite(bmap, last_pos, last_neg)) { + int single, lower; + if (pos != 1) + continue; + single = single_unknown(bmap, last_pos, i); + if (!single) + continue; + if (has_coef_one(bmap, i, last_pos)) + return set_eq_and_try_again(bmap, last_pos, + pairs); + lower = lower_bound_is_cst(bmap, i, last_pos); + if (lower >= 0) + return fix_cst_lower(bmap, i, last_pos, lower, + pairs); + continue; + } + + isl_int_add(bmap->ineq[last_pos][0], + bmap->ineq[last_pos][0], bmap->ineq[last_neg][0]); + isl_int_add_ui(bmap->ineq[last_pos][0], + bmap->ineq[last_pos][0], 1); + redundant = isl_int_ge(bmap->ineq[last_pos][0], + bmap->ineq[last_pos][1+off+i]); + isl_int_sub_ui(bmap->ineq[last_pos][0], + bmap->ineq[last_pos][0], 1); + isl_int_sub(bmap->ineq[last_pos][0], + bmap->ineq[last_pos][0], bmap->ineq[last_neg][0]); + if (redundant) + return drop_div_and_try_again(bmap, i, + last_pos, last_neg, pairs); + if (!defined && ok_to_set_div_from_bound(bmap, i, last_pos)) { + bmap = set_div_from_lower_bound(bmap, i, last_pos); + return drop_redundant_divs_again(bmap, pairs, 1); + } + pairs[i] = 0; + --n; + } + + if (n > 0) + return coalesce_or_drop_more_redundant_divs(bmap, pairs, n); + + free(pairs); + return bmap; +error: + free(pairs); + isl_basic_map_free(bmap); + return NULL; +} + +/* Consider the coefficients at "c" as a row vector and replace + * them with their product with "T". "T" is assumed to be a square matrix. + */ +static isl_stat preimage(isl_int *c, __isl_keep isl_mat *T) +{ + int n; + isl_ctx *ctx; + isl_vec *v; + + if (!T) + return isl_stat_error; + n = isl_mat_rows(T); + if (isl_seq_first_non_zero(c, n) == -1) + return isl_stat_ok; + ctx = isl_mat_get_ctx(T); + v = isl_vec_alloc(ctx, n); + if (!v) + return isl_stat_error; + isl_seq_swp_or_cpy(v->el, c, n); + v = isl_vec_mat_product(v, isl_mat_copy(T)); + if (!v) + return isl_stat_error; + isl_seq_swp_or_cpy(c, v->el, n); + isl_vec_free(v); + + return isl_stat_ok; +} + +/* Plug in T for the variables in "bmap" starting at "pos". + * T is a linear unimodular matrix, i.e., without constant term. + */ +static __isl_give isl_basic_map *isl_basic_map_preimage_vars( + __isl_take isl_basic_map *bmap, unsigned pos, __isl_take isl_mat *T) +{ + int i; + unsigned n, total; + + bmap = isl_basic_map_cow(bmap); + if (!bmap || !T) + goto error; + + n = isl_mat_cols(T); + if (n != isl_mat_rows(T)) + isl_die(isl_mat_get_ctx(T), isl_error_invalid, + "expecting square matrix", goto error); + + total = isl_basic_map_dim(bmap, isl_dim_all); + if (pos + n > total || pos + n < pos) + isl_die(isl_mat_get_ctx(T), isl_error_invalid, + "invalid range", goto error); + + for (i = 0; i < bmap->n_eq; ++i) + if (preimage(bmap->eq[i] + 1 + pos, T) < 0) + goto error; + for (i = 0; i < bmap->n_ineq; ++i) + if (preimage(bmap->ineq[i] + 1 + pos, T) < 0) + goto error; + for (i = 0; i < bmap->n_div; ++i) { + if (!isl_basic_map_div_is_known(bmap, i)) + continue; + if (preimage(bmap->div[i] + 1 + 1 + pos, T) < 0) + goto error; + } + + isl_mat_free(T); + return bmap; +error: + isl_basic_map_free(bmap); + isl_mat_free(T); + return NULL; +} + +/* Remove divs that are not strictly needed. + * + * First look for an equality constraint involving two or more + * existentially quantified variables without an explicit + * representation. Replace the combination that appears + * in the equality constraint by a single existentially quantified + * variable such that the equality can be used to derive + * an explicit representation for the variable. + * If there are no more such equality constraints, then continue + * with isl_basic_map_drop_redundant_divs_ineq. + * + * In particular, if the equality constraint is of the form + * + * f(x) + \sum_i c_i a_i = 0 + * + * with a_i existentially quantified variable without explicit + * representation, then apply a transformation on the existentially + * quantified variables to turn the constraint into + * + * f(x) + g a_1' = 0 + * + * with g the gcd of the c_i. + * In order to easily identify which existentially quantified variables + * have a complete explicit representation, i.e., without being defined + * in terms of other existentially quantified variables without + * an explicit representation, the existentially quantified variables + * are first sorted. + * + * The variable transformation is computed by extending the row + * [c_1/g ... c_n/g] to a unimodular matrix, obtaining the transformation + * + * [a_1'] [c_1/g ... c_n/g] [ a_1 ] + * [a_2'] [ a_2 ] + * ... = U .... + * [a_n'] [ a_n ] + * + * with [c_1/g ... c_n/g] representing the first row of U. + * The inverse of U is then plugged into the original constraints. + * The call to isl_basic_map_simplify makes sure the explicit + * representation for a_1' is extracted from the equality constraint. + */ +__isl_give isl_basic_map *isl_basic_map_drop_redundant_divs( + __isl_take isl_basic_map *bmap) +{ + int first; + int i; + unsigned o_div, n_div; + int l; + isl_ctx *ctx; + isl_mat *T; + + if (!bmap) + return NULL; + if (isl_basic_map_divs_known(bmap)) + return isl_basic_map_drop_redundant_divs_ineq(bmap); + if (bmap->n_eq == 0) + return isl_basic_map_drop_redundant_divs_ineq(bmap); + bmap = isl_basic_map_sort_divs(bmap); + if (!bmap) + return NULL; + + first = isl_basic_map_first_unknown_div(bmap); + if (first < 0) + return isl_basic_map_free(bmap); + + o_div = isl_basic_map_offset(bmap, isl_dim_div); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + + for (i = 0; i < bmap->n_eq; ++i) { + l = isl_seq_first_non_zero(bmap->eq[i] + o_div + first, + n_div - (first)); + if (l < 0) + continue; + l += first; + if (isl_seq_first_non_zero(bmap->eq[i] + o_div + l + 1, + n_div - (l + 1)) == -1) + continue; + break; + } + if (i >= bmap->n_eq) + return isl_basic_map_drop_redundant_divs_ineq(bmap); + + ctx = isl_basic_map_get_ctx(bmap); + T = isl_mat_alloc(ctx, n_div - l, n_div - l); + if (!T) + return isl_basic_map_free(bmap); + isl_seq_cpy(T->row[0], bmap->eq[i] + o_div + l, n_div - l); + T = isl_mat_normalize_row(T, 0); + T = isl_mat_unimodular_complete(T, 1); + T = isl_mat_right_inverse(T); + + for (i = l; i < n_div; ++i) + bmap = isl_basic_map_mark_div_unknown(bmap, i); + bmap = isl_basic_map_preimage_vars(bmap, o_div - 1 + l, T); + bmap = isl_basic_map_simplify(bmap); + + return isl_basic_map_drop_redundant_divs(bmap); +} + +struct isl_basic_set *isl_basic_set_drop_redundant_divs( + struct isl_basic_set *bset) +{ + return (struct isl_basic_set *) + isl_basic_map_drop_redundant_divs((struct isl_basic_map *)bset); +} + +struct isl_map *isl_map_drop_redundant_divs(struct isl_map *map) +{ + int i; + + if (!map) + return NULL; + for (i = 0; i < map->n; ++i) { + map->p[i] = isl_basic_map_drop_redundant_divs(map->p[i]); + if (!map->p[i]) + goto error; + } + ISL_F_CLR(map, ISL_MAP_NORMALIZED); + return map; +error: + isl_map_free(map); + return NULL; +} + +struct isl_set *isl_set_drop_redundant_divs(struct isl_set *set) +{ + return (struct isl_set *) + isl_map_drop_redundant_divs((struct isl_map *)set); +} + +/* Does "bmap" satisfy any equality that involves more than 2 variables + * and/or has coefficients different from -1 and 1? + */ +static int has_multiple_var_equality(__isl_keep isl_basic_map *bmap) +{ + int i; + unsigned total; + + total = isl_basic_map_dim(bmap, isl_dim_all); + + for (i = 0; i < bmap->n_eq; ++i) { + int j, k; + + j = isl_seq_first_non_zero(bmap->eq[i] + 1, total); + if (j < 0) + continue; + if (!isl_int_is_one(bmap->eq[i][1 + j]) && + !isl_int_is_negone(bmap->eq[i][1 + j])) + return 1; + + j += 1; + k = isl_seq_first_non_zero(bmap->eq[i] + 1 + j, total - j); + if (k < 0) + continue; + j += k; + if (!isl_int_is_one(bmap->eq[i][1 + j]) && + !isl_int_is_negone(bmap->eq[i][1 + j])) + return 1; + + j += 1; + k = isl_seq_first_non_zero(bmap->eq[i] + 1 + j, total - j); + if (k >= 0) + return 1; + } + + return 0; +} + +/* Remove any common factor g from the constraint coefficients in "v". + * The constant term is stored in the first position and is replaced + * by floor(c/g). If any common factor is removed and if this results + * in a tightening of the constraint, then set *tightened. + */ +static __isl_give isl_vec *normalize_constraint(__isl_take isl_vec *v, + int *tightened) +{ + isl_ctx *ctx; + + if (!v) + return NULL; + ctx = isl_vec_get_ctx(v); + isl_seq_gcd(v->el + 1, v->size - 1, &ctx->normalize_gcd); + if (isl_int_is_zero(ctx->normalize_gcd)) + return v; + if (isl_int_is_one(ctx->normalize_gcd)) + return v; + v = isl_vec_cow(v); + if (!v) + return NULL; + if (tightened && !isl_int_is_divisible_by(v->el[0], ctx->normalize_gcd)) + *tightened = 1; + isl_int_fdiv_q(v->el[0], v->el[0], ctx->normalize_gcd); + isl_seq_scale_down(v->el + 1, v->el + 1, ctx->normalize_gcd, + v->size - 1); + return v; +} + +/* If "bmap" is an integer set that satisfies any equality involving + * more than 2 variables and/or has coefficients different from -1 and 1, + * then use variable compression to reduce the coefficients by removing + * any (hidden) common factor. + * In particular, apply the variable compression to each constraint, + * factor out any common factor in the non-constant coefficients and + * then apply the inverse of the compression. + * At the end, we mark the basic map as having reduced constants. + * If this flag is still set on the next invocation of this function, + * then we skip the computation. + * + * Removing a common factor may result in a tightening of some of + * the constraints. If this happens, then we may end up with two + * opposite inequalities that can be replaced by an equality. + * We therefore call isl_basic_map_detect_inequality_pairs, + * which checks for such pairs of inequalities as well as eliminate_divs_eq + * and isl_basic_map_gauss if such a pair was found. + */ +__isl_give isl_basic_map *isl_basic_map_reduce_coefficients( + __isl_take isl_basic_map *bmap) +{ + unsigned total; + isl_ctx *ctx; + isl_vec *v; + isl_mat *eq, *T, *T2; + int i; + int tightened; + + if (!bmap) + return NULL; + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS)) + return bmap; + if (isl_basic_map_is_rational(bmap)) + return bmap; + if (bmap->n_eq == 0) + return bmap; + if (!has_multiple_var_equality(bmap)) + return bmap; + + total = isl_basic_map_dim(bmap, isl_dim_all); + ctx = isl_basic_map_get_ctx(bmap); + v = isl_vec_alloc(ctx, 1 + total); + if (!v) + return isl_basic_map_free(bmap); + + eq = isl_mat_sub_alloc6(ctx, bmap->eq, 0, bmap->n_eq, 0, 1 + total); + T = isl_mat_variable_compression(eq, &T2); + if (!T || !T2) + goto error; + if (T->n_col == 0) { + isl_mat_free(T); + isl_mat_free(T2); + isl_vec_free(v); + return isl_basic_map_set_to_empty(bmap); + } + + tightened = 0; + for (i = 0; i < bmap->n_ineq; ++i) { + isl_seq_cpy(v->el, bmap->ineq[i], 1 + total); + v = isl_vec_mat_product(v, isl_mat_copy(T)); + v = normalize_constraint(v, &tightened); + v = isl_vec_mat_product(v, isl_mat_copy(T2)); + if (!v) + goto error; + isl_seq_cpy(bmap->ineq[i], v->el, 1 + total); + } + + isl_mat_free(T); + isl_mat_free(T2); + isl_vec_free(v); + + ISL_F_SET(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS); + + if (tightened) { + int progress = 0; + + bmap = isl_basic_map_detect_inequality_pairs(bmap, &progress); + if (progress) { + bmap = eliminate_divs_eq(bmap, &progress); + bmap = isl_basic_map_gauss(bmap, NULL); + } + } + + return bmap; +error: + isl_mat_free(T); + isl_mat_free(T2); + isl_vec_free(v); + return isl_basic_map_free(bmap); +} + +/* Shift the integer division at position "div" of "bmap" + * by "shift" times the variable at position "pos". + * "pos" is as determined by isl_basic_map_offset, i.e., pos == 0 + * corresponds to the constant term. + * + * That is, if the integer division has the form + * + * floor(f(x)/d) + * + * then replace it by + * + * floor((f(x) + shift * d * x_pos)/d) - shift * x_pos + */ +__isl_give isl_basic_map *isl_basic_map_shift_div( + __isl_take isl_basic_map *bmap, int div, int pos, isl_int shift) +{ + int i; + unsigned total; + + if (!bmap) + return NULL; + + total = isl_basic_map_dim(bmap, isl_dim_all); + total -= isl_basic_map_dim(bmap, isl_dim_div); + + isl_int_addmul(bmap->div[div][1 + pos], shift, bmap->div[div][0]); + + for (i = 0; i < bmap->n_eq; ++i) { + if (isl_int_is_zero(bmap->eq[i][1 + total + div])) + continue; + isl_int_submul(bmap->eq[i][pos], + shift, bmap->eq[i][1 + total + div]); + } + for (i = 0; i < bmap->n_ineq; ++i) { + if (isl_int_is_zero(bmap->ineq[i][1 + total + div])) + continue; + isl_int_submul(bmap->ineq[i][pos], + shift, bmap->ineq[i][1 + total + div]); + } + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (isl_int_is_zero(bmap->div[i][1 + 1 + total + div])) + continue; + isl_int_submul(bmap->div[i][1 + pos], + shift, bmap->div[i][1 + 1 + total + div]); + } + + return bmap; +} Index: lib/Analysis/isl/isl_map_subtract.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map_subtract.c @@ -0,0 +1,931 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include "isl_tab.h" +#include +#include + +/* Expand the constraint "c" into "v". The initial "dim" dimensions + * are the same, but "v" may have more divs than "c" and the divs of "c" + * may appear in different positions in "v". + * The number of divs in "c" is given by "n_div" and the mapping + * of divs in "c" to divs in "v" is given by "div_map". + * + * Although it shouldn't happen in practice, it is theoretically + * possible that two or more divs in "c" are mapped to the same div in "v". + * These divs are then necessarily the same, so we simply add their + * coefficients. + */ +static void expand_constraint(isl_vec *v, unsigned dim, + isl_int *c, int *div_map, unsigned n_div) +{ + int i; + + isl_seq_cpy(v->el, c, 1 + dim); + isl_seq_clr(v->el + 1 + dim, v->size - (1 + dim)); + + for (i = 0; i < n_div; ++i) { + int pos = 1 + dim + div_map[i]; + isl_int_add(v->el[pos], v->el[pos], c[1 + dim + i]); + } +} + +/* Add all constraints of bmap to tab. The equalities of bmap + * are added as a pair of inequalities. + */ +static int tab_add_constraints(struct isl_tab *tab, + __isl_keep isl_basic_map *bmap, int *div_map) +{ + int i; + unsigned dim; + unsigned tab_total; + unsigned bmap_total; + isl_vec *v; + + if (!tab || !bmap) + return -1; + + tab_total = isl_basic_map_total_dim(tab->bmap); + bmap_total = isl_basic_map_total_dim(bmap); + dim = isl_space_dim(tab->bmap->dim, isl_dim_all); + + if (isl_tab_extend_cons(tab, 2 * bmap->n_eq + bmap->n_ineq) < 0) + return -1; + + v = isl_vec_alloc(bmap->ctx, 1 + tab_total); + if (!v) + return -1; + + for (i = 0; i < bmap->n_eq; ++i) { + expand_constraint(v, dim, bmap->eq[i], div_map, bmap->n_div); + if (isl_tab_add_ineq(tab, v->el) < 0) + goto error; + isl_seq_neg(bmap->eq[i], bmap->eq[i], 1 + bmap_total); + expand_constraint(v, dim, bmap->eq[i], div_map, bmap->n_div); + if (isl_tab_add_ineq(tab, v->el) < 0) + goto error; + isl_seq_neg(bmap->eq[i], bmap->eq[i], 1 + bmap_total); + if (tab->empty) + break; + } + + for (i = 0; i < bmap->n_ineq; ++i) { + expand_constraint(v, dim, bmap->ineq[i], div_map, bmap->n_div); + if (isl_tab_add_ineq(tab, v->el) < 0) + goto error; + if (tab->empty) + break; + } + + isl_vec_free(v); + return 0; +error: + isl_vec_free(v); + return -1; +} + +/* Add a specific constraint of bmap (or its opposite) to tab. + * The position of the constraint is specified by "c", where + * the equalities of bmap are counted twice, once for the inequality + * that is equal to the equality, and once for its negation. + * + * Each of these constraints has been added to "tab" before by + * tab_add_constraints (and later removed again), so there should + * already be a row available for the constraint. + */ +static int tab_add_constraint(struct isl_tab *tab, + __isl_keep isl_basic_map *bmap, int *div_map, int c, int oppose) +{ + unsigned dim; + unsigned tab_total; + unsigned bmap_total; + isl_vec *v; + int r; + + if (!tab || !bmap) + return -1; + + tab_total = isl_basic_map_total_dim(tab->bmap); + bmap_total = isl_basic_map_total_dim(bmap); + dim = isl_space_dim(tab->bmap->dim, isl_dim_all); + + v = isl_vec_alloc(bmap->ctx, 1 + tab_total); + if (!v) + return -1; + + if (c < 2 * bmap->n_eq) { + if ((c % 2) != oppose) + isl_seq_neg(bmap->eq[c/2], bmap->eq[c/2], + 1 + bmap_total); + if (oppose) + isl_int_sub_ui(bmap->eq[c/2][0], bmap->eq[c/2][0], 1); + expand_constraint(v, dim, bmap->eq[c/2], div_map, bmap->n_div); + r = isl_tab_add_ineq(tab, v->el); + if (oppose) + isl_int_add_ui(bmap->eq[c/2][0], bmap->eq[c/2][0], 1); + if ((c % 2) != oppose) + isl_seq_neg(bmap->eq[c/2], bmap->eq[c/2], + 1 + bmap_total); + } else { + c -= 2 * bmap->n_eq; + if (oppose) { + isl_seq_neg(bmap->ineq[c], bmap->ineq[c], + 1 + bmap_total); + isl_int_sub_ui(bmap->ineq[c][0], bmap->ineq[c][0], 1); + } + expand_constraint(v, dim, bmap->ineq[c], div_map, bmap->n_div); + r = isl_tab_add_ineq(tab, v->el); + if (oppose) { + isl_int_add_ui(bmap->ineq[c][0], bmap->ineq[c][0], 1); + isl_seq_neg(bmap->ineq[c], bmap->ineq[c], + 1 + bmap_total); + } + } + + isl_vec_free(v); + return r; +} + +static int tab_add_divs(struct isl_tab *tab, __isl_keep isl_basic_map *bmap, + int **div_map) +{ + int i, j; + struct isl_vec *vec; + unsigned total; + unsigned dim; + + if (!bmap) + return -1; + if (!bmap->n_div) + return 0; + + if (!*div_map) + *div_map = isl_alloc_array(bmap->ctx, int, bmap->n_div); + if (!*div_map) + return -1; + + total = isl_basic_map_total_dim(tab->bmap); + dim = total - tab->bmap->n_div; + vec = isl_vec_alloc(bmap->ctx, 2 + total + bmap->n_div); + if (!vec) + return -1; + + for (i = 0; i < bmap->n_div; ++i) { + isl_seq_cpy(vec->el, bmap->div[i], 2 + dim); + isl_seq_clr(vec->el + 2 + dim, tab->bmap->n_div); + for (j = 0; j < i; ++j) + isl_int_set(vec->el[2 + dim + (*div_map)[j]], + bmap->div[i][2 + dim + j]); + for (j = 0; j < tab->bmap->n_div; ++j) + if (isl_seq_eq(tab->bmap->div[j], + vec->el, 2 + dim + tab->bmap->n_div)) + break; + (*div_map)[i] = j; + if (j == tab->bmap->n_div) { + vec->size = 2 + dim + tab->bmap->n_div; + if (isl_tab_add_div(tab, vec, NULL, NULL) < 0) + goto error; + } + } + + isl_vec_free(vec); + + return 0; +error: + isl_vec_free(vec); + + return -1; +} + +/* Freeze all constraints of tableau tab. + */ +static int tab_freeze_constraints(struct isl_tab *tab) +{ + int i; + + for (i = 0; i < tab->n_con; ++i) + if (isl_tab_freeze_constraint(tab, i) < 0) + return -1; + + return 0; +} + +/* Check for redundant constraints starting at offset. + * Put the indices of the redundant constraints in index + * and return the number of redundant constraints. + */ +static int n_non_redundant(isl_ctx *ctx, struct isl_tab *tab, + int offset, int **index) +{ + int i, n; + int n_test = tab->n_con - offset; + + if (isl_tab_detect_redundant(tab) < 0) + return -1; + + if (n_test == 0) + return 0; + if (!*index) + *index = isl_alloc_array(ctx, int, n_test); + if (!*index) + return -1; + + for (n = 0, i = 0; i < n_test; ++i) { + int r; + r = isl_tab_is_redundant(tab, offset + i); + if (r < 0) + return -1; + if (r) + continue; + (*index)[n++] = i; + } + + return n; +} + +/* basic_map_collect_diff calls add on each of the pieces of + * the set difference between bmap and map until the add method + * return a negative value. + */ +struct isl_diff_collector { + int (*add)(struct isl_diff_collector *dc, + __isl_take isl_basic_map *bmap); +}; + +/* Compute the set difference between bmap and map and call + * dc->add on each of the piece until this function returns + * a negative value. + * Return 0 on success and -1 on error. dc->add returning + * a negative value is treated as an error, but the calling + * function can interpret the results based on the state of dc. + * + * Assumes that map has known divs. + * + * The difference is computed by a backtracking algorithm. + * Each level corresponds to a basic map in "map". + * When a node in entered for the first time, we check + * if the corresonding basic map intersects the current piece + * of "bmap". If not, we move to the next level. + * Otherwise, we split the current piece into as many + * pieces as there are non-redundant constraints of the current + * basic map in the intersection. Each of these pieces is + * handled by a child of the current node. + * In particular, if there are n non-redundant constraints, + * then for each 0 <= i < n, a piece is cut off by adding + * constraints 0 <= j < i and adding the opposite of constraint i. + * If there are no non-redundant constraints, meaning that the current + * piece is a subset of the current basic map, then we simply backtrack. + * + * In the leaves, we check if the remaining piece has any integer points + * and if so, pass it along to dc->add. As a special case, if nothing + * has been removed when we end up in a leaf, we simply pass along + * the original basic map. + */ +static isl_stat basic_map_collect_diff(__isl_take isl_basic_map *bmap, + __isl_take isl_map *map, struct isl_diff_collector *dc) +{ + int i; + int modified; + int level; + int init; + isl_bool empty; + isl_ctx *ctx; + struct isl_tab *tab = NULL; + struct isl_tab_undo **snap = NULL; + int *k = NULL; + int *n = NULL; + int **index = NULL; + int **div_map = NULL; + + empty = isl_basic_map_is_empty(bmap); + if (empty) { + isl_basic_map_free(bmap); + isl_map_free(map); + return empty < 0 ? isl_stat_error : isl_stat_ok; + } + + bmap = isl_basic_map_cow(bmap); + map = isl_map_cow(map); + + if (!bmap || !map) + goto error; + + ctx = map->ctx; + snap = isl_alloc_array(map->ctx, struct isl_tab_undo *, map->n); + k = isl_alloc_array(map->ctx, int, map->n); + n = isl_alloc_array(map->ctx, int, map->n); + index = isl_calloc_array(map->ctx, int *, map->n); + div_map = isl_calloc_array(map->ctx, int *, map->n); + if (!snap || !k || !n || !index || !div_map) + goto error; + + bmap = isl_basic_map_order_divs(bmap); + map = isl_map_order_divs(map); + + tab = isl_tab_from_basic_map(bmap, 1); + if (!tab) + goto error; + + modified = 0; + level = 0; + init = 1; + + while (level >= 0) { + if (level >= map->n) { + int empty; + struct isl_basic_map *bm; + if (!modified) { + if (dc->add(dc, isl_basic_map_copy(bmap)) < 0) + goto error; + break; + } + bm = isl_basic_map_copy(tab->bmap); + bm = isl_basic_map_cow(bm); + bm = isl_basic_map_update_from_tab(bm, tab); + bm = isl_basic_map_simplify(bm); + bm = isl_basic_map_finalize(bm); + empty = isl_basic_map_is_empty(bm); + if (empty) + isl_basic_map_free(bm); + else if (dc->add(dc, bm) < 0) + goto error; + if (empty < 0) + goto error; + level--; + init = 0; + continue; + } + if (init) { + int offset; + struct isl_tab_undo *snap2; + snap2 = isl_tab_snap(tab); + if (tab_add_divs(tab, map->p[level], + &div_map[level]) < 0) + goto error; + offset = tab->n_con; + snap[level] = isl_tab_snap(tab); + if (tab_freeze_constraints(tab) < 0) + goto error; + if (tab_add_constraints(tab, map->p[level], + div_map[level]) < 0) + goto error; + k[level] = 0; + n[level] = 0; + if (tab->empty) { + if (isl_tab_rollback(tab, snap2) < 0) + goto error; + level++; + continue; + } + modified = 1; + n[level] = n_non_redundant(ctx, tab, offset, + &index[level]); + if (n[level] < 0) + goto error; + if (n[level] == 0) { + level--; + init = 0; + continue; + } + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + if (tab_add_constraint(tab, map->p[level], + div_map[level], index[level][0], 1) < 0) + goto error; + level++; + continue; + } else { + if (k[level] + 1 >= n[level]) { + level--; + continue; + } + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + if (tab_add_constraint(tab, map->p[level], + div_map[level], + index[level][k[level]], 0) < 0) + goto error; + snap[level] = isl_tab_snap(tab); + k[level]++; + if (tab_add_constraint(tab, map->p[level], + div_map[level], + index[level][k[level]], 1) < 0) + goto error; + level++; + init = 1; + continue; + } + } + + isl_tab_free(tab); + free(snap); + free(n); + free(k); + for (i = 0; index && i < map->n; ++i) + free(index[i]); + free(index); + for (i = 0; div_map && i < map->n; ++i) + free(div_map[i]); + free(div_map); + + isl_basic_map_free(bmap); + isl_map_free(map); + + return isl_stat_ok; +error: + isl_tab_free(tab); + free(snap); + free(n); + free(k); + for (i = 0; index && i < map->n; ++i) + free(index[i]); + free(index); + for (i = 0; div_map && i < map->n; ++i) + free(div_map[i]); + free(div_map); + isl_basic_map_free(bmap); + isl_map_free(map); + return isl_stat_error; +} + +/* A diff collector that actually collects all parts of the + * set difference in the field diff. + */ +struct isl_subtract_diff_collector { + struct isl_diff_collector dc; + struct isl_map *diff; +}; + +/* isl_subtract_diff_collector callback. + */ +static int basic_map_subtract_add(struct isl_diff_collector *dc, + __isl_take isl_basic_map *bmap) +{ + struct isl_subtract_diff_collector *sdc; + sdc = (struct isl_subtract_diff_collector *)dc; + + sdc->diff = isl_map_union_disjoint(sdc->diff, + isl_map_from_basic_map(bmap)); + + return sdc->diff ? 0 : -1; +} + +/* Return the set difference between bmap and map. + */ +static __isl_give isl_map *basic_map_subtract(__isl_take isl_basic_map *bmap, + __isl_take isl_map *map) +{ + struct isl_subtract_diff_collector sdc; + sdc.dc.add = &basic_map_subtract_add; + sdc.diff = isl_map_empty(isl_basic_map_get_space(bmap)); + if (basic_map_collect_diff(bmap, map, &sdc.dc) < 0) { + isl_map_free(sdc.diff); + sdc.diff = NULL; + } + return sdc.diff; +} + +/* Return an empty map living in the same space as "map1" and "map2". + */ +static __isl_give isl_map *replace_pair_by_empty( __isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + isl_space *space; + + space = isl_map_get_space(map1); + isl_map_free(map1); + isl_map_free(map2); + return isl_map_empty(space); +} + +/* Return the set difference between map1 and map2. + * (U_i A_i) \ (U_j B_j) is computed as U_i (A_i \ (U_j B_j)) + * + * If "map1" and "map2" are obviously equal to each other, + * then return an empty map in the same space. + * + * If "map1" and "map2" are disjoint, then simply return "map1". + */ +static __isl_give isl_map *map_subtract( __isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + int i; + int equal, disjoint; + struct isl_map *diff; + + if (!map1 || !map2) + goto error; + + isl_assert(map1->ctx, isl_space_is_equal(map1->dim, map2->dim), goto error); + + equal = isl_map_plain_is_equal(map1, map2); + if (equal < 0) + goto error; + if (equal) + return replace_pair_by_empty(map1, map2); + + disjoint = isl_map_is_disjoint(map1, map2); + if (disjoint < 0) + goto error; + if (disjoint) { + isl_map_free(map2); + return map1; + } + + map1 = isl_map_compute_divs(map1); + map2 = isl_map_compute_divs(map2); + if (!map1 || !map2) + goto error; + + map1 = isl_map_remove_empty_parts(map1); + map2 = isl_map_remove_empty_parts(map2); + + diff = isl_map_empty(isl_map_get_space(map1)); + for (i = 0; i < map1->n; ++i) { + struct isl_map *d; + d = basic_map_subtract(isl_basic_map_copy(map1->p[i]), + isl_map_copy(map2)); + if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT)) + diff = isl_map_union_disjoint(diff, d); + else + diff = isl_map_union(diff, d); + } + + isl_map_free(map1); + isl_map_free(map2); + + return diff; +error: + isl_map_free(map1); + isl_map_free(map2); + return NULL; +} + +__isl_give isl_map *isl_map_subtract( __isl_take isl_map *map1, + __isl_take isl_map *map2) +{ + return isl_map_align_params_map_map_and(map1, map2, &map_subtract); +} + +struct isl_set *isl_set_subtract(struct isl_set *set1, struct isl_set *set2) +{ + return (struct isl_set *) + isl_map_subtract( + (struct isl_map *)set1, (struct isl_map *)set2); +} + +/* Remove the elements of "dom" from the domain of "map". + */ +static __isl_give isl_map *map_subtract_domain(__isl_take isl_map *map, + __isl_take isl_set *dom) +{ + isl_map *ext_dom; + + if (!isl_map_compatible_domain(map, dom)) + isl_die(isl_set_get_ctx(dom), isl_error_invalid, + "incompatible spaces", goto error); + + ext_dom = isl_map_universe(isl_map_get_space(map)); + ext_dom = isl_map_intersect_domain(ext_dom, dom); + return isl_map_subtract(map, ext_dom); +error: + isl_map_free(map); + isl_set_free(dom); + return NULL; +} + +__isl_give isl_map *isl_map_subtract_domain(__isl_take isl_map *map, + __isl_take isl_set *dom) +{ + return isl_map_align_params_map_map_and(map, dom, &map_subtract_domain); +} + +/* Remove the elements of "dom" from the range of "map". + */ +static __isl_give isl_map *map_subtract_range(__isl_take isl_map *map, + __isl_take isl_set *dom) +{ + isl_map *ext_dom; + + if (!isl_map_compatible_range(map, dom)) + isl_die(isl_set_get_ctx(dom), isl_error_invalid, + "incompatible spaces", goto error); + + ext_dom = isl_map_universe(isl_map_get_space(map)); + ext_dom = isl_map_intersect_range(ext_dom, dom); + return isl_map_subtract(map, ext_dom); +error: + isl_map_free(map); + isl_set_free(dom); + return NULL; +} + +__isl_give isl_map *isl_map_subtract_range(__isl_take isl_map *map, + __isl_take isl_set *dom) +{ + return isl_map_align_params_map_map_and(map, dom, &map_subtract_range); +} + +/* A diff collector that aborts as soon as its add function is called, + * setting empty to 0. + */ +struct isl_is_empty_diff_collector { + struct isl_diff_collector dc; + isl_bool empty; +}; + +/* isl_is_empty_diff_collector callback. + */ +static int basic_map_is_empty_add(struct isl_diff_collector *dc, + __isl_take isl_basic_map *bmap) +{ + struct isl_is_empty_diff_collector *edc; + edc = (struct isl_is_empty_diff_collector *)dc; + + edc->empty = 0; + + isl_basic_map_free(bmap); + return -1; +} + +/* Check if bmap \ map is empty by computing this set difference + * and breaking off as soon as the difference is known to be non-empty. + */ +static isl_bool basic_map_diff_is_empty(__isl_keep isl_basic_map *bmap, + __isl_keep isl_map *map) +{ + isl_bool empty; + isl_stat r; + struct isl_is_empty_diff_collector edc; + + empty = isl_basic_map_plain_is_empty(bmap); + if (empty) + return empty; + + edc.dc.add = &basic_map_is_empty_add; + edc.empty = isl_bool_true; + r = basic_map_collect_diff(isl_basic_map_copy(bmap), + isl_map_copy(map), &edc.dc); + if (!edc.empty) + return isl_bool_false; + + return r < 0 ? isl_bool_error : isl_bool_true; +} + +/* Check if map1 \ map2 is empty by checking if the set difference is empty + * for each of the basic maps in map1. + */ +static isl_bool map_diff_is_empty(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + int i; + isl_bool is_empty = isl_bool_true; + + if (!map1 || !map2) + return isl_bool_error; + + for (i = 0; i < map1->n; ++i) { + is_empty = basic_map_diff_is_empty(map1->p[i], map2); + if (is_empty < 0 || !is_empty) + break; + } + + return is_empty; +} + +/* Return 1 if "bmap" contains a single element. + */ +int isl_basic_map_plain_is_singleton(__isl_keep isl_basic_map *bmap) +{ + if (!bmap) + return -1; + if (bmap->n_div) + return 0; + if (bmap->n_ineq) + return 0; + return bmap->n_eq == isl_basic_map_total_dim(bmap); +} + +/* Return 1 if "map" contains a single element. + */ +int isl_map_plain_is_singleton(__isl_keep isl_map *map) +{ + if (!map) + return -1; + if (map->n != 1) + return 0; + + return isl_basic_map_plain_is_singleton(map->p[0]); +} + +/* Given a singleton basic map, extract the single element + * as an isl_point. + */ +static __isl_give isl_point *singleton_extract_point( + __isl_keep isl_basic_map *bmap) +{ + int j; + unsigned dim; + struct isl_vec *point; + isl_int m; + + if (!bmap) + return NULL; + + dim = isl_basic_map_total_dim(bmap); + isl_assert(bmap->ctx, bmap->n_eq == dim, return NULL); + point = isl_vec_alloc(bmap->ctx, 1 + dim); + if (!point) + return NULL; + + isl_int_init(m); + + isl_int_set_si(point->el[0], 1); + for (j = 0; j < bmap->n_eq; ++j) { + int i = dim - 1 - j; + isl_assert(bmap->ctx, + isl_seq_first_non_zero(bmap->eq[j] + 1, i) == -1, + goto error); + isl_assert(bmap->ctx, + isl_int_is_one(bmap->eq[j][1 + i]) || + isl_int_is_negone(bmap->eq[j][1 + i]), + goto error); + isl_assert(bmap->ctx, + isl_seq_first_non_zero(bmap->eq[j]+1+i+1, dim-i-1) == -1, + goto error); + + isl_int_gcd(m, point->el[0], bmap->eq[j][1 + i]); + isl_int_divexact(m, bmap->eq[j][1 + i], m); + isl_int_abs(m, m); + isl_seq_scale(point->el, point->el, m, 1 + i); + isl_int_divexact(m, point->el[0], bmap->eq[j][1 + i]); + isl_int_neg(m, m); + isl_int_mul(point->el[1 + i], m, bmap->eq[j][0]); + } + + isl_int_clear(m); + return isl_point_alloc(isl_basic_map_get_space(bmap), point); +error: + isl_int_clear(m); + isl_vec_free(point); + return NULL; +} + +/* Return isl_bool_true if the singleton map "map1" is a subset of "map2", + * i.e., if the single element of "map1" is also an element of "map2". + * Assumes "map2" has known divs. + */ +static isl_bool map_is_singleton_subset(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + int i; + isl_bool is_subset = isl_bool_false; + struct isl_point *point; + + if (!map1 || !map2) + return isl_bool_error; + if (map1->n != 1) + isl_die(isl_map_get_ctx(map1), isl_error_invalid, + "expecting single-disjunct input", + return isl_bool_error); + + point = singleton_extract_point(map1->p[0]); + if (!point) + return isl_bool_error; + + for (i = 0; i < map2->n; ++i) { + is_subset = isl_basic_map_contains_point(map2->p[i], point); + if (is_subset) + break; + } + + isl_point_free(point); + return is_subset; +} + +static isl_bool map_is_subset(__isl_keep isl_map *map1, + __isl_keep isl_map *map2) +{ + isl_bool is_subset = isl_bool_false; + isl_bool empty; + int rat1, rat2; + + if (!map1 || !map2) + return isl_bool_error; + + if (!isl_map_has_equal_space(map1, map2)) + return isl_bool_false; + + empty = isl_map_is_empty(map1); + if (empty < 0) + return isl_bool_error; + if (empty) + return isl_bool_true; + + empty = isl_map_is_empty(map2); + if (empty < 0) + return isl_bool_error; + if (empty) + return isl_bool_false; + + rat1 = isl_map_has_rational(map1); + rat2 = isl_map_has_rational(map2); + if (rat1 < 0 || rat2 < 0) + return isl_bool_error; + if (rat1 && !rat2) + return isl_bool_false; + + if (isl_map_plain_is_universe(map2)) + return isl_bool_true; + + map2 = isl_map_compute_divs(isl_map_copy(map2)); + if (isl_map_plain_is_singleton(map1)) { + is_subset = map_is_singleton_subset(map1, map2); + isl_map_free(map2); + return is_subset; + } + is_subset = map_diff_is_empty(map1, map2); + isl_map_free(map2); + + return is_subset; +} + +isl_bool isl_map_is_subset(__isl_keep isl_map *map1, __isl_keep isl_map *map2) +{ + return isl_map_align_params_map_map_and_test(map1, map2, + &map_is_subset); +} + +isl_bool isl_set_is_subset(__isl_keep isl_set *set1, __isl_keep isl_set *set2) +{ + return isl_map_is_subset( + (struct isl_map *)set1, (struct isl_map *)set2); +} + +__isl_give isl_map *isl_map_make_disjoint(__isl_take isl_map *map) +{ + int i; + struct isl_subtract_diff_collector sdc; + sdc.dc.add = &basic_map_subtract_add; + + if (!map) + return NULL; + if (ISL_F_ISSET(map, ISL_MAP_DISJOINT)) + return map; + if (map->n <= 1) + return map; + + map = isl_map_compute_divs(map); + map = isl_map_remove_empty_parts(map); + + if (!map || map->n <= 1) + return map; + + sdc.diff = isl_map_from_basic_map(isl_basic_map_copy(map->p[0])); + + for (i = 1; i < map->n; ++i) { + struct isl_basic_map *bmap = isl_basic_map_copy(map->p[i]); + struct isl_map *copy = isl_map_copy(sdc.diff); + if (basic_map_collect_diff(bmap, copy, &sdc.dc) < 0) { + isl_map_free(sdc.diff); + sdc.diff = NULL; + break; + } + } + + isl_map_free(map); + + return sdc.diff; +} + +__isl_give isl_set *isl_set_make_disjoint(__isl_take isl_set *set) +{ + return (struct isl_set *)isl_map_make_disjoint((struct isl_map *)set); +} + +__isl_give isl_map *isl_map_complement(__isl_take isl_map *map) +{ + isl_map *universe; + + if (!map) + return NULL; + + universe = isl_map_universe(isl_map_get_space(map)); + + return isl_map_subtract(universe, map); +} + +__isl_give isl_set *isl_set_complement(__isl_take isl_set *set) +{ + return isl_map_complement(set); +} Index: lib/Analysis/isl/isl_map_to_basic_set.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_map_to_basic_set.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#define ISL_KEY isl_map +#define ISL_VAL isl_basic_set +#define ISL_HMAP_SUFFIX map_to_basic_set +#define ISL_HMAP isl_map_to_basic_set +#define ISL_KEY_IS_EQUAL isl_map_plain_is_equal +#define ISL_VAL_IS_EQUAL isl_basic_set_plain_is_equal +#define ISL_KEY_PRINT isl_printer_print_map +#define ISL_VAL_PRINT isl_printer_print_basic_set + +#include Index: lib/Analysis/isl/isl_mat.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_mat.c @@ -0,0 +1,1800 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +isl_ctx *isl_mat_get_ctx(__isl_keep isl_mat *mat) +{ + return mat ? mat->ctx : NULL; +} + +/* Return a hash value that digests "mat". + */ +uint32_t isl_mat_get_hash(__isl_keep isl_mat *mat) +{ + int i; + uint32_t hash; + + if (!mat) + return 0; + + hash = isl_hash_init(); + isl_hash_byte(hash, mat->n_row & 0xFF); + isl_hash_byte(hash, mat->n_col & 0xFF); + for (i = 0; i < mat->n_row; ++i) { + uint32_t row_hash; + + row_hash = isl_seq_get_hash(mat->row[i], mat->n_col); + isl_hash_hash(hash, row_hash); + } + + return hash; +} + +struct isl_mat *isl_mat_alloc(struct isl_ctx *ctx, + unsigned n_row, unsigned n_col) +{ + int i; + struct isl_mat *mat; + + mat = isl_alloc_type(ctx, struct isl_mat); + if (!mat) + return NULL; + + mat->row = NULL; + mat->block = isl_blk_alloc(ctx, n_row * n_col); + if (isl_blk_is_error(mat->block)) + goto error; + mat->row = isl_alloc_array(ctx, isl_int *, n_row); + if (n_row && !mat->row) + goto error; + + for (i = 0; i < n_row; ++i) + mat->row[i] = mat->block.data + i * n_col; + + mat->ctx = ctx; + isl_ctx_ref(ctx); + mat->ref = 1; + mat->n_row = n_row; + mat->n_col = n_col; + mat->max_col = n_col; + mat->flags = 0; + + return mat; +error: + isl_blk_free(ctx, mat->block); + free(mat); + return NULL; +} + +struct isl_mat *isl_mat_extend(struct isl_mat *mat, + unsigned n_row, unsigned n_col) +{ + int i; + isl_int *old; + isl_int **row; + + if (!mat) + return NULL; + + if (mat->max_col >= n_col && mat->n_row >= n_row) { + if (mat->n_col < n_col) + mat->n_col = n_col; + return mat; + } + + if (mat->max_col < n_col) { + struct isl_mat *new_mat; + + if (n_row < mat->n_row) + n_row = mat->n_row; + new_mat = isl_mat_alloc(mat->ctx, n_row, n_col); + if (!new_mat) + goto error; + for (i = 0; i < mat->n_row; ++i) + isl_seq_cpy(new_mat->row[i], mat->row[i], mat->n_col); + isl_mat_free(mat); + return new_mat; + } + + mat = isl_mat_cow(mat); + if (!mat) + goto error; + + old = mat->block.data; + mat->block = isl_blk_extend(mat->ctx, mat->block, n_row * mat->max_col); + if (isl_blk_is_error(mat->block)) + goto error; + row = isl_realloc_array(mat->ctx, mat->row, isl_int *, n_row); + if (n_row && !row) + goto error; + mat->row = row; + + for (i = 0; i < mat->n_row; ++i) + mat->row[i] = mat->block.data + (mat->row[i] - old); + for (i = mat->n_row; i < n_row; ++i) + mat->row[i] = mat->block.data + i * mat->max_col; + mat->n_row = n_row; + if (mat->n_col < n_col) + mat->n_col = n_col; + + return mat; +error: + isl_mat_free(mat); + return NULL; +} + +__isl_give isl_mat *isl_mat_sub_alloc6(isl_ctx *ctx, isl_int **row, + unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col) +{ + int i; + struct isl_mat *mat; + + mat = isl_alloc_type(ctx, struct isl_mat); + if (!mat) + return NULL; + mat->row = isl_alloc_array(ctx, isl_int *, n_row); + if (n_row && !mat->row) + goto error; + for (i = 0; i < n_row; ++i) + mat->row[i] = row[first_row+i] + first_col; + mat->ctx = ctx; + isl_ctx_ref(ctx); + mat->ref = 1; + mat->n_row = n_row; + mat->n_col = n_col; + mat->block = isl_blk_empty(); + mat->flags = ISL_MAT_BORROWED; + return mat; +error: + free(mat); + return NULL; +} + +__isl_give isl_mat *isl_mat_sub_alloc(__isl_keep isl_mat *mat, + unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col) +{ + if (!mat) + return NULL; + return isl_mat_sub_alloc6(mat->ctx, mat->row, first_row, n_row, + first_col, n_col); +} + +void isl_mat_sub_copy(struct isl_ctx *ctx, isl_int **dst, isl_int **src, + unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col) +{ + int i; + + for (i = 0; i < n_row; ++i) + isl_seq_cpy(dst[i]+dst_col, src[i]+src_col, n_col); +} + +void isl_mat_sub_neg(struct isl_ctx *ctx, isl_int **dst, isl_int **src, + unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col) +{ + int i; + + for (i = 0; i < n_row; ++i) + isl_seq_neg(dst[i]+dst_col, src[i]+src_col, n_col); +} + +struct isl_mat *isl_mat_copy(struct isl_mat *mat) +{ + if (!mat) + return NULL; + + mat->ref++; + return mat; +} + +struct isl_mat *isl_mat_dup(struct isl_mat *mat) +{ + int i; + struct isl_mat *mat2; + + if (!mat) + return NULL; + mat2 = isl_mat_alloc(mat->ctx, mat->n_row, mat->n_col); + if (!mat2) + return NULL; + for (i = 0; i < mat->n_row; ++i) + isl_seq_cpy(mat2->row[i], mat->row[i], mat->n_col); + return mat2; +} + +struct isl_mat *isl_mat_cow(struct isl_mat *mat) +{ + struct isl_mat *mat2; + if (!mat) + return NULL; + + if (mat->ref == 1 && !ISL_F_ISSET(mat, ISL_MAT_BORROWED)) + return mat; + + mat2 = isl_mat_dup(mat); + isl_mat_free(mat); + return mat2; +} + +__isl_null isl_mat *isl_mat_free(__isl_take isl_mat *mat) +{ + if (!mat) + return NULL; + + if (--mat->ref > 0) + return NULL; + + if (!ISL_F_ISSET(mat, ISL_MAT_BORROWED)) + isl_blk_free(mat->ctx, mat->block); + isl_ctx_deref(mat->ctx); + free(mat->row); + free(mat); + + return NULL; +} + +int isl_mat_rows(__isl_keep isl_mat *mat) +{ + return mat ? mat->n_row : -1; +} + +int isl_mat_cols(__isl_keep isl_mat *mat) +{ + return mat ? mat->n_col : -1; +} + +int isl_mat_get_element(__isl_keep isl_mat *mat, int row, int col, isl_int *v) +{ + if (!mat) + return -1; + if (row < 0 || row >= mat->n_row) + isl_die(mat->ctx, isl_error_invalid, "row out of range", + return -1); + if (col < 0 || col >= mat->n_col) + isl_die(mat->ctx, isl_error_invalid, "column out of range", + return -1); + isl_int_set(*v, mat->row[row][col]); + return 0; +} + +/* Extract the element at row "row", oolumn "col" of "mat". + */ +__isl_give isl_val *isl_mat_get_element_val(__isl_keep isl_mat *mat, + int row, int col) +{ + isl_ctx *ctx; + + if (!mat) + return NULL; + ctx = isl_mat_get_ctx(mat); + if (row < 0 || row >= mat->n_row) + isl_die(ctx, isl_error_invalid, "row out of range", + return NULL); + if (col < 0 || col >= mat->n_col) + isl_die(ctx, isl_error_invalid, "column out of range", + return NULL); + return isl_val_int_from_isl_int(ctx, mat->row[row][col]); +} + +__isl_give isl_mat *isl_mat_set_element(__isl_take isl_mat *mat, + int row, int col, isl_int v) +{ + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + if (row < 0 || row >= mat->n_row) + isl_die(mat->ctx, isl_error_invalid, "row out of range", + goto error); + if (col < 0 || col >= mat->n_col) + isl_die(mat->ctx, isl_error_invalid, "column out of range", + goto error); + isl_int_set(mat->row[row][col], v); + return mat; +error: + isl_mat_free(mat); + return NULL; +} + +__isl_give isl_mat *isl_mat_set_element_si(__isl_take isl_mat *mat, + int row, int col, int v) +{ + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + if (row < 0 || row >= mat->n_row) + isl_die(mat->ctx, isl_error_invalid, "row out of range", + goto error); + if (col < 0 || col >= mat->n_col) + isl_die(mat->ctx, isl_error_invalid, "column out of range", + goto error); + isl_int_set_si(mat->row[row][col], v); + return mat; +error: + isl_mat_free(mat); + return NULL; +} + +/* Replace the element at row "row", column "col" of "mat" by "v". + */ +__isl_give isl_mat *isl_mat_set_element_val(__isl_take isl_mat *mat, + int row, int col, __isl_take isl_val *v) +{ + if (!v) + return isl_mat_free(mat); + if (!isl_val_is_int(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting integer value", goto error); + mat = isl_mat_set_element(mat, row, col, v->n); + isl_val_free(v); + return mat; +error: + isl_val_free(v); + return isl_mat_free(mat); +} + +__isl_give isl_mat *isl_mat_diag(isl_ctx *ctx, unsigned n_row, isl_int d) +{ + int i; + struct isl_mat *mat; + + mat = isl_mat_alloc(ctx, n_row, n_row); + if (!mat) + return NULL; + for (i = 0; i < n_row; ++i) { + isl_seq_clr(mat->row[i], i); + isl_int_set(mat->row[i][i], d); + isl_seq_clr(mat->row[i]+i+1, n_row-(i+1)); + } + + return mat; +} + +__isl_give isl_mat *isl_mat_identity(isl_ctx *ctx, unsigned n_row) +{ + if (!ctx) + return NULL; + return isl_mat_diag(ctx, n_row, ctx->one); +} + +/* Is "mat" a (possibly scaled) identity matrix? + */ +int isl_mat_is_scaled_identity(__isl_keep isl_mat *mat) +{ + int i; + + if (!mat) + return -1; + if (mat->n_row != mat->n_col) + return 0; + + for (i = 0; i < mat->n_row; ++i) { + if (isl_seq_first_non_zero(mat->row[i], i) != -1) + return 0; + if (isl_int_ne(mat->row[0][0], mat->row[i][i])) + return 0; + if (isl_seq_first_non_zero(mat->row[i] + i + 1, + mat->n_col - (i + 1)) != -1) + return 0; + } + + return 1; +} + +struct isl_vec *isl_mat_vec_product(struct isl_mat *mat, struct isl_vec *vec) +{ + int i; + struct isl_vec *prod; + + if (!mat || !vec) + goto error; + + isl_assert(mat->ctx, mat->n_col == vec->size, goto error); + + prod = isl_vec_alloc(mat->ctx, mat->n_row); + if (!prod) + goto error; + + for (i = 0; i < prod->size; ++i) + isl_seq_inner_product(mat->row[i], vec->el, vec->size, + &prod->block.data[i]); + isl_mat_free(mat); + isl_vec_free(vec); + return prod; +error: + isl_mat_free(mat); + isl_vec_free(vec); + return NULL; +} + +__isl_give isl_vec *isl_mat_vec_inverse_product(__isl_take isl_mat *mat, + __isl_take isl_vec *vec) +{ + struct isl_mat *vec_mat; + int i; + + if (!mat || !vec) + goto error; + vec_mat = isl_mat_alloc(vec->ctx, vec->size, 1); + if (!vec_mat) + goto error; + for (i = 0; i < vec->size; ++i) + isl_int_set(vec_mat->row[i][0], vec->el[i]); + vec_mat = isl_mat_inverse_product(mat, vec_mat); + isl_vec_free(vec); + if (!vec_mat) + return NULL; + vec = isl_vec_alloc(vec_mat->ctx, vec_mat->n_row); + if (vec) + for (i = 0; i < vec->size; ++i) + isl_int_set(vec->el[i], vec_mat->row[i][0]); + isl_mat_free(vec_mat); + return vec; +error: + isl_mat_free(mat); + isl_vec_free(vec); + return NULL; +} + +struct isl_vec *isl_vec_mat_product(struct isl_vec *vec, struct isl_mat *mat) +{ + int i, j; + struct isl_vec *prod; + + if (!mat || !vec) + goto error; + + isl_assert(mat->ctx, mat->n_row == vec->size, goto error); + + prod = isl_vec_alloc(mat->ctx, mat->n_col); + if (!prod) + goto error; + + for (i = 0; i < prod->size; ++i) { + isl_int_set_si(prod->el[i], 0); + for (j = 0; j < vec->size; ++j) + isl_int_addmul(prod->el[i], vec->el[j], mat->row[j][i]); + } + isl_mat_free(mat); + isl_vec_free(vec); + return prod; +error: + isl_mat_free(mat); + isl_vec_free(vec); + return NULL; +} + +struct isl_mat *isl_mat_aff_direct_sum(struct isl_mat *left, + struct isl_mat *right) +{ + int i; + struct isl_mat *sum; + + if (!left || !right) + goto error; + + isl_assert(left->ctx, left->n_row == right->n_row, goto error); + isl_assert(left->ctx, left->n_row >= 1, goto error); + isl_assert(left->ctx, left->n_col >= 1, goto error); + isl_assert(left->ctx, right->n_col >= 1, goto error); + isl_assert(left->ctx, + isl_seq_first_non_zero(left->row[0]+1, left->n_col-1) == -1, + goto error); + isl_assert(left->ctx, + isl_seq_first_non_zero(right->row[0]+1, right->n_col-1) == -1, + goto error); + + sum = isl_mat_alloc(left->ctx, left->n_row, left->n_col + right->n_col - 1); + if (!sum) + goto error; + isl_int_lcm(sum->row[0][0], left->row[0][0], right->row[0][0]); + isl_int_divexact(left->row[0][0], sum->row[0][0], left->row[0][0]); + isl_int_divexact(right->row[0][0], sum->row[0][0], right->row[0][0]); + + isl_seq_clr(sum->row[0]+1, sum->n_col-1); + for (i = 1; i < sum->n_row; ++i) { + isl_int_mul(sum->row[i][0], left->row[0][0], left->row[i][0]); + isl_int_addmul(sum->row[i][0], + right->row[0][0], right->row[i][0]); + isl_seq_scale(sum->row[i]+1, left->row[i]+1, left->row[0][0], + left->n_col-1); + isl_seq_scale(sum->row[i]+left->n_col, + right->row[i]+1, right->row[0][0], + right->n_col-1); + } + + isl_int_divexact(left->row[0][0], sum->row[0][0], left->row[0][0]); + isl_int_divexact(right->row[0][0], sum->row[0][0], right->row[0][0]); + isl_mat_free(left); + isl_mat_free(right); + return sum; +error: + isl_mat_free(left); + isl_mat_free(right); + return NULL; +} + +static void exchange(struct isl_mat *M, struct isl_mat **U, + struct isl_mat **Q, unsigned row, unsigned i, unsigned j) +{ + int r; + for (r = row; r < M->n_row; ++r) + isl_int_swap(M->row[r][i], M->row[r][j]); + if (U) { + for (r = 0; r < (*U)->n_row; ++r) + isl_int_swap((*U)->row[r][i], (*U)->row[r][j]); + } + if (Q) + isl_mat_swap_rows(*Q, i, j); +} + +static void subtract(struct isl_mat *M, struct isl_mat **U, + struct isl_mat **Q, unsigned row, unsigned i, unsigned j, isl_int m) +{ + int r; + for (r = row; r < M->n_row; ++r) + isl_int_submul(M->row[r][j], m, M->row[r][i]); + if (U) { + for (r = 0; r < (*U)->n_row; ++r) + isl_int_submul((*U)->row[r][j], m, (*U)->row[r][i]); + } + if (Q) { + for (r = 0; r < (*Q)->n_col; ++r) + isl_int_addmul((*Q)->row[i][r], m, (*Q)->row[j][r]); + } +} + +static void oppose(struct isl_mat *M, struct isl_mat **U, + struct isl_mat **Q, unsigned row, unsigned col) +{ + int r; + for (r = row; r < M->n_row; ++r) + isl_int_neg(M->row[r][col], M->row[r][col]); + if (U) { + for (r = 0; r < (*U)->n_row; ++r) + isl_int_neg((*U)->row[r][col], (*U)->row[r][col]); + } + if (Q) + isl_seq_neg((*Q)->row[col], (*Q)->row[col], (*Q)->n_col); +} + +/* Given matrix M, compute + * + * M U = H + * M = H Q + * + * with U and Q unimodular matrices and H a matrix in column echelon form + * such that on each echelon row the entries in the non-echelon column + * are non-negative (if neg == 0) or non-positive (if neg == 1) + * and strictly smaller (in absolute value) than the entries in the echelon + * column. + * If U or Q are NULL, then these matrices are not computed. + */ +struct isl_mat *isl_mat_left_hermite(struct isl_mat *M, int neg, + struct isl_mat **U, struct isl_mat **Q) +{ + isl_int c; + int row, col; + + if (U) + *U = NULL; + if (Q) + *Q = NULL; + if (!M) + goto error; + M = isl_mat_cow(M); + if (!M) + goto error; + if (U) { + *U = isl_mat_identity(M->ctx, M->n_col); + if (!*U) + goto error; + } + if (Q) { + *Q = isl_mat_identity(M->ctx, M->n_col); + if (!*Q) + goto error; + } + + col = 0; + isl_int_init(c); + for (row = 0; row < M->n_row; ++row) { + int first, i, off; + first = isl_seq_abs_min_non_zero(M->row[row]+col, M->n_col-col); + if (first == -1) + continue; + first += col; + if (first != col) + exchange(M, U, Q, row, first, col); + if (isl_int_is_neg(M->row[row][col])) + oppose(M, U, Q, row, col); + first = col+1; + while ((off = isl_seq_first_non_zero(M->row[row]+first, + M->n_col-first)) != -1) { + first += off; + isl_int_fdiv_q(c, M->row[row][first], M->row[row][col]); + subtract(M, U, Q, row, col, first, c); + if (!isl_int_is_zero(M->row[row][first])) + exchange(M, U, Q, row, first, col); + else + ++first; + } + for (i = 0; i < col; ++i) { + if (isl_int_is_zero(M->row[row][i])) + continue; + if (neg) + isl_int_cdiv_q(c, M->row[row][i], M->row[row][col]); + else + isl_int_fdiv_q(c, M->row[row][i], M->row[row][col]); + if (isl_int_is_zero(c)) + continue; + subtract(M, U, Q, row, col, i, c); + } + ++col; + } + isl_int_clear(c); + + return M; +error: + if (Q) { + isl_mat_free(*Q); + *Q = NULL; + } + if (U) { + isl_mat_free(*U); + *U = NULL; + } + isl_mat_free(M); + return NULL; +} + +struct isl_mat *isl_mat_right_kernel(struct isl_mat *mat) +{ + int i, rank; + struct isl_mat *U = NULL; + struct isl_mat *K; + + mat = isl_mat_left_hermite(mat, 0, &U, NULL); + if (!mat || !U) + goto error; + + for (i = 0, rank = 0; rank < mat->n_col; ++rank) { + while (i < mat->n_row && isl_int_is_zero(mat->row[i][rank])) + ++i; + if (i >= mat->n_row) + break; + } + K = isl_mat_alloc(U->ctx, U->n_row, U->n_col - rank); + if (!K) + goto error; + isl_mat_sub_copy(K->ctx, K->row, U->row, U->n_row, 0, rank, U->n_col-rank); + isl_mat_free(mat); + isl_mat_free(U); + return K; +error: + isl_mat_free(mat); + isl_mat_free(U); + return NULL; +} + +struct isl_mat *isl_mat_lin_to_aff(struct isl_mat *mat) +{ + int i; + struct isl_mat *mat2; + + if (!mat) + return NULL; + mat2 = isl_mat_alloc(mat->ctx, 1+mat->n_row, 1+mat->n_col); + if (!mat2) + goto error; + isl_int_set_si(mat2->row[0][0], 1); + isl_seq_clr(mat2->row[0]+1, mat->n_col); + for (i = 0; i < mat->n_row; ++i) { + isl_int_set_si(mat2->row[1+i][0], 0); + isl_seq_cpy(mat2->row[1+i]+1, mat->row[i], mat->n_col); + } + isl_mat_free(mat); + return mat2; +error: + isl_mat_free(mat); + return NULL; +} + +/* Given two matrices M1 and M2, return the block matrix + * + * [ M1 0 ] + * [ 0 M2 ] + */ +__isl_give isl_mat *isl_mat_diagonal(__isl_take isl_mat *mat1, + __isl_take isl_mat *mat2) +{ + int i; + isl_mat *mat; + + if (!mat1 || !mat2) + goto error; + + mat = isl_mat_alloc(mat1->ctx, mat1->n_row + mat2->n_row, + mat1->n_col + mat2->n_col); + if (!mat) + goto error; + for (i = 0; i < mat1->n_row; ++i) { + isl_seq_cpy(mat->row[i], mat1->row[i], mat1->n_col); + isl_seq_clr(mat->row[i] + mat1->n_col, mat2->n_col); + } + for (i = 0; i < mat2->n_row; ++i) { + isl_seq_clr(mat->row[mat1->n_row + i], mat1->n_col); + isl_seq_cpy(mat->row[mat1->n_row + i] + mat1->n_col, + mat2->row[i], mat2->n_col); + } + isl_mat_free(mat1); + isl_mat_free(mat2); + return mat; +error: + isl_mat_free(mat1); + isl_mat_free(mat2); + return NULL; +} + +static int row_first_non_zero(isl_int **row, unsigned n_row, unsigned col) +{ + int i; + + for (i = 0; i < n_row; ++i) + if (!isl_int_is_zero(row[i][col])) + return i; + return -1; +} + +static int row_abs_min_non_zero(isl_int **row, unsigned n_row, unsigned col) +{ + int i, min = row_first_non_zero(row, n_row, col); + if (min < 0) + return -1; + for (i = min + 1; i < n_row; ++i) { + if (isl_int_is_zero(row[i][col])) + continue; + if (isl_int_abs_lt(row[i][col], row[min][col])) + min = i; + } + return min; +} + +static void inv_exchange(struct isl_mat *left, struct isl_mat *right, + unsigned i, unsigned j) +{ + left = isl_mat_swap_rows(left, i, j); + right = isl_mat_swap_rows(right, i, j); +} + +static void inv_oppose( + struct isl_mat *left, struct isl_mat *right, unsigned row) +{ + isl_seq_neg(left->row[row]+row, left->row[row]+row, left->n_col-row); + isl_seq_neg(right->row[row], right->row[row], right->n_col); +} + +static void inv_subtract(struct isl_mat *left, struct isl_mat *right, + unsigned row, unsigned i, isl_int m) +{ + isl_int_neg(m, m); + isl_seq_combine(left->row[i]+row, + left->ctx->one, left->row[i]+row, + m, left->row[row]+row, + left->n_col-row); + isl_seq_combine(right->row[i], right->ctx->one, right->row[i], + m, right->row[row], right->n_col); +} + +/* Compute inv(left)*right + */ +struct isl_mat *isl_mat_inverse_product(struct isl_mat *left, + struct isl_mat *right) +{ + int row; + isl_int a, b; + + if (!left || !right) + goto error; + + isl_assert(left->ctx, left->n_row == left->n_col, goto error); + isl_assert(left->ctx, left->n_row == right->n_row, goto error); + + if (left->n_row == 0) { + isl_mat_free(left); + return right; + } + + left = isl_mat_cow(left); + right = isl_mat_cow(right); + if (!left || !right) + goto error; + + isl_int_init(a); + isl_int_init(b); + for (row = 0; row < left->n_row; ++row) { + int pivot, first, i, off; + pivot = row_abs_min_non_zero(left->row+row, left->n_row-row, row); + if (pivot < 0) { + isl_int_clear(a); + isl_int_clear(b); + isl_assert(left->ctx, pivot >= 0, goto error); + } + pivot += row; + if (pivot != row) + inv_exchange(left, right, pivot, row); + if (isl_int_is_neg(left->row[row][row])) + inv_oppose(left, right, row); + first = row+1; + while ((off = row_first_non_zero(left->row+first, + left->n_row-first, row)) != -1) { + first += off; + isl_int_fdiv_q(a, left->row[first][row], + left->row[row][row]); + inv_subtract(left, right, row, first, a); + if (!isl_int_is_zero(left->row[first][row])) + inv_exchange(left, right, row, first); + else + ++first; + } + for (i = 0; i < row; ++i) { + if (isl_int_is_zero(left->row[i][row])) + continue; + isl_int_gcd(a, left->row[row][row], left->row[i][row]); + isl_int_divexact(b, left->row[i][row], a); + isl_int_divexact(a, left->row[row][row], a); + isl_int_neg(b, b); + isl_seq_combine(left->row[i] + i, + a, left->row[i] + i, + b, left->row[row] + i, + left->n_col - i); + isl_seq_combine(right->row[i], a, right->row[i], + b, right->row[row], right->n_col); + } + } + isl_int_clear(b); + + isl_int_set(a, left->row[0][0]); + for (row = 1; row < left->n_row; ++row) + isl_int_lcm(a, a, left->row[row][row]); + if (isl_int_is_zero(a)){ + isl_int_clear(a); + isl_assert(left->ctx, 0, goto error); + } + for (row = 0; row < left->n_row; ++row) { + isl_int_divexact(left->row[row][row], a, left->row[row][row]); + if (isl_int_is_one(left->row[row][row])) + continue; + isl_seq_scale(right->row[row], right->row[row], + left->row[row][row], right->n_col); + } + isl_int_clear(a); + + isl_mat_free(left); + return right; +error: + isl_mat_free(left); + isl_mat_free(right); + return NULL; +} + +void isl_mat_col_scale(struct isl_mat *mat, unsigned col, isl_int m) +{ + int i; + + for (i = 0; i < mat->n_row; ++i) + isl_int_mul(mat->row[i][col], mat->row[i][col], m); +} + +void isl_mat_col_combine(struct isl_mat *mat, unsigned dst, + isl_int m1, unsigned src1, isl_int m2, unsigned src2) +{ + int i; + isl_int tmp; + + isl_int_init(tmp); + for (i = 0; i < mat->n_row; ++i) { + isl_int_mul(tmp, m1, mat->row[i][src1]); + isl_int_addmul(tmp, m2, mat->row[i][src2]); + isl_int_set(mat->row[i][dst], tmp); + } + isl_int_clear(tmp); +} + +struct isl_mat *isl_mat_right_inverse(struct isl_mat *mat) +{ + struct isl_mat *inv; + int row; + isl_int a, b; + + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + + inv = isl_mat_identity(mat->ctx, mat->n_col); + inv = isl_mat_cow(inv); + if (!inv) + goto error; + + isl_int_init(a); + isl_int_init(b); + for (row = 0; row < mat->n_row; ++row) { + int pivot, first, i, off; + pivot = isl_seq_abs_min_non_zero(mat->row[row]+row, mat->n_col-row); + if (pivot < 0) { + isl_int_clear(a); + isl_int_clear(b); + isl_assert(mat->ctx, pivot >= 0, goto error); + } + pivot += row; + if (pivot != row) + exchange(mat, &inv, NULL, row, pivot, row); + if (isl_int_is_neg(mat->row[row][row])) + oppose(mat, &inv, NULL, row, row); + first = row+1; + while ((off = isl_seq_first_non_zero(mat->row[row]+first, + mat->n_col-first)) != -1) { + first += off; + isl_int_fdiv_q(a, mat->row[row][first], + mat->row[row][row]); + subtract(mat, &inv, NULL, row, row, first, a); + if (!isl_int_is_zero(mat->row[row][first])) + exchange(mat, &inv, NULL, row, row, first); + else + ++first; + } + for (i = 0; i < row; ++i) { + if (isl_int_is_zero(mat->row[row][i])) + continue; + isl_int_gcd(a, mat->row[row][row], mat->row[row][i]); + isl_int_divexact(b, mat->row[row][i], a); + isl_int_divexact(a, mat->row[row][row], a); + isl_int_neg(a, a); + isl_mat_col_combine(mat, i, a, i, b, row); + isl_mat_col_combine(inv, i, a, i, b, row); + } + } + isl_int_clear(b); + + isl_int_set(a, mat->row[0][0]); + for (row = 1; row < mat->n_row; ++row) + isl_int_lcm(a, a, mat->row[row][row]); + if (isl_int_is_zero(a)){ + isl_int_clear(a); + goto error; + } + for (row = 0; row < mat->n_row; ++row) { + isl_int_divexact(mat->row[row][row], a, mat->row[row][row]); + if (isl_int_is_one(mat->row[row][row])) + continue; + isl_mat_col_scale(inv, row, mat->row[row][row]); + } + isl_int_clear(a); + + isl_mat_free(mat); + + return inv; +error: + isl_mat_free(mat); + isl_mat_free(inv); + return NULL; +} + +struct isl_mat *isl_mat_transpose(struct isl_mat *mat) +{ + struct isl_mat *transpose = NULL; + int i, j; + + if (!mat) + return NULL; + + if (mat->n_col == mat->n_row) { + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + for (i = 0; i < mat->n_row; ++i) + for (j = i + 1; j < mat->n_col; ++j) + isl_int_swap(mat->row[i][j], mat->row[j][i]); + return mat; + } + transpose = isl_mat_alloc(mat->ctx, mat->n_col, mat->n_row); + if (!transpose) + goto error; + for (i = 0; i < mat->n_row; ++i) + for (j = 0; j < mat->n_col; ++j) + isl_int_set(transpose->row[j][i], mat->row[i][j]); + isl_mat_free(mat); + return transpose; +error: + isl_mat_free(mat); + return NULL; +} + +struct isl_mat *isl_mat_swap_cols(struct isl_mat *mat, unsigned i, unsigned j) +{ + int r; + + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + isl_assert(mat->ctx, i < mat->n_col, goto error); + isl_assert(mat->ctx, j < mat->n_col, goto error); + + for (r = 0; r < mat->n_row; ++r) + isl_int_swap(mat->row[r][i], mat->row[r][j]); + return mat; +error: + isl_mat_free(mat); + return NULL; +} + +struct isl_mat *isl_mat_swap_rows(struct isl_mat *mat, unsigned i, unsigned j) +{ + isl_int *t; + + if (!mat) + return NULL; + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + t = mat->row[i]; + mat->row[i] = mat->row[j]; + mat->row[j] = t; + return mat; +} + +/* Calculate the product of two matrices. + * + * This function is optimized for operand matrices that contain many zeros and + * skips multiplications where we know one of the operands is zero. + */ +__isl_give isl_mat *isl_mat_product(__isl_take isl_mat *left, + __isl_take isl_mat *right) +{ + int i, j, k; + struct isl_mat *prod; + + if (!left || !right) + goto error; + isl_assert(left->ctx, left->n_col == right->n_row, goto error); + prod = isl_mat_alloc(left->ctx, left->n_row, right->n_col); + if (!prod) + goto error; + if (left->n_col == 0) { + for (i = 0; i < prod->n_row; ++i) + isl_seq_clr(prod->row[i], prod->n_col); + isl_mat_free(left); + isl_mat_free(right); + return prod; + } + for (i = 0; i < prod->n_row; ++i) { + for (j = 0; j < prod->n_col; ++j) + isl_int_mul(prod->row[i][j], + left->row[i][0], right->row[0][j]); + for (k = 1; k < left->n_col; ++k) { + if (isl_int_is_zero(left->row[i][k])) + continue; + for (j = 0; j < prod->n_col; ++j) + isl_int_addmul(prod->row[i][j], + left->row[i][k], right->row[k][j]); + } + } + isl_mat_free(left); + isl_mat_free(right); + return prod; +error: + isl_mat_free(left); + isl_mat_free(right); + return NULL; +} + +/* Replace the variables x in the rows q by x' given by x = M x', + * with M the matrix mat. + * + * If the number of new variables is greater than the original + * number of variables, then the rows q have already been + * preextended. If the new number is smaller, then the coefficients + * of the divs, which are not changed, need to be shifted down. + * The row q may be the equalities, the inequalities or the + * div expressions. In the latter case, has_div is true and + * we need to take into account the extra denominator column. + */ +static int preimage(struct isl_ctx *ctx, isl_int **q, unsigned n, + unsigned n_div, int has_div, struct isl_mat *mat) +{ + int i; + struct isl_mat *t; + int e; + + if (mat->n_col >= mat->n_row) + e = 0; + else + e = mat->n_row - mat->n_col; + if (has_div) + for (i = 0; i < n; ++i) + isl_int_mul(q[i][0], q[i][0], mat->row[0][0]); + t = isl_mat_sub_alloc6(mat->ctx, q, 0, n, has_div, mat->n_row); + t = isl_mat_product(t, mat); + if (!t) + return -1; + for (i = 0; i < n; ++i) { + isl_seq_swp_or_cpy(q[i] + has_div, t->row[i], t->n_col); + isl_seq_cpy(q[i] + has_div + t->n_col, + q[i] + has_div + t->n_col + e, n_div); + isl_seq_clr(q[i] + has_div + t->n_col + n_div, e); + } + isl_mat_free(t); + return 0; +} + +/* Replace the variables x in bset by x' given by x = M x', with + * M the matrix mat. + * + * If there are fewer variables x' then there are x, then we perform + * the transformation in place, which means that, in principle, + * this frees up some extra variables as the number + * of columns remains constant, but we would have to extend + * the div array too as the number of rows in this array is assumed + * to be equal to extra. + */ +struct isl_basic_set *isl_basic_set_preimage(struct isl_basic_set *bset, + struct isl_mat *mat) +{ + struct isl_ctx *ctx; + + if (!bset || !mat) + goto error; + + ctx = bset->ctx; + bset = isl_basic_set_cow(bset); + if (!bset) + goto error; + + isl_assert(ctx, bset->dim->nparam == 0, goto error); + isl_assert(ctx, 1+bset->dim->n_out == mat->n_row, goto error); + isl_assert(ctx, mat->n_col > 0, goto error); + + if (mat->n_col > mat->n_row) { + bset = isl_basic_set_extend(bset, 0, mat->n_col-1, 0, 0, 0); + if (!bset) + goto error; + } else if (mat->n_col < mat->n_row) { + bset->dim = isl_space_cow(bset->dim); + if (!bset->dim) + goto error; + bset->dim->n_out -= mat->n_row - mat->n_col; + } + + if (preimage(ctx, bset->eq, bset->n_eq, bset->n_div, 0, + isl_mat_copy(mat)) < 0) + goto error; + + if (preimage(ctx, bset->ineq, bset->n_ineq, bset->n_div, 0, + isl_mat_copy(mat)) < 0) + goto error; + + if (preimage(ctx, bset->div, bset->n_div, bset->n_div, 1, mat) < 0) + goto error2; + + ISL_F_CLR(bset, ISL_BASIC_SET_NO_IMPLICIT); + ISL_F_CLR(bset, ISL_BASIC_SET_NO_REDUNDANT); + ISL_F_CLR(bset, ISL_BASIC_SET_NORMALIZED); + ISL_F_CLR(bset, ISL_BASIC_SET_NORMALIZED_DIVS); + ISL_F_CLR(bset, ISL_BASIC_SET_ALL_EQUALITIES); + + bset = isl_basic_set_simplify(bset); + bset = isl_basic_set_finalize(bset); + + return bset; +error: + isl_mat_free(mat); +error2: + isl_basic_set_free(bset); + return NULL; +} + +struct isl_set *isl_set_preimage(struct isl_set *set, struct isl_mat *mat) +{ + int i; + + set = isl_set_cow(set); + if (!set) + goto error; + + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_basic_set_preimage(set->p[i], + isl_mat_copy(mat)); + if (!set->p[i]) + goto error; + } + if (mat->n_col != mat->n_row) { + set->dim = isl_space_cow(set->dim); + if (!set->dim) + goto error; + set->dim->n_out += mat->n_col; + set->dim->n_out -= mat->n_row; + } + isl_mat_free(mat); + ISL_F_CLR(set, ISL_SET_NORMALIZED); + return set; +error: + isl_set_free(set); + isl_mat_free(mat); + return NULL; +} + +/* Replace the variables x starting at pos in the rows q + * by x' with x = M x' with M the matrix mat. + * That is, replace the corresponding coefficients c by c M. + */ +static int transform(isl_ctx *ctx, isl_int **q, unsigned n, + unsigned pos, __isl_take isl_mat *mat) +{ + int i; + isl_mat *t; + + t = isl_mat_sub_alloc6(ctx, q, 0, n, pos, mat->n_row); + t = isl_mat_product(t, mat); + if (!t) + return -1; + for (i = 0; i < n; ++i) + isl_seq_swp_or_cpy(q[i] + pos, t->row[i], t->n_col); + isl_mat_free(t); + return 0; +} + +/* Replace the variables x of type "type" starting at "first" in "bmap" + * by x' with x = M x' with M the matrix trans. + * That is, replace the corresponding coefficients c by c M. + * + * The transformation matrix should be a square matrix. + */ +__isl_give isl_basic_map *isl_basic_map_transform_dims( + __isl_take isl_basic_map *bmap, enum isl_dim_type type, unsigned first, + __isl_take isl_mat *trans) +{ + isl_ctx *ctx; + unsigned pos; + + bmap = isl_basic_map_cow(bmap); + if (!bmap || !trans) + goto error; + + ctx = isl_basic_map_get_ctx(bmap); + if (trans->n_row != trans->n_col) + isl_die(trans->ctx, isl_error_invalid, + "expecting square transformation matrix", goto error); + if (first + trans->n_row > isl_basic_map_dim(bmap, type)) + isl_die(trans->ctx, isl_error_invalid, + "oversized transformation matrix", goto error); + + pos = isl_basic_map_offset(bmap, type) + first; + + if (transform(ctx, bmap->eq, bmap->n_eq, pos, isl_mat_copy(trans)) < 0) + goto error; + if (transform(ctx, bmap->ineq, bmap->n_ineq, pos, + isl_mat_copy(trans)) < 0) + goto error; + if (transform(ctx, bmap->div, bmap->n_div, 1 + pos, + isl_mat_copy(trans)) < 0) + goto error; + + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED); + ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS); + + isl_mat_free(trans); + return bmap; +error: + isl_mat_free(trans); + isl_basic_map_free(bmap); + return NULL; +} + +/* Replace the variables x of type "type" starting at "first" in "bset" + * by x' with x = M x' with M the matrix trans. + * That is, replace the corresponding coefficients c by c M. + * + * The transformation matrix should be a square matrix. + */ +__isl_give isl_basic_set *isl_basic_set_transform_dims( + __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned first, + __isl_take isl_mat *trans) +{ + return isl_basic_map_transform_dims(bset, type, first, trans); +} + +void isl_mat_print_internal(__isl_keep isl_mat *mat, FILE *out, int indent) +{ + int i, j; + + if (!mat) { + fprintf(out, "%*snull mat\n", indent, ""); + return; + } + + if (mat->n_row == 0) + fprintf(out, "%*s[]\n", indent, ""); + + for (i = 0; i < mat->n_row; ++i) { + if (!i) + fprintf(out, "%*s[[", indent, ""); + else + fprintf(out, "%*s[", indent+1, ""); + for (j = 0; j < mat->n_col; ++j) { + if (j) + fprintf(out, ","); + isl_int_print(out, mat->row[i][j], 0); + } + if (i == mat->n_row-1) + fprintf(out, "]]\n"); + else + fprintf(out, "]\n"); + } +} + +void isl_mat_dump(__isl_keep isl_mat *mat) +{ + isl_mat_print_internal(mat, stderr, 0); +} + +struct isl_mat *isl_mat_drop_cols(struct isl_mat *mat, unsigned col, unsigned n) +{ + int r; + + if (n == 0) + return mat; + + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + + if (col != mat->n_col-n) { + for (r = 0; r < mat->n_row; ++r) + isl_seq_cpy(mat->row[r]+col, mat->row[r]+col+n, + mat->n_col - col - n); + } + mat->n_col -= n; + return mat; +} + +struct isl_mat *isl_mat_drop_rows(struct isl_mat *mat, unsigned row, unsigned n) +{ + int r; + + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + + for (r = row; r+n < mat->n_row; ++r) + mat->row[r] = mat->row[r+n]; + + mat->n_row -= n; + return mat; +} + +__isl_give isl_mat *isl_mat_insert_cols(__isl_take isl_mat *mat, + unsigned col, unsigned n) +{ + isl_mat *ext; + + if (!mat) + return NULL; + if (n == 0) + return mat; + + ext = isl_mat_alloc(mat->ctx, mat->n_row, mat->n_col + n); + if (!ext) + goto error; + + isl_mat_sub_copy(mat->ctx, ext->row, mat->row, mat->n_row, 0, 0, col); + isl_mat_sub_copy(mat->ctx, ext->row, mat->row, mat->n_row, + col + n, col, mat->n_col - col); + + isl_mat_free(mat); + return ext; +error: + isl_mat_free(mat); + return NULL; +} + +__isl_give isl_mat *isl_mat_insert_zero_cols(__isl_take isl_mat *mat, + unsigned first, unsigned n) +{ + int i; + + if (!mat) + return NULL; + mat = isl_mat_insert_cols(mat, first, n); + if (!mat) + return NULL; + + for (i = 0; i < mat->n_row; ++i) + isl_seq_clr(mat->row[i] + first, n); + + return mat; +} + +__isl_give isl_mat *isl_mat_add_zero_cols(__isl_take isl_mat *mat, unsigned n) +{ + if (!mat) + return NULL; + + return isl_mat_insert_zero_cols(mat, mat->n_col, n); +} + +__isl_give isl_mat *isl_mat_insert_rows(__isl_take isl_mat *mat, + unsigned row, unsigned n) +{ + isl_mat *ext; + + if (!mat) + return NULL; + if (n == 0) + return mat; + + ext = isl_mat_alloc(mat->ctx, mat->n_row + n, mat->n_col); + if (!ext) + goto error; + + isl_mat_sub_copy(mat->ctx, ext->row, mat->row, row, 0, 0, mat->n_col); + isl_mat_sub_copy(mat->ctx, ext->row + row + n, mat->row + row, + mat->n_row - row, 0, 0, mat->n_col); + + isl_mat_free(mat); + return ext; +error: + isl_mat_free(mat); + return NULL; +} + +__isl_give isl_mat *isl_mat_add_rows(__isl_take isl_mat *mat, unsigned n) +{ + if (!mat) + return NULL; + + return isl_mat_insert_rows(mat, mat->n_row, n); +} + +__isl_give isl_mat *isl_mat_insert_zero_rows(__isl_take isl_mat *mat, + unsigned row, unsigned n) +{ + int i; + + mat = isl_mat_insert_rows(mat, row, n); + if (!mat) + return NULL; + + for (i = 0; i < n; ++i) + isl_seq_clr(mat->row[row + i], mat->n_col); + + return mat; +} + +__isl_give isl_mat *isl_mat_add_zero_rows(__isl_take isl_mat *mat, unsigned n) +{ + if (!mat) + return NULL; + + return isl_mat_insert_zero_rows(mat, mat->n_row, n); +} + +void isl_mat_col_submul(struct isl_mat *mat, + int dst_col, isl_int f, int src_col) +{ + int i; + + for (i = 0; i < mat->n_row; ++i) + isl_int_submul(mat->row[i][dst_col], f, mat->row[i][src_col]); +} + +void isl_mat_col_add(__isl_keep isl_mat *mat, int dst_col, int src_col) +{ + int i; + + if (!mat) + return; + + for (i = 0; i < mat->n_row; ++i) + isl_int_add(mat->row[i][dst_col], + mat->row[i][dst_col], mat->row[i][src_col]); +} + +void isl_mat_col_mul(struct isl_mat *mat, int dst_col, isl_int f, int src_col) +{ + int i; + + for (i = 0; i < mat->n_row; ++i) + isl_int_mul(mat->row[i][dst_col], f, mat->row[i][src_col]); +} + +struct isl_mat *isl_mat_unimodular_complete(struct isl_mat *M, int row) +{ + int r; + struct isl_mat *H = NULL, *Q = NULL; + + if (!M) + return NULL; + + isl_assert(M->ctx, M->n_row == M->n_col, goto error); + M->n_row = row; + H = isl_mat_left_hermite(isl_mat_copy(M), 0, NULL, &Q); + M->n_row = M->n_col; + if (!H) + goto error; + for (r = 0; r < row; ++r) + isl_assert(M->ctx, isl_int_is_one(H->row[r][r]), goto error); + for (r = row; r < M->n_row; ++r) + isl_seq_cpy(M->row[r], Q->row[r], M->n_col); + isl_mat_free(H); + isl_mat_free(Q); + return M; +error: + isl_mat_free(H); + isl_mat_free(Q); + isl_mat_free(M); + return NULL; +} + +__isl_give isl_mat *isl_mat_concat(__isl_take isl_mat *top, + __isl_take isl_mat *bot) +{ + struct isl_mat *mat; + + if (!top || !bot) + goto error; + + isl_assert(top->ctx, top->n_col == bot->n_col, goto error); + if (top->n_row == 0) { + isl_mat_free(top); + return bot; + } + if (bot->n_row == 0) { + isl_mat_free(bot); + return top; + } + + mat = isl_mat_alloc(top->ctx, top->n_row + bot->n_row, top->n_col); + if (!mat) + goto error; + isl_mat_sub_copy(mat->ctx, mat->row, top->row, top->n_row, + 0, 0, mat->n_col); + isl_mat_sub_copy(mat->ctx, mat->row + top->n_row, bot->row, bot->n_row, + 0, 0, mat->n_col); + isl_mat_free(top); + isl_mat_free(bot); + return mat; +error: + isl_mat_free(top); + isl_mat_free(bot); + return NULL; +} + +int isl_mat_is_equal(__isl_keep isl_mat *mat1, __isl_keep isl_mat *mat2) +{ + int i; + + if (!mat1 || !mat2) + return -1; + + if (mat1->n_row != mat2->n_row) + return 0; + + if (mat1->n_col != mat2->n_col) + return 0; + + for (i = 0; i < mat1->n_row; ++i) + if (!isl_seq_eq(mat1->row[i], mat2->row[i], mat1->n_col)) + return 0; + + return 1; +} + +__isl_give isl_mat *isl_mat_from_row_vec(__isl_take isl_vec *vec) +{ + struct isl_mat *mat; + + if (!vec) + return NULL; + mat = isl_mat_alloc(vec->ctx, 1, vec->size); + if (!mat) + goto error; + + isl_seq_cpy(mat->row[0], vec->el, vec->size); + + isl_vec_free(vec); + return mat; +error: + isl_vec_free(vec); + return NULL; +} + +/* Return a copy of row "row" of "mat" as an isl_vec. + */ +__isl_give isl_vec *isl_mat_get_row(__isl_keep isl_mat *mat, unsigned row) +{ + isl_vec *v; + + if (!mat) + return NULL; + if (row >= mat->n_row) + isl_die(mat->ctx, isl_error_invalid, "row out of range", + return NULL); + + v = isl_vec_alloc(isl_mat_get_ctx(mat), mat->n_col); + if (!v) + return NULL; + isl_seq_cpy(v->el, mat->row[row], mat->n_col); + + return v; +} + +__isl_give isl_mat *isl_mat_vec_concat(__isl_take isl_mat *top, + __isl_take isl_vec *bot) +{ + return isl_mat_concat(top, isl_mat_from_row_vec(bot)); +} + +__isl_give isl_mat *isl_mat_move_cols(__isl_take isl_mat *mat, + unsigned dst_col, unsigned src_col, unsigned n) +{ + isl_mat *res; + + if (!mat) + return NULL; + if (n == 0 || dst_col == src_col) + return mat; + + res = isl_mat_alloc(mat->ctx, mat->n_row, mat->n_col); + if (!res) + goto error; + + if (dst_col < src_col) { + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + 0, 0, dst_col); + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + dst_col, src_col, n); + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + dst_col + n, dst_col, src_col - dst_col); + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + src_col + n, src_col + n, + res->n_col - src_col - n); + } else { + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + 0, 0, src_col); + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + src_col, src_col + n, dst_col - src_col); + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + dst_col, src_col, n); + isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row, + dst_col + n, dst_col + n, + res->n_col - dst_col - n); + } + isl_mat_free(mat); + + return res; +error: + isl_mat_free(mat); + return NULL; +} + +/* Return the gcd of the elements in row "row" of "mat" in *gcd. + * Return isl_stat_ok on success and isl_stat_error on failure. + */ +isl_stat isl_mat_row_gcd(__isl_keep isl_mat *mat, int row, isl_int *gcd) +{ + if (!mat) + return isl_stat_error; + + if (row < 0 || row >= mat->n_row) + isl_die(isl_mat_get_ctx(mat), isl_error_invalid, + "row out of range", return isl_stat_error); + isl_seq_gcd(mat->row[row], mat->n_col, gcd); + + return isl_stat_ok; +} + +void isl_mat_gcd(__isl_keep isl_mat *mat, isl_int *gcd) +{ + int i; + isl_int g; + + isl_int_set_si(*gcd, 0); + if (!mat) + return; + + isl_int_init(g); + for (i = 0; i < mat->n_row; ++i) { + isl_seq_gcd(mat->row[i], mat->n_col, &g); + isl_int_gcd(*gcd, *gcd, g); + } + isl_int_clear(g); +} + +__isl_give isl_mat *isl_mat_scale_down(__isl_take isl_mat *mat, isl_int m) +{ + int i; + + if (isl_int_is_one(m)) + return mat; + + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + + for (i = 0; i < mat->n_row; ++i) + isl_seq_scale_down(mat->row[i], mat->row[i], m, mat->n_col); + + return mat; +} + +__isl_give isl_mat *isl_mat_scale_down_row(__isl_take isl_mat *mat, int row, + isl_int m) +{ + if (isl_int_is_one(m)) + return mat; + + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + + isl_seq_scale_down(mat->row[row], mat->row[row], m, mat->n_col); + + return mat; +} + +__isl_give isl_mat *isl_mat_normalize(__isl_take isl_mat *mat) +{ + isl_int gcd; + + if (!mat) + return NULL; + + isl_int_init(gcd); + isl_mat_gcd(mat, &gcd); + mat = isl_mat_scale_down(mat, gcd); + isl_int_clear(gcd); + + return mat; +} + +__isl_give isl_mat *isl_mat_normalize_row(__isl_take isl_mat *mat, int row) +{ + mat = isl_mat_cow(mat); + if (!mat) + return NULL; + + isl_seq_normalize(mat->ctx, mat->row[row], mat->n_col); + + return mat; +} + +/* Number of initial non-zero columns. + */ +int isl_mat_initial_non_zero_cols(__isl_keep isl_mat *mat) +{ + int i; + + if (!mat) + return -1; + + for (i = 0; i < mat->n_col; ++i) + if (row_first_non_zero(mat->row, mat->n_row, i) < 0) + break; + + return i; +} Index: lib/Analysis/isl/isl_mat_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_mat_private.h @@ -0,0 +1,50 @@ +#include +#include + +struct isl_mat { + int ref; + + struct isl_ctx *ctx; + +#define ISL_MAT_BORROWED (1 << 0) + unsigned flags; + + unsigned n_row; + unsigned n_col; + + isl_int **row; + + /* actual size of the rows in memory; n_col <= max_col */ + unsigned max_col; + + struct isl_blk block; +}; + +uint32_t isl_mat_get_hash(__isl_keep isl_mat *mat); + +__isl_give isl_mat *isl_mat_sub_alloc(__isl_keep isl_mat *mat, + unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col); +__isl_give isl_mat *isl_mat_sub_alloc6(isl_ctx *ctx, isl_int **row, + unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col); +void isl_mat_sub_copy(struct isl_ctx *ctx, isl_int **dst, isl_int **src, + unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col); +void isl_mat_sub_neg(struct isl_ctx *ctx, isl_int **dst, isl_int **src, + unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col); +__isl_give isl_mat *isl_mat_diag(isl_ctx *ctx, unsigned n_row, isl_int d); + +__isl_give isl_mat *isl_mat_scale_down_row(__isl_take isl_mat *mat, int row, + isl_int m); + +__isl_give isl_vec *isl_mat_get_row(__isl_keep isl_mat *mat, unsigned row); + +int isl_mat_is_scaled_identity(__isl_keep isl_mat *mat); + +isl_stat isl_mat_row_gcd(__isl_keep isl_mat *mat, int row, isl_int *gcd); + +void isl_mat_col_mul(struct isl_mat *mat, int dst_col, isl_int f, int src_col); +void isl_mat_col_submul(struct isl_mat *mat, + int dst_col, isl_int f, int src_col); + +int isl_mat_get_element(__isl_keep isl_mat *mat, int row, int col, isl_int *v); +__isl_give isl_mat *isl_mat_set_element(__isl_take isl_mat *mat, + int row, int col, isl_int v); Index: lib/Analysis/isl/isl_morph.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_morph.h @@ -0,0 +1,86 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#ifndef ISL_MORHP_H +#define ISL_MORHP_H + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* An isl_morph is a "morphism" on (basic) sets. + * "map" is an affine mapping from "dom" to "ran" + * and "inv" is the inverse mapping. + */ +struct isl_morph { + int ref; + + isl_basic_set *dom; + isl_basic_set *ran; + + isl_mat *map; + isl_mat *inv; +}; +typedef struct isl_morph isl_morph; + +isl_ctx *isl_morph_get_ctx(__isl_keep isl_morph *morph); + +__isl_give isl_morph *isl_morph_alloc( + __isl_take isl_basic_set *dom, __isl_take isl_basic_set *ran, + __isl_take isl_mat *map, __isl_take isl_mat *inv); +__isl_give isl_morph *isl_morph_copy(__isl_keep isl_morph *morph); +__isl_give isl_morph *isl_morph_identity(__isl_keep isl_basic_set *bset); +void isl_morph_free(__isl_take isl_morph *morph); + +__isl_give isl_space *isl_morph_get_dom_space(__isl_keep isl_morph *morph); +__isl_give isl_space *isl_morph_get_ran_space(__isl_keep isl_morph *morph); +__isl_give isl_multi_aff *isl_morph_get_var_multi_aff( + __isl_keep isl_morph *morph); +unsigned isl_morph_dom_dim(__isl_keep isl_morph *morph, enum isl_dim_type type); +unsigned isl_morph_ran_dim(__isl_keep isl_morph *morph, enum isl_dim_type type); + +__isl_give isl_morph *isl_morph_remove_dom_dims(__isl_take isl_morph *morph, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_morph *isl_morph_remove_ran_dims(__isl_take isl_morph *morph, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_morph *isl_morph_dom_params(__isl_take isl_morph *morph); +__isl_give isl_morph *isl_morph_ran_params(__isl_take isl_morph *morph); + +__isl_give isl_morph *isl_morph_compose(__isl_take isl_morph *morph1, + __isl_take isl_morph *morph2); +__isl_give isl_morph *isl_morph_inverse(__isl_take isl_morph *morph); + +void isl_morph_print_internal(__isl_take isl_morph *morph, FILE *out); +void isl_morph_dump(__isl_take isl_morph *morph); + +__isl_give isl_morph *isl_basic_set_variable_compression( + __isl_keep isl_basic_set *bset, enum isl_dim_type type); +__isl_give isl_morph *isl_basic_set_parameter_compression( + __isl_keep isl_basic_set *bset); +__isl_give isl_morph *isl_basic_set_full_compression( + __isl_keep isl_basic_set *bset); + +__isl_give isl_basic_set *isl_morph_basic_set(__isl_take isl_morph *morph, + __isl_take isl_basic_set *bset); +__isl_give isl_set *isl_morph_set(__isl_take isl_morph *morph, + __isl_take isl_set *set); +__isl_give isl_vec *isl_morph_vec(__isl_take isl_morph *morph, + __isl_take isl_vec *vec); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/isl_morph.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_morph.c @@ -0,0 +1,819 @@ +/* + * Copyright 2010-2011 INRIA Saclay + * Copyright 2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include + +isl_ctx *isl_morph_get_ctx(__isl_keep isl_morph *morph) +{ + if (!morph) + return NULL; + return isl_basic_set_get_ctx(morph->dom); +} + +__isl_give isl_morph *isl_morph_alloc( + __isl_take isl_basic_set *dom, __isl_take isl_basic_set *ran, + __isl_take isl_mat *map, __isl_take isl_mat *inv) +{ + isl_morph *morph; + + if (!dom || !ran || !map || !inv) + goto error; + + morph = isl_alloc_type(dom->ctx, struct isl_morph); + if (!morph) + goto error; + + morph->ref = 1; + morph->dom = dom; + morph->ran = ran; + morph->map = map; + morph->inv = inv; + + return morph; +error: + isl_basic_set_free(dom); + isl_basic_set_free(ran); + isl_mat_free(map); + isl_mat_free(inv); + return NULL; +} + +__isl_give isl_morph *isl_morph_copy(__isl_keep isl_morph *morph) +{ + if (!morph) + return NULL; + + morph->ref++; + return morph; +} + +__isl_give isl_morph *isl_morph_dup(__isl_keep isl_morph *morph) +{ + if (!morph) + return NULL; + + return isl_morph_alloc(isl_basic_set_copy(morph->dom), + isl_basic_set_copy(morph->ran), + isl_mat_copy(morph->map), isl_mat_copy(morph->inv)); +} + +__isl_give isl_morph *isl_morph_cow(__isl_take isl_morph *morph) +{ + if (!morph) + return NULL; + + if (morph->ref == 1) + return morph; + morph->ref--; + return isl_morph_dup(morph); +} + +void isl_morph_free(__isl_take isl_morph *morph) +{ + if (!morph) + return; + + if (--morph->ref > 0) + return; + + isl_basic_set_free(morph->dom); + isl_basic_set_free(morph->ran); + isl_mat_free(morph->map); + isl_mat_free(morph->inv); + free(morph); +} + +/* Is "morph" an identity on the parameters? + */ +static int identity_on_parameters(__isl_keep isl_morph *morph) +{ + int is_identity; + unsigned nparam; + isl_mat *sub; + + nparam = isl_morph_dom_dim(morph, isl_dim_param); + if (nparam != isl_morph_ran_dim(morph, isl_dim_param)) + return 0; + if (nparam == 0) + return 1; + sub = isl_mat_sub_alloc(morph->map, 0, 1 + nparam, 0, 1 + nparam); + is_identity = isl_mat_is_scaled_identity(sub); + isl_mat_free(sub); + + return is_identity; +} + +/* Return an affine expression of the variables of the range of "morph" + * in terms of the parameters and the variables of the domain on "morph". + * + * In order for the space manipulations to make sense, we require + * that the parameters are not modified by "morph". + */ +__isl_give isl_multi_aff *isl_morph_get_var_multi_aff( + __isl_keep isl_morph *morph) +{ + isl_space *dom, *ran, *space; + isl_local_space *ls; + isl_multi_aff *ma; + unsigned nparam, nvar; + int i; + int is_identity; + + if (!morph) + return NULL; + + is_identity = identity_on_parameters(morph); + if (is_identity < 0) + return NULL; + if (!is_identity) + isl_die(isl_morph_get_ctx(morph), isl_error_invalid, + "cannot handle parameter compression", return NULL); + + dom = isl_morph_get_dom_space(morph); + ls = isl_local_space_from_space(isl_space_copy(dom)); + ran = isl_morph_get_ran_space(morph); + space = isl_space_map_from_domain_and_range(dom, ran); + ma = isl_multi_aff_zero(space); + + nparam = isl_multi_aff_dim(ma, isl_dim_param); + nvar = isl_multi_aff_dim(ma, isl_dim_out); + for (i = 0; i < nvar; ++i) { + isl_val *val; + isl_vec *v; + isl_aff *aff; + + v = isl_mat_get_row(morph->map, 1 + nparam + i); + v = isl_vec_insert_els(v, 0, 1); + val = isl_mat_get_element_val(morph->map, 0, 0); + v = isl_vec_set_element_val(v, 0, val); + aff = isl_aff_alloc_vec(isl_local_space_copy(ls), v); + ma = isl_multi_aff_set_aff(ma, i, aff); + } + + isl_local_space_free(ls); + return ma; +} + +/* Return the domain space of "morph". + */ +__isl_give isl_space *isl_morph_get_dom_space(__isl_keep isl_morph *morph) +{ + if (!morph) + return NULL; + + return isl_basic_set_get_space(morph->dom); +} + +__isl_give isl_space *isl_morph_get_ran_space(__isl_keep isl_morph *morph) +{ + if (!morph) + return NULL; + + return isl_space_copy(morph->ran->dim); +} + +unsigned isl_morph_dom_dim(__isl_keep isl_morph *morph, enum isl_dim_type type) +{ + if (!morph) + return 0; + + return isl_basic_set_dim(morph->dom, type); +} + +unsigned isl_morph_ran_dim(__isl_keep isl_morph *morph, enum isl_dim_type type) +{ + if (!morph) + return 0; + + return isl_basic_set_dim(morph->ran, type); +} + +__isl_give isl_morph *isl_morph_remove_dom_dims(__isl_take isl_morph *morph, + enum isl_dim_type type, unsigned first, unsigned n) +{ + unsigned dom_offset; + + if (n == 0) + return morph; + + morph = isl_morph_cow(morph); + if (!morph) + return NULL; + + dom_offset = 1 + isl_space_offset(morph->dom->dim, type); + + morph->dom = isl_basic_set_remove_dims(morph->dom, type, first, n); + + morph->map = isl_mat_drop_cols(morph->map, dom_offset + first, n); + + morph->inv = isl_mat_drop_rows(morph->inv, dom_offset + first, n); + + if (morph->dom && morph->ran && morph->map && morph->inv) + return morph; + + isl_morph_free(morph); + return NULL; +} + +__isl_give isl_morph *isl_morph_remove_ran_dims(__isl_take isl_morph *morph, + enum isl_dim_type type, unsigned first, unsigned n) +{ + unsigned ran_offset; + + if (n == 0) + return morph; + + morph = isl_morph_cow(morph); + if (!morph) + return NULL; + + ran_offset = 1 + isl_space_offset(morph->ran->dim, type); + + morph->ran = isl_basic_set_remove_dims(morph->ran, type, first, n); + + morph->map = isl_mat_drop_rows(morph->map, ran_offset + first, n); + + morph->inv = isl_mat_drop_cols(morph->inv, ran_offset + first, n); + + if (morph->dom && morph->ran && morph->map && morph->inv) + return morph; + + isl_morph_free(morph); + return NULL; +} + +/* Project domain of morph onto its parameter domain. + */ +__isl_give isl_morph *isl_morph_dom_params(__isl_take isl_morph *morph) +{ + unsigned n; + + morph = isl_morph_cow(morph); + if (!morph) + return NULL; + n = isl_basic_set_dim(morph->dom, isl_dim_set); + morph = isl_morph_remove_dom_dims(morph, isl_dim_set, 0, n); + if (!morph) + return NULL; + morph->dom = isl_basic_set_params(morph->dom); + if (morph->dom) + return morph; + + isl_morph_free(morph); + return NULL; +} + +/* Project range of morph onto its parameter domain. + */ +__isl_give isl_morph *isl_morph_ran_params(__isl_take isl_morph *morph) +{ + unsigned n; + + morph = isl_morph_cow(morph); + if (!morph) + return NULL; + n = isl_basic_set_dim(morph->ran, isl_dim_set); + morph = isl_morph_remove_ran_dims(morph, isl_dim_set, 0, n); + if (!morph) + return NULL; + morph->ran = isl_basic_set_params(morph->ran); + if (morph->ran) + return morph; + + isl_morph_free(morph); + return NULL; +} + +void isl_morph_print_internal(__isl_take isl_morph *morph, FILE *out) +{ + if (!morph) + return; + + isl_basic_set_dump(morph->dom); + isl_basic_set_dump(morph->ran); + isl_mat_print_internal(morph->map, out, 4); + isl_mat_print_internal(morph->inv, out, 4); +} + +void isl_morph_dump(__isl_take isl_morph *morph) +{ + isl_morph_print_internal(morph, stderr); +} + +__isl_give isl_morph *isl_morph_identity(__isl_keep isl_basic_set *bset) +{ + isl_mat *id; + isl_basic_set *universe; + unsigned total; + + if (!bset) + return NULL; + + total = isl_basic_set_total_dim(bset); + id = isl_mat_identity(bset->ctx, 1 + total); + universe = isl_basic_set_universe(isl_space_copy(bset->dim)); + + return isl_morph_alloc(universe, isl_basic_set_copy(universe), + id, isl_mat_copy(id)); +} + +/* Create a(n identity) morphism between empty sets of the same dimension + * a "bset". + */ +__isl_give isl_morph *isl_morph_empty(__isl_keep isl_basic_set *bset) +{ + isl_mat *id; + isl_basic_set *empty; + unsigned total; + + if (!bset) + return NULL; + + total = isl_basic_set_total_dim(bset); + id = isl_mat_identity(bset->ctx, 1 + total); + empty = isl_basic_set_empty(isl_space_copy(bset->dim)); + + return isl_morph_alloc(empty, isl_basic_set_copy(empty), + id, isl_mat_copy(id)); +} + +/* Construct a basic set described by the "n" equalities of "bset" starting + * at "first". + */ +static __isl_give isl_basic_set *copy_equalities(__isl_keep isl_basic_set *bset, + unsigned first, unsigned n) +{ + int i, k; + isl_basic_set *eq; + unsigned total; + + isl_assert(bset->ctx, bset->n_div == 0, return NULL); + + total = isl_basic_set_total_dim(bset); + eq = isl_basic_set_alloc_space(isl_space_copy(bset->dim), 0, n, 0); + if (!eq) + return NULL; + for (i = 0; i < n; ++i) { + k = isl_basic_set_alloc_equality(eq); + if (k < 0) + goto error; + isl_seq_cpy(eq->eq[k], bset->eq[first + i], 1 + total); + } + + return eq; +error: + isl_basic_set_free(eq); + return NULL; +} + +/* Given a basic set, exploit the equalties in the basic set to construct + * a morphishm that maps the basic set to a lower-dimensional space. + * Specifically, the morphism reduces the number of dimensions of type "type". + * + * We first select the equalities of interest, that is those that involve + * variables of type "type" and no later variables. + * Denote those equalities as + * + * -C(p) + M x = 0 + * + * where C(p) depends on the parameters if type == isl_dim_set and + * is a constant if type == isl_dim_param. + * + * Use isl_mat_final_variable_compression to construct a compression + * + * x = T x' + * + * x' = Q x + * + * If T is a zero-column matrix, then the set of equality constraints + * do not admit a solution. In this case, an empty morphism is returned. + * + * Both matrices are extended to map the full original space to the full + * compressed space. + */ +__isl_give isl_morph *isl_basic_set_variable_compression( + __isl_keep isl_basic_set *bset, enum isl_dim_type type) +{ + unsigned otype; + unsigned ntype; + unsigned orest; + unsigned nrest; + int f_eq, n_eq; + isl_space *dim; + isl_mat *E, *Q, *C; + isl_basic_set *dom, *ran; + + if (!bset) + return NULL; + + if (isl_basic_set_plain_is_empty(bset)) + return isl_morph_empty(bset); + + isl_assert(bset->ctx, bset->n_div == 0, return NULL); + + otype = 1 + isl_space_offset(bset->dim, type); + ntype = isl_basic_set_dim(bset, type); + orest = otype + ntype; + nrest = isl_basic_set_total_dim(bset) - (orest - 1); + + for (f_eq = 0; f_eq < bset->n_eq; ++f_eq) + if (isl_seq_first_non_zero(bset->eq[f_eq] + orest, nrest) == -1) + break; + for (n_eq = 0; f_eq + n_eq < bset->n_eq; ++n_eq) + if (isl_seq_first_non_zero(bset->eq[f_eq + n_eq] + otype, ntype) == -1) + break; + if (n_eq == 0) + return isl_morph_identity(bset); + + E = isl_mat_sub_alloc6(bset->ctx, bset->eq, f_eq, n_eq, 0, orest); + C = isl_mat_final_variable_compression(E, otype - 1, &Q); + if (!Q) + C = isl_mat_free(C); + if (C && C->n_col == 0) { + isl_mat_free(C); + isl_mat_free(Q); + return isl_morph_empty(bset); + } + + Q = isl_mat_diagonal(Q, isl_mat_identity(bset->ctx, nrest)); + C = isl_mat_diagonal(C, isl_mat_identity(bset->ctx, nrest)); + + dim = isl_space_copy(bset->dim); + dim = isl_space_drop_dims(dim, type, 0, ntype); + dim = isl_space_add_dims(dim, type, ntype - n_eq); + ran = isl_basic_set_universe(dim); + dom = copy_equalities(bset, f_eq, n_eq); + + return isl_morph_alloc(dom, ran, Q, C); +} + +/* Construct a parameter compression for "bset". + * We basically just call isl_mat_parameter_compression with the right input + * and then extend the resulting matrix to include the variables. + * + * The implementation assumes that "bset" does not have any equalities + * that only involve the parameters and that isl_basic_set_gauss has + * been applied to "bset". + * + * Let the equalities be given as + * + * B(p) + A x = 0. + * + * We use isl_mat_parameter_compression_ext to compute the compression + * + * p = T p'. + */ +__isl_give isl_morph *isl_basic_set_parameter_compression( + __isl_keep isl_basic_set *bset) +{ + unsigned nparam; + unsigned nvar; + unsigned n_div; + int n_eq; + isl_mat *H, *B; + isl_mat *map, *inv; + isl_basic_set *dom, *ran; + + if (!bset) + return NULL; + + if (isl_basic_set_plain_is_empty(bset)) + return isl_morph_empty(bset); + if (bset->n_eq == 0) + return isl_morph_identity(bset); + + n_eq = bset->n_eq; + nparam = isl_basic_set_dim(bset, isl_dim_param); + nvar = isl_basic_set_dim(bset, isl_dim_set); + n_div = isl_basic_set_dim(bset, isl_dim_div); + + if (isl_seq_first_non_zero(bset->eq[bset->n_eq - 1] + 1 + nparam, + nvar + n_div) == -1) + isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid, + "input not allowed to have parameter equalities", + return NULL); + if (n_eq > nvar + n_div) + isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid, + "input not gaussed", return NULL); + + B = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, n_eq, 0, 1 + nparam); + H = isl_mat_sub_alloc6(bset->ctx, bset->eq, + 0, n_eq, 1 + nparam, nvar + n_div); + inv = isl_mat_parameter_compression_ext(B, H); + inv = isl_mat_diagonal(inv, isl_mat_identity(bset->ctx, nvar)); + map = isl_mat_right_inverse(isl_mat_copy(inv)); + + dom = isl_basic_set_universe(isl_space_copy(bset->dim)); + ran = isl_basic_set_universe(isl_space_copy(bset->dim)); + + return isl_morph_alloc(dom, ran, map, inv); +} + +/* Add stride constraints to "bset" based on the inverse mapping + * that was plugged in. In particular, if morph maps x' to x, + * the the constraints of the original input + * + * A x' + b >= 0 + * + * have been rewritten to + * + * A inv x + b >= 0 + * + * However, this substitution may loose information on the integrality of x', + * so we need to impose that + * + * inv x + * + * is integral. If inv = B/d, this means that we need to impose that + * + * B x = 0 mod d + * + * or + * + * exists alpha in Z^m: B x = d alpha + * + * This function is similar to add_strides in isl_affine_hull.c + */ +static __isl_give isl_basic_set *add_strides(__isl_take isl_basic_set *bset, + __isl_keep isl_morph *morph) +{ + int i, div, k; + isl_int gcd; + + if (isl_int_is_one(morph->inv->row[0][0])) + return bset; + + isl_int_init(gcd); + + for (i = 0; 1 + i < morph->inv->n_row; ++i) { + isl_seq_gcd(morph->inv->row[1 + i], morph->inv->n_col, &gcd); + if (isl_int_is_divisible_by(gcd, morph->inv->row[0][0])) + continue; + div = isl_basic_set_alloc_div(bset); + if (div < 0) + goto error; + isl_int_set_si(bset->div[div][0], 0); + k = isl_basic_set_alloc_equality(bset); + if (k < 0) + goto error; + isl_seq_cpy(bset->eq[k], morph->inv->row[1 + i], + morph->inv->n_col); + isl_seq_clr(bset->eq[k] + morph->inv->n_col, bset->n_div); + isl_int_set(bset->eq[k][morph->inv->n_col + div], + morph->inv->row[0][0]); + } + + isl_int_clear(gcd); + + return bset; +error: + isl_int_clear(gcd); + isl_basic_set_free(bset); + return NULL; +} + +/* Apply the morphism to the basic set. + * We basically just compute the preimage of "bset" under the inverse mapping + * in morph, add in stride constraints and intersect with the range + * of the morphism. + */ +__isl_give isl_basic_set *isl_morph_basic_set(__isl_take isl_morph *morph, + __isl_take isl_basic_set *bset) +{ + isl_basic_set *res = NULL; + isl_mat *mat = NULL; + int i, k; + int max_stride; + + if (!morph || !bset) + goto error; + + isl_assert(bset->ctx, isl_space_is_equal(bset->dim, morph->dom->dim), + goto error); + + max_stride = morph->inv->n_row - 1; + if (isl_int_is_one(morph->inv->row[0][0])) + max_stride = 0; + res = isl_basic_set_alloc_space(isl_space_copy(morph->ran->dim), + bset->n_div + max_stride, bset->n_eq + max_stride, bset->n_ineq); + + for (i = 0; i < bset->n_div; ++i) + if (isl_basic_set_alloc_div(res) < 0) + goto error; + + mat = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, bset->n_eq, + 0, morph->inv->n_row); + mat = isl_mat_product(mat, isl_mat_copy(morph->inv)); + if (!mat) + goto error; + for (i = 0; i < bset->n_eq; ++i) { + k = isl_basic_set_alloc_equality(res); + if (k < 0) + goto error; + isl_seq_cpy(res->eq[k], mat->row[i], mat->n_col); + isl_seq_scale(res->eq[k] + mat->n_col, bset->eq[i] + mat->n_col, + morph->inv->row[0][0], bset->n_div); + } + isl_mat_free(mat); + + mat = isl_mat_sub_alloc6(bset->ctx, bset->ineq, 0, bset->n_ineq, + 0, morph->inv->n_row); + mat = isl_mat_product(mat, isl_mat_copy(morph->inv)); + if (!mat) + goto error; + for (i = 0; i < bset->n_ineq; ++i) { + k = isl_basic_set_alloc_inequality(res); + if (k < 0) + goto error; + isl_seq_cpy(res->ineq[k], mat->row[i], mat->n_col); + isl_seq_scale(res->ineq[k] + mat->n_col, + bset->ineq[i] + mat->n_col, + morph->inv->row[0][0], bset->n_div); + } + isl_mat_free(mat); + + mat = isl_mat_sub_alloc6(bset->ctx, bset->div, 0, bset->n_div, + 1, morph->inv->n_row); + mat = isl_mat_product(mat, isl_mat_copy(morph->inv)); + if (!mat) + goto error; + for (i = 0; i < bset->n_div; ++i) { + isl_int_mul(res->div[i][0], + morph->inv->row[0][0], bset->div[i][0]); + isl_seq_cpy(res->div[i] + 1, mat->row[i], mat->n_col); + isl_seq_scale(res->div[i] + 1 + mat->n_col, + bset->div[i] + 1 + mat->n_col, + morph->inv->row[0][0], bset->n_div); + } + isl_mat_free(mat); + + res = add_strides(res, morph); + + if (isl_basic_set_is_rational(bset)) + res = isl_basic_set_set_rational(res); + + res = isl_basic_set_simplify(res); + res = isl_basic_set_finalize(res); + + res = isl_basic_set_intersect(res, isl_basic_set_copy(morph->ran)); + + isl_morph_free(morph); + isl_basic_set_free(bset); + return res; +error: + isl_mat_free(mat); + isl_morph_free(morph); + isl_basic_set_free(bset); + isl_basic_set_free(res); + return NULL; +} + +/* Apply the morphism to the set. + */ +__isl_give isl_set *isl_morph_set(__isl_take isl_morph *morph, + __isl_take isl_set *set) +{ + int i; + + if (!morph || !set) + goto error; + + isl_assert(set->ctx, isl_space_is_equal(set->dim, morph->dom->dim), goto error); + + set = isl_set_cow(set); + if (!set) + goto error; + + isl_space_free(set->dim); + set->dim = isl_space_copy(morph->ran->dim); + if (!set->dim) + goto error; + + for (i = 0; i < set->n; ++i) { + set->p[i] = isl_morph_basic_set(isl_morph_copy(morph), set->p[i]); + if (!set->p[i]) + goto error; + } + + isl_morph_free(morph); + + ISL_F_CLR(set, ISL_SET_NORMALIZED); + + return set; +error: + isl_set_free(set); + isl_morph_free(morph); + return NULL; +} + +/* Construct a morphism that first does morph2 and then morph1. + */ +__isl_give isl_morph *isl_morph_compose(__isl_take isl_morph *morph1, + __isl_take isl_morph *morph2) +{ + isl_mat *map, *inv; + isl_basic_set *dom, *ran; + + if (!morph1 || !morph2) + goto error; + + map = isl_mat_product(isl_mat_copy(morph1->map), isl_mat_copy(morph2->map)); + inv = isl_mat_product(isl_mat_copy(morph2->inv), isl_mat_copy(morph1->inv)); + dom = isl_morph_basic_set(isl_morph_inverse(isl_morph_copy(morph2)), + isl_basic_set_copy(morph1->dom)); + dom = isl_basic_set_intersect(dom, isl_basic_set_copy(morph2->dom)); + ran = isl_morph_basic_set(isl_morph_copy(morph1), + isl_basic_set_copy(morph2->ran)); + ran = isl_basic_set_intersect(ran, isl_basic_set_copy(morph1->ran)); + + isl_morph_free(morph1); + isl_morph_free(morph2); + + return isl_morph_alloc(dom, ran, map, inv); +error: + isl_morph_free(morph1); + isl_morph_free(morph2); + return NULL; +} + +__isl_give isl_morph *isl_morph_inverse(__isl_take isl_morph *morph) +{ + isl_basic_set *bset; + isl_mat *mat; + + morph = isl_morph_cow(morph); + if (!morph) + return NULL; + + bset = morph->dom; + morph->dom = morph->ran; + morph->ran = bset; + + mat = morph->map; + morph->map = morph->inv; + morph->inv = mat; + + return morph; +} + +/* We detect all the equalities first to avoid implicit equalties + * being discovered during the computations. In particular, + * the compression on the variables could expose additional stride + * constraints on the parameters. This would result in existentially + * quantified variables after applying the resulting morph, which + * in turn could break invariants of the calling functions. + */ +__isl_give isl_morph *isl_basic_set_full_compression( + __isl_keep isl_basic_set *bset) +{ + isl_morph *morph, *morph2; + + bset = isl_basic_set_copy(bset); + bset = isl_basic_set_detect_equalities(bset); + + morph = isl_basic_set_variable_compression(bset, isl_dim_param); + bset = isl_morph_basic_set(isl_morph_copy(morph), bset); + + morph2 = isl_basic_set_parameter_compression(bset); + bset = isl_morph_basic_set(isl_morph_copy(morph2), bset); + + morph = isl_morph_compose(morph2, morph); + + morph2 = isl_basic_set_variable_compression(bset, isl_dim_set); + isl_basic_set_free(bset); + + morph = isl_morph_compose(morph2, morph); + + return morph; +} + +__isl_give isl_vec *isl_morph_vec(__isl_take isl_morph *morph, + __isl_take isl_vec *vec) +{ + if (!morph) + goto error; + + vec = isl_mat_vec_product(isl_mat_copy(morph->map), vec); + + isl_morph_free(morph); + return vec; +error: + isl_morph_free(morph); + isl_vec_free(vec); + return NULL; +} Index: lib/Analysis/isl/isl_multi_apply_set.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_apply_set.c @@ -0,0 +1,7 @@ +#define APPLY_DOMBASE set +#define APPLY_DOM isl_set + +#include + +#undef APPLY_DOMBASE +#undef APPLY_DOM Index: lib/Analysis/isl/isl_multi_apply_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_apply_templ.c @@ -0,0 +1,81 @@ +/* + * Copyright 2011 Sven Verdoolaege + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include + +/* Transform the elements of "multi" by applying "fn" to them + * with extra argument "set". + * + * The parameters of "multi" and "set" are assumed to have been aligned. + */ +__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),apply_aligned),APPLY_DOMBASE)( + __isl_take MULTI(BASE) *multi, __isl_take APPLY_DOM *set, + __isl_give EL *(*fn)(EL *el, __isl_take APPLY_DOM *set)) +{ + int i; + + if (!multi || !set) + goto error; + + if (multi->n == 0) { + FN(APPLY_DOM,free)(set); + return multi; + } + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + goto error; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = fn(multi->p[i], FN(APPLY_DOM,copy)(set)); + if (!multi->p[i]) + goto error; + } + + FN(APPLY_DOM,free)(set); + return multi; +error: + FN(APPLY_DOM,free)(set); + FN(MULTI(BASE),free)(multi); + return NULL; +} + +/* Transform the elements of "multi" by applying "fn" to them + * with extra argument "set". + * + * Align the parameters if needed and call apply_set_aligned. + */ +static __isl_give MULTI(BASE) *FN(FN(MULTI(BASE),apply),APPLY_DOMBASE)( + __isl_take MULTI(BASE) *multi, __isl_take APPLY_DOM *set, + __isl_give EL *(*fn)(EL *el, __isl_take APPLY_DOM *set)) +{ + isl_ctx *ctx; + + if (!multi || !set) + goto error; + + if (isl_space_match(multi->space, isl_dim_param, + set->dim, isl_dim_param)) + return FN(FN(MULTI(BASE),apply_aligned),APPLY_DOMBASE)(multi, + set, fn); + ctx = FN(MULTI(BASE),get_ctx)(multi); + if (!isl_space_has_named_params(multi->space) || + !isl_space_has_named_params(set->dim)) + isl_die(ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + multi = FN(MULTI(BASE),align_params)(multi, + FN(APPLY_DOM,get_space)(set)); + set = FN(APPLY_DOM,align_params)(set, FN(MULTI(BASE),get_space)(multi)); + return FN(FN(MULTI(BASE),apply_aligned),APPLY_DOMBASE)(multi, set, fn); +error: + FN(MULTI(BASE),free)(multi); + FN(APPLY_DOM,free)(set); + return NULL; +} Index: lib/Analysis/isl/isl_multi_apply_union_set.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_apply_union_set.c @@ -0,0 +1,7 @@ +#define APPLY_DOMBASE union_set +#define APPLY_DOM isl_union_set + +#include + +#undef APPLY_DOMBASE +#undef APPLY_DOM Index: lib/Analysis/isl/isl_multi_cmp.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_cmp.c @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege + */ + +#include + +/* Compare two multi expressions. + * + * Return -1 if "multi1" is "smaller" than "multi2", 1 if "multi1" is "greater" + * than "multi2" and 0 if they are equal. + */ +int FN(MULTI(BASE),plain_cmp)(__isl_keep MULTI(BASE) *multi1, + __isl_keep MULTI(BASE) *multi2) +{ + int i; + int cmp; + + if (multi1 == multi2) + return 0; + if (!multi1) + return -1; + if (!multi2) + return 1; + + cmp = isl_space_cmp(multi1->space, multi2->space); + if (cmp != 0) + return cmp; + + for (i = 0; i < multi1->n; ++i) { + cmp = FN(EL,plain_cmp)(multi1->p[i], multi2->p[i]); + if (cmp != 0) + return cmp; + } + + return 0; +} Index: lib/Analysis/isl/isl_multi_coalesce.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_coalesce.c @@ -0,0 +1,35 @@ +/* + * Copyright 2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include + +/* Coalesce the elements of "multi". + * + * Note that such coalescing does not change the meaning of "multi" + * so there is no need to cow. We do need to be careful not to + * destroy any other copies of "multi" in case of failure. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),coalesce)(__isl_take MULTI(BASE) *multi) +{ + int i; + + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + EL *el = FN(EL,copy)(multi->p[i]); + el = FN(EL,coalesce)(el); + if (!el) + return FN(MULTI(BASE),free)(multi); + FN(EL,free)(multi->p[i]); + multi->p[i] = el; + } + + return multi; +} Index: lib/Analysis/isl/isl_multi_floor.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_floor.c @@ -0,0 +1,29 @@ +/* + * Copyright 2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include + +/* Given f, return floor(f). + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),floor)(__isl_take MULTI(BASE) *multi) +{ + int i; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,floor)(multi->p[i]); + if (!multi->p[i]) + return FN(MULTI(BASE),free)(multi); + } + + return multi; +} Index: lib/Analysis/isl/isl_multi_gist.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_gist.c @@ -0,0 +1,29 @@ +/* + * Copyright 2011 Sven Verdoolaege + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include + +/* Compute the gist of "multi" with respect to the domain constraints + * of "context". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),gist)(__isl_take MULTI(BASE) *multi, + __isl_take DOM *context) +{ + return FN(FN(MULTI(BASE),apply),DOMBASE)(multi, context, &FN(EL,gist)); +} + +/* Compute the gist of "multi" with respect to the parameter constraints + * of "context". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),gist_params)( + __isl_take MULTI(BASE) *multi, __isl_take isl_set *context) +{ + return FN(MULTI(BASE),apply_set)(multi, context, &FN(EL,gist_params)); +} Index: lib/Analysis/isl/isl_multi_hash.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_hash.c @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege + */ + +#include +#include + +/* Return a hash value that digests "multi". + */ +uint32_t FN(MULTI(BASE),get_hash)(__isl_keep MULTI(BASE) *multi) +{ + int i; + uint32_t hash; + + if (!multi) + return 0; + + hash = isl_hash_init(); + for (i = 0; i < multi->n; ++i) { + uint32_t el_hash; + el_hash = FN(EL,get_hash)(multi->p[i]); + isl_hash_hash(hash, el_hash); + } + + return hash; +} Index: lib/Analysis/isl/isl_multi_intersect.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_intersect.c @@ -0,0 +1,29 @@ +/* + * Copyright 2011 Sven Verdoolaege + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include + +/* Intersect the domain of "multi" with "domain". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_domain)( + __isl_take MULTI(BASE) *multi, __isl_take DOM *domain) +{ + return FN(FN(MULTI(BASE),apply),DOMBASE)(multi, domain, + &FN(EL,intersect_domain)); +} + +/* Intersect the parameter domain of "multi" with "domain". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_params)( + __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain) +{ + return FN(MULTI(BASE),apply_set)(multi, domain, + &FN(EL,intersect_params)); +} Index: lib/Analysis/isl/isl_multi_macro.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_macro.h @@ -0,0 +1,10 @@ +#define xCAT(A,B) A ## B +#define CAT(A,B) xCAT(A,B) +#undef EL +#define EL CAT(isl_,BASE) +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) +#define xMULTI(BASE) isl_multi_ ## BASE +#define MULTI(BASE) xMULTI(BASE) +#undef DOM +#define DOM CAT(isl_,DOMBASE) Index: lib/Analysis/isl/isl_multi_templ.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_templ.h @@ -0,0 +1,13 @@ +#include + +#include + +struct MULTI(BASE) { + int ref; + isl_space *space; + + int n; + EL *p[1]; +}; + +__isl_give MULTI(BASE) *CAT(MULTI(BASE),_alloc)(__isl_take isl_space *space); Index: lib/Analysis/isl/isl_multi_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_multi_templ.c @@ -0,0 +1,1488 @@ +/* + * Copyright 2011 Sven Verdoolaege + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include + +#include + +#define MULTI_NAME(BASE) "isl_multi_" #BASE +#define xLIST(EL) EL ## _list +#define LIST(EL) xLIST(EL) + +isl_ctx *FN(MULTI(BASE),get_ctx)(__isl_keep MULTI(BASE) *multi) +{ + return multi ? isl_space_get_ctx(multi->space) : NULL; +} + +__isl_give isl_space *FN(MULTI(BASE),get_space)(__isl_keep MULTI(BASE) *multi) +{ + return multi ? isl_space_copy(multi->space) : NULL; +} + +/* Return the position of the dimension of the given type and name + * in "multi". + * Return -1 if no such dimension can be found. + */ +int FN(MULTI(BASE),find_dim_by_name)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type, const char *name) +{ + if (!multi) + return -1; + return isl_space_find_dim_by_name(multi->space, type, name); +} + +__isl_give isl_space *FN(MULTI(BASE),get_domain_space)( + __isl_keep MULTI(BASE) *multi) +{ + return multi ? isl_space_domain(isl_space_copy(multi->space)) : NULL; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),alloc)(__isl_take isl_space *space) +{ + isl_ctx *ctx; + int n; + MULTI(BASE) *multi; + + if (!space) + return NULL; + + ctx = isl_space_get_ctx(space); + n = isl_space_dim(space, isl_dim_out); + multi = isl_calloc(ctx, MULTI(BASE), + sizeof(MULTI(BASE)) + (n - 1) * sizeof(struct EL *)); + if (!multi) + goto error; + + multi->space = space; + multi->n = n; + multi->ref = 1; + return multi; +error: + isl_space_free(space); + return NULL; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),dup)(__isl_keep MULTI(BASE) *multi) +{ + int i; + MULTI(BASE) *dup; + + if (!multi) + return NULL; + + dup = FN(MULTI(BASE),alloc)(isl_space_copy(multi->space)); + if (!dup) + return NULL; + + for (i = 0; i < multi->n; ++i) + dup = FN(FN(MULTI(BASE),set),BASE)(dup, i, + FN(EL,copy)(multi->p[i])); + + return dup; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),cow)(__isl_take MULTI(BASE) *multi) +{ + if (!multi) + return NULL; + + if (multi->ref == 1) + return multi; + + multi->ref--; + return FN(MULTI(BASE),dup)(multi); +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),copy)(__isl_keep MULTI(BASE) *multi) +{ + if (!multi) + return NULL; + + multi->ref++; + return multi; +} + +__isl_null MULTI(BASE) *FN(MULTI(BASE),free)(__isl_take MULTI(BASE) *multi) +{ + int i; + + if (!multi) + return NULL; + + if (--multi->ref > 0) + return NULL; + + isl_space_free(multi->space); + for (i = 0; i < multi->n; ++i) + FN(EL,free)(multi->p[i]); + free(multi); + + return NULL; +} + +#ifndef NO_DIMS +/* Check whether "multi" has non-zero coefficients for any dimension + * in the given range or if any of these dimensions appear + * with non-zero coefficients in any of the integer divisions involved. + */ +isl_bool FN(MULTI(BASE),involves_dims)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!multi) + return isl_bool_error; + if (multi->n == 0 || n == 0) + return isl_bool_false; + + for (i = 0; i < multi->n; ++i) { + isl_bool involves; + + involves = FN(EL,involves_dims)(multi->p[i], type, first, n); + if (involves < 0 || involves) + return involves; + } + + return isl_bool_false; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),insert_dims)( + __isl_take MULTI(BASE) *multi, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!multi) + return NULL; + if (type == isl_dim_out) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "cannot insert output/set dimensions", + return FN(MULTI(BASE),free)(multi)); + if (n == 0 && !isl_space_is_named_or_nested(multi->space, type)) + return multi; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + multi->space = isl_space_insert_dims(multi->space, type, first, n); + if (!multi->space) + return FN(MULTI(BASE),free)(multi); + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,insert_dims)(multi->p[i], type, first, n); + if (!multi->p[i]) + return FN(MULTI(BASE),free)(multi); + } + + return multi; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),add_dims)(__isl_take MULTI(BASE) *multi, + enum isl_dim_type type, unsigned n) +{ + unsigned pos; + + pos = FN(MULTI(BASE),dim)(multi, type); + + return FN(MULTI(BASE),insert_dims)(multi, type, pos, n); +} +#endif + +unsigned FN(MULTI(BASE),dim)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type) +{ + return multi ? isl_space_dim(multi->space, type) : 0; +} + +/* Return the position of the first dimension of "type" with id "id". + * Return -1 if there is no such dimension. + */ +int FN(MULTI(BASE),find_dim_by_id)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type, __isl_keep isl_id *id) +{ + if (!multi) + return -1; + return isl_space_find_dim_by_id(multi->space, type, id); +} + +/* Return the id of the given dimension. + */ +__isl_give isl_id *FN(MULTI(BASE),get_dim_id)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type, unsigned pos) +{ + return multi ? isl_space_get_dim_id(multi->space, type, pos) : NULL; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),set_dim_name)( + __isl_take MULTI(BASE) *multi, + enum isl_dim_type type, unsigned pos, const char *s) +{ + int i; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + multi->space = isl_space_set_dim_name(multi->space, type, pos, s); + if (!multi->space) + return FN(MULTI(BASE),free)(multi); + + if (type == isl_dim_out) + return multi; + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,set_dim_name)(multi->p[i], type, pos, s); + if (!multi->p[i]) + return FN(MULTI(BASE),free)(multi); + } + + return multi; +} + +const char *FN(MULTI(BASE),get_tuple_name)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type) +{ + return multi ? isl_space_get_tuple_name(multi->space, type) : NULL; +} + +/* Does the specified tuple have an id? + */ +isl_bool FN(MULTI(BASE),has_tuple_id)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type) +{ + if (!multi) + return isl_bool_error; + return isl_space_has_tuple_id(multi->space, type); +} + +/* Return the id of the specified tuple. + */ +__isl_give isl_id *FN(MULTI(BASE),get_tuple_id)(__isl_keep MULTI(BASE) *multi, + enum isl_dim_type type) +{ + return multi ? isl_space_get_tuple_id(multi->space, type) : NULL; +} + +__isl_give EL *FN(FN(MULTI(BASE),get),BASE)(__isl_keep MULTI(BASE) *multi, + int pos) +{ + isl_ctx *ctx; + + if (!multi) + return NULL; + ctx = FN(MULTI(BASE),get_ctx)(multi); + if (pos < 0 || pos >= multi->n) + isl_die(ctx, isl_error_invalid, + "index out of bounds", return NULL); + return FN(EL,copy)(multi->p[pos]); +} + +__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),set),BASE)( + __isl_take MULTI(BASE) *multi, int pos, __isl_take EL *el) +{ + isl_space *multi_space = NULL; + isl_space *el_space = NULL; + int match; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi || !el) + goto error; + + multi_space = FN(MULTI(BASE),get_space)(multi); + match = FN(EL,matching_params)(el, multi_space); + if (match < 0) + goto error; + if (!match) { + multi = FN(MULTI(BASE),align_params)(multi, + FN(EL,get_space)(el)); + isl_space_free(multi_space); + multi_space = FN(MULTI(BASE),get_space)(multi); + el = FN(EL,align_params)(el, isl_space_copy(multi_space)); + } + if (FN(EL,check_match_domain_space)(el, multi_space) < 0) + goto error; + + if (pos < 0 || pos >= multi->n) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "index out of bounds", goto error); + + FN(EL,free)(multi->p[pos]); + multi->p[pos] = el; + + isl_space_free(multi_space); + isl_space_free(el_space); + + return multi; +error: + FN(MULTI(BASE),free)(multi); + FN(EL,free)(el); + isl_space_free(multi_space); + isl_space_free(el_space); + return NULL; +} + +/* Reset the space of "multi". This function is called from isl_pw_templ.c + * and doesn't know if the space of an element object is represented + * directly or through its domain. It therefore passes along both, + * which we pass along to the element function since we don't how + * that is represented either. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_space_and_domain)( + __isl_take MULTI(BASE) *multi, __isl_take isl_space *space, + __isl_take isl_space *domain) +{ + int i; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi || !space || !domain) + goto error; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,reset_domain_space)(multi->p[i], + isl_space_copy(domain)); + if (!multi->p[i]) + goto error; + } + isl_space_free(domain); + isl_space_free(multi->space); + multi->space = space; + + return multi; +error: + isl_space_free(domain); + isl_space_free(space); + FN(MULTI(BASE),free)(multi); + return NULL; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_domain_space)( + __isl_take MULTI(BASE) *multi, __isl_take isl_space *domain) +{ + isl_space *space; + + space = isl_space_extend_domain_with_range(isl_space_copy(domain), + isl_space_copy(multi->space)); + return FN(MULTI(BASE),reset_space_and_domain)(multi, space, domain); +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_space)( + __isl_take MULTI(BASE) *multi, __isl_take isl_space *space) +{ + isl_space *domain; + + domain = isl_space_domain(isl_space_copy(space)); + return FN(MULTI(BASE),reset_space_and_domain)(multi, space, domain); +} + +/* Set the id of the given dimension of "multi" to "id". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),set_dim_id)( + __isl_take MULTI(BASE) *multi, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + isl_space *space; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi || !id) + goto error; + + space = FN(MULTI(BASE),get_space)(multi); + space = isl_space_set_dim_id(space, type, pos, id); + + return FN(MULTI(BASE),reset_space)(multi, space); +error: + isl_id_free(id); + FN(MULTI(BASE),free)(multi); + return NULL; +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),set_tuple_name)( + __isl_keep MULTI(BASE) *multi, enum isl_dim_type type, + const char *s) +{ + isl_space *space; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + space = FN(MULTI(BASE),get_space)(multi); + space = isl_space_set_tuple_name(space, type, s); + + return FN(MULTI(BASE),reset_space)(multi, space); +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),set_tuple_id)( + __isl_take MULTI(BASE) *multi, enum isl_dim_type type, + __isl_take isl_id *id) +{ + isl_space *space; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + goto error; + + space = FN(MULTI(BASE),get_space)(multi); + space = isl_space_set_tuple_id(space, type, id); + + return FN(MULTI(BASE),reset_space)(multi, space); +error: + isl_id_free(id); + return NULL; +} + +/* Drop the id on the specified tuple. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_tuple_id)( + __isl_take MULTI(BASE) *multi, enum isl_dim_type type) +{ + isl_space *space; + + if (!multi) + return NULL; + if (!FN(MULTI(BASE),has_tuple_id)(multi, type)) + return multi; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + space = FN(MULTI(BASE),get_space)(multi); + space = isl_space_reset_tuple_id(space, type); + + return FN(MULTI(BASE),reset_space)(multi, space); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the space of "multi". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_user)( + __isl_take MULTI(BASE) *multi) +{ + isl_space *space; + + space = FN(MULTI(BASE),get_space)(multi); + space = isl_space_reset_user(space); + + return FN(MULTI(BASE),reset_space)(multi, space); +} + +__isl_give MULTI(BASE) *FN(MULTI(BASE),realign_domain)( + __isl_take MULTI(BASE) *multi, __isl_take isl_reordering *exp) +{ + int i; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi || !exp) + goto error; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,realign_domain)(multi->p[i], + isl_reordering_copy(exp)); + if (!multi->p[i]) + goto error; + } + + multi = FN(MULTI(BASE),reset_domain_space)(multi, + isl_space_copy(exp->dim)); + + isl_reordering_free(exp); + return multi; +error: + isl_reordering_free(exp); + FN(MULTI(BASE),free)(multi); + return NULL; +} + +/* Align the parameters of "multi" to those of "model". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),align_params)( + __isl_take MULTI(BASE) *multi, __isl_take isl_space *model) +{ + isl_ctx *ctx; + isl_reordering *exp; + + if (!multi || !model) + goto error; + + if (isl_space_match(multi->space, isl_dim_param, + model, isl_dim_param)) { + isl_space_free(model); + return multi; + } + + ctx = isl_space_get_ctx(model); + if (!isl_space_has_named_params(model)) + isl_die(ctx, isl_error_invalid, + "model has unnamed parameters", goto error); + if (!isl_space_has_named_params(multi->space)) + isl_die(ctx, isl_error_invalid, + "input has unnamed parameters", goto error); + + model = isl_space_params(model); + exp = isl_parameter_alignment_reordering(multi->space, model); + exp = isl_reordering_extend_space(exp, + FN(MULTI(BASE),get_domain_space)(multi)); + multi = FN(MULTI(BASE),realign_domain)(multi, exp); + + isl_space_free(model); + return multi; +error: + isl_space_free(model); + FN(MULTI(BASE),free)(multi); + return NULL; +} + +__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),from),LIST(BASE))( + __isl_take isl_space *space, __isl_take LIST(EL) *list) +{ + int i; + int n; + isl_ctx *ctx; + MULTI(BASE) *multi; + + if (!space || !list) + goto error; + + ctx = isl_space_get_ctx(space); + n = FN(FN(LIST(EL),n),BASE)(list); + if (n != isl_space_dim(space, isl_dim_out)) + isl_die(ctx, isl_error_invalid, + "invalid number of elements in list", goto error); + + multi = FN(MULTI(BASE),alloc)(isl_space_copy(space)); + for (i = 0; i < n; ++i) { + multi = FN(FN(MULTI(BASE),set),BASE)(multi, i, + FN(FN(LIST(EL),get),BASE)(list, i)); + } + + isl_space_free(space); + FN(LIST(EL),free)(list); + return multi; +error: + isl_space_free(space); + FN(LIST(EL),free)(list); + return NULL; +} + +#ifndef NO_IDENTITY +/* Create a multi expression in the given space that maps each + * input dimension to the corresponding output dimension. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),identity)(__isl_take isl_space *space) +{ + int i, n; + isl_local_space *ls; + MULTI(BASE) *multi; + + if (!space) + return NULL; + + if (isl_space_is_set(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "expecting map space", goto error); + + n = isl_space_dim(space, isl_dim_out); + if (n != isl_space_dim(space, isl_dim_in)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "number of input and output dimensions needs to be " + "the same", goto error); + + multi = FN(MULTI(BASE),alloc)(isl_space_copy(space)); + + if (!n) { + isl_space_free(space); + return multi; + } + + space = isl_space_domain(space); + ls = isl_local_space_from_space(space); + + for (i = 0; i < n; ++i) { + EL *el; + el = FN(EL,var_on_domain)(isl_local_space_copy(ls), + isl_dim_set, i); + multi = FN(FN(MULTI(BASE),set),BASE)(multi, i, el); + } + + isl_local_space_free(ls); + + return multi; +error: + isl_space_free(space); + return NULL; +} +#endif + +#ifndef NO_ZERO +/* Construct a multi expression in the given space with value zero in + * each of the output dimensions. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),zero)(__isl_take isl_space *space) +{ + int n; + MULTI(BASE) *multi; + + if (!space) + return NULL; + + n = isl_space_dim(space , isl_dim_out); + multi = FN(MULTI(BASE),alloc)(isl_space_copy(space)); + + if (!n) + isl_space_free(space); + else { + int i; + isl_local_space *ls; + EL *el; + + space = isl_space_domain(space); + ls = isl_local_space_from_space(space); + el = FN(EL,zero_on_domain)(ls); + + for (i = 0; i < n; ++i) + multi = FN(FN(MULTI(BASE),set),BASE)(multi, i, + FN(EL,copy)(el)); + + FN(EL,free)(el); + } + + return multi; +} +#endif + +#ifndef NO_FROM_BASE +/* Create a multiple expression with a single output/set dimension + * equal to "el". + * For most multiple expression types, the base type has a single + * output/set dimension and the space of the result is therefore + * the same as the space of the input. + * In the case of isl_multi_union_pw_aff, however, the base type + * lives in a parameter space and we therefore need to add + * a single set dimension. + */ +__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),from),BASE)(__isl_take EL *el) +{ + isl_space *space; + MULTI(BASE) *multi; + + space = FN(EL,get_space(el)); + if (isl_space_is_params(space)) { + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, 1); + } + multi = FN(MULTI(BASE),alloc)(space); + multi = FN(FN(MULTI(BASE),set),BASE)(multi, 0, el); + + return multi; +} +#endif + +__isl_give MULTI(BASE) *FN(MULTI(BASE),drop_dims)( + __isl_take MULTI(BASE) *multi, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + unsigned dim; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + dim = FN(MULTI(BASE),dim)(multi, type); + if (first + n > dim || first + n < first) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "index out of bounds", + return FN(MULTI(BASE),cow)(multi)); + + multi->space = isl_space_drop_dims(multi->space, type, first, n); + if (!multi->space) + return FN(MULTI(BASE),cow)(multi); + + if (type == isl_dim_out) { + for (i = 0; i < n; ++i) + FN(EL,free)(multi->p[first + i]); + for (i = first; i + n < multi->n; ++i) + multi->p[i] = multi->p[i + n]; + multi->n -= n; + + return multi; + } + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,drop_dims)(multi->p[i], type, first, n); + if (!multi->p[i]) + return FN(MULTI(BASE),cow)(multi); + } + + return multi; +} + +/* Align the parameters of "multi1" and "multi2" (if needed) and call "fn". + */ +static __isl_give MULTI(BASE) *FN(MULTI(BASE),align_params_multi_multi_and)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2, + __isl_give MULTI(BASE) *(*fn)(__isl_take MULTI(BASE) *multi1, + __isl_take MULTI(BASE) *multi2)) +{ + isl_ctx *ctx; + + if (!multi1 || !multi2) + goto error; + if (isl_space_match(multi1->space, isl_dim_param, + multi2->space, isl_dim_param)) + return fn(multi1, multi2); + ctx = FN(MULTI(BASE),get_ctx)(multi1); + if (!isl_space_has_named_params(multi1->space) || + !isl_space_has_named_params(multi2->space)) + isl_die(ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + multi1 = FN(MULTI(BASE),align_params)(multi1, + FN(MULTI(BASE),get_space)(multi2)); + multi2 = FN(MULTI(BASE),align_params)(multi2, + FN(MULTI(BASE),get_space)(multi1)); + return fn(multi1, multi2); +error: + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return NULL; +} + +/* Given two MULTI(BASE)s A -> B and C -> D, + * construct a MULTI(BASE) (A * C) -> [B -> D]. + * + * The parameters are assumed to have been aligned. + */ +static __isl_give MULTI(BASE) *FN(MULTI(BASE),range_product_aligned)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + int i, n1, n2; + EL *el; + isl_space *space; + MULTI(BASE) *res; + + if (!multi1 || !multi2) + goto error; + + space = isl_space_range_product(FN(MULTI(BASE),get_space)(multi1), + FN(MULTI(BASE),get_space)(multi2)); + res = FN(MULTI(BASE),alloc)(space); + + n1 = FN(MULTI(BASE),dim)(multi1, isl_dim_out); + n2 = FN(MULTI(BASE),dim)(multi2, isl_dim_out); + + for (i = 0; i < n1; ++i) { + el = FN(FN(MULTI(BASE),get),BASE)(multi1, i); + res = FN(FN(MULTI(BASE),set),BASE)(res, i, el); + } + + for (i = 0; i < n2; ++i) { + el = FN(FN(MULTI(BASE),get),BASE)(multi2, i); + res = FN(FN(MULTI(BASE),set),BASE)(res, n1 + i, el); + } + + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return res; +error: + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return NULL; +} + +/* Given two MULTI(BASE)s A -> B and C -> D, + * construct a MULTI(BASE) (A * C) -> [B -> D]. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),range_product)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + return FN(MULTI(BASE),align_params_multi_multi_and)(multi1, multi2, + &FN(MULTI(BASE),range_product_aligned)); +} + +/* Is the range of "multi" a wrapped relation? + */ +isl_bool FN(MULTI(BASE),range_is_wrapping)(__isl_keep MULTI(BASE) *multi) +{ + if (!multi) + return isl_bool_error; + return isl_space_range_is_wrapping(multi->space); +} + +/* Given a function A -> [B -> C], extract the function A -> B. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),range_factor_domain)( + __isl_take MULTI(BASE) *multi) +{ + isl_space *space; + int total, keep; + + if (!multi) + return NULL; + if (!isl_space_range_is_wrapping(multi->space)) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "range is not a product", + return FN(MULTI(BASE),free)(multi)); + + space = FN(MULTI(BASE),get_space)(multi); + total = isl_space_dim(space, isl_dim_out); + space = isl_space_range_factor_domain(space); + keep = isl_space_dim(space, isl_dim_out); + multi = FN(MULTI(BASE),drop_dims)(multi, + isl_dim_out, keep, total - keep); + multi = FN(MULTI(BASE),reset_space)(multi, space); + + return multi; +} + +/* Given a function A -> [B -> C], extract the function A -> C. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),range_factor_range)( + __isl_take MULTI(BASE) *multi) +{ + isl_space *space; + int total, keep; + + if (!multi) + return NULL; + if (!isl_space_range_is_wrapping(multi->space)) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "range is not a product", + return FN(MULTI(BASE),free)(multi)); + + space = FN(MULTI(BASE),get_space)(multi); + total = isl_space_dim(space, isl_dim_out); + space = isl_space_range_factor_range(space); + keep = isl_space_dim(space, isl_dim_out); + multi = FN(MULTI(BASE),drop_dims)(multi, isl_dim_out, 0, total - keep); + multi = FN(MULTI(BASE),reset_space)(multi, space); + + return multi; +} + +/* Given a function [B -> C], extract the function C. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),factor_range)( + __isl_take MULTI(BASE) *multi) +{ + isl_space *space; + int total, keep; + + if (!multi) + return NULL; + if (!isl_space_is_wrapping(multi->space)) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "not a product", return FN(MULTI(BASE),free)(multi)); + + space = FN(MULTI(BASE),get_space)(multi); + total = isl_space_dim(space, isl_dim_out); + space = isl_space_factor_range(space); + keep = isl_space_dim(space, isl_dim_out); + multi = FN(MULTI(BASE),drop_dims)(multi, isl_dim_out, 0, total - keep); + multi = FN(MULTI(BASE),reset_space)(multi, space); + + return multi; +} + +#ifndef NO_PRODUCT +/* Given two MULTI(BASE)s A -> B and C -> D, + * construct a MULTI(BASE) [A -> C] -> [B -> D]. + * + * The parameters are assumed to have been aligned. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),product_aligned)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + int i; + EL *el; + isl_space *space; + MULTI(BASE) *res; + int in1, in2, out1, out2; + + in1 = FN(MULTI(BASE),dim)(multi1, isl_dim_in); + in2 = FN(MULTI(BASE),dim)(multi2, isl_dim_in); + out1 = FN(MULTI(BASE),dim)(multi1, isl_dim_out); + out2 = FN(MULTI(BASE),dim)(multi2, isl_dim_out); + space = isl_space_product(FN(MULTI(BASE),get_space)(multi1), + FN(MULTI(BASE),get_space)(multi2)); + res = FN(MULTI(BASE),alloc)(isl_space_copy(space)); + space = isl_space_domain(space); + + for (i = 0; i < out1; ++i) { + el = FN(FN(MULTI(BASE),get),BASE)(multi1, i); + el = FN(EL,insert_dims)(el, isl_dim_in, in1, in2); + el = FN(EL,reset_domain_space)(el, isl_space_copy(space)); + res = FN(FN(MULTI(BASE),set),BASE)(res, i, el); + } + + for (i = 0; i < out2; ++i) { + el = FN(FN(MULTI(BASE),get),BASE)(multi2, i); + el = FN(EL,insert_dims)(el, isl_dim_in, 0, in1); + el = FN(EL,reset_domain_space)(el, isl_space_copy(space)); + res = FN(FN(MULTI(BASE),set),BASE)(res, out1 + i, el); + } + + isl_space_free(space); + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return res; +} + +/* Given two MULTI(BASE)s A -> B and C -> D, + * construct a MULTI(BASE) [A -> C] -> [B -> D]. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),product)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + return FN(MULTI(BASE),align_params_multi_multi_and)(multi1, multi2, + &FN(MULTI(BASE),product_aligned)); +} +#endif + +__isl_give MULTI(BASE) *FN(MULTI(BASE),flatten_range)( + __isl_take MULTI(BASE) *multi) +{ + if (!multi) + return NULL; + + if (!multi->space->nested[1]) + return multi; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + multi->space = isl_space_flatten_range(multi->space); + if (!multi->space) + return FN(MULTI(BASE),free)(multi); + + return multi; +} + +/* Given two MULTI(BASE)s A -> B and C -> D, + * construct a MULTI(BASE) (A * C) -> (B, D). + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),flat_range_product)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + MULTI(BASE) *multi; + + multi = FN(MULTI(BASE),range_product)(multi1, multi2); + multi = FN(MULTI(BASE),flatten_range)(multi); + return multi; +} + +/* Given two multi expressions, "multi1" + * + * [A] -> [B1 B2] + * + * where B2 starts at position "pos", and "multi2" + * + * [A] -> [D] + * + * return the multi expression + * + * [A] -> [B1 D B2] + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),range_splice)( + __isl_take MULTI(BASE) *multi1, unsigned pos, + __isl_take MULTI(BASE) *multi2) +{ + MULTI(BASE) *res; + unsigned dim; + + if (!multi1 || !multi2) + goto error; + + dim = FN(MULTI(BASE),dim)(multi1, isl_dim_out); + if (pos > dim) + isl_die(FN(MULTI(BASE),get_ctx)(multi1), isl_error_invalid, + "index out of bounds", goto error); + + res = FN(MULTI(BASE),copy)(multi1); + res = FN(MULTI(BASE),drop_dims)(res, isl_dim_out, pos, dim - pos); + multi1 = FN(MULTI(BASE),drop_dims)(multi1, isl_dim_out, 0, pos); + + res = FN(MULTI(BASE),flat_range_product)(res, multi2); + res = FN(MULTI(BASE),flat_range_product)(res, multi1); + + return res; +error: + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return NULL; +} + +#ifndef NO_SPLICE +/* Given two multi expressions, "multi1" + * + * [A1 A2] -> [B1 B2] + * + * where A2 starts at position "in_pos" and B2 starts at position "out_pos", + * and "multi2" + * + * [C] -> [D] + * + * return the multi expression + * + * [A1 C A2] -> [B1 D B2] + * + * We first insert input dimensions to obtain + * + * [A1 C A2] -> [B1 B2] + * + * and + * + * [A1 C A2] -> [D] + * + * and then apply range_splice. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),splice)( + __isl_take MULTI(BASE) *multi1, unsigned in_pos, unsigned out_pos, + __isl_take MULTI(BASE) *multi2) +{ + unsigned n_in1; + unsigned n_in2; + + if (!multi1 || !multi2) + goto error; + + n_in1 = FN(MULTI(BASE),dim)(multi1, isl_dim_in); + if (in_pos > n_in1) + isl_die(FN(MULTI(BASE),get_ctx)(multi1), isl_error_invalid, + "index out of bounds", goto error); + + n_in2 = FN(MULTI(BASE),dim)(multi2, isl_dim_in); + + multi1 = FN(MULTI(BASE),insert_dims)(multi1, isl_dim_in, in_pos, n_in2); + multi2 = FN(MULTI(BASE),insert_dims)(multi2, isl_dim_in, n_in2, + n_in1 - in_pos); + multi2 = FN(MULTI(BASE),insert_dims)(multi2, isl_dim_in, 0, in_pos); + + return FN(MULTI(BASE),range_splice)(multi1, out_pos, multi2); +error: + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return NULL; +} +#endif + +/* This function is currently only used from isl_aff.c + */ +static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2, + __isl_give EL *(*fn)(__isl_take EL *, __isl_take EL *)) + __attribute__ ((unused)); + +/* Pairwise perform "fn" to the elements of "multi1" and "multi2" and + * return the result. + */ +static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2, + __isl_give EL *(*fn)(__isl_take EL *, __isl_take EL *)) +{ + int i; + isl_ctx *ctx; + + multi1 = FN(MULTI(BASE),cow)(multi1); + if (!multi1 || !multi2) + goto error; + + ctx = FN(MULTI(BASE),get_ctx)(multi1); + if (!isl_space_is_equal(multi1->space, multi2->space)) + isl_die(ctx, isl_error_invalid, + "spaces don't match", goto error); + + for (i = 0; i < multi1->n; ++i) { + multi1->p[i] = fn(multi1->p[i], FN(EL,copy)(multi2->p[i])); + if (!multi1->p[i]) + goto error; + } + + FN(MULTI(BASE),free)(multi2); + return multi1; +error: + FN(MULTI(BASE),free)(multi1); + FN(MULTI(BASE),free)(multi2); + return NULL; +} + +/* Add "multi2" from "multi1" and return the result. + * + * The parameters of "multi1" and "multi2" are assumed to have been aligned. + */ +static __isl_give MULTI(BASE) *FN(MULTI(BASE),add_aligned)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + return FN(MULTI(BASE),bin_op)(multi1, multi2, &FN(EL,add)); +} + +/* Add "multi2" from "multi1" and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),add)(__isl_take MULTI(BASE) *multi1, + __isl_take MULTI(BASE) *multi2) +{ + return FN(MULTI(BASE),align_params_multi_multi_and)(multi1, multi2, + &FN(MULTI(BASE),add_aligned)); +} + +/* Subtract "multi2" from "multi1" and return the result. + * + * The parameters of "multi1" and "multi2" are assumed to have been aligned. + */ +static __isl_give MULTI(BASE) *FN(MULTI(BASE),sub_aligned)( + __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2) +{ + return FN(MULTI(BASE),bin_op)(multi1, multi2, &FN(EL,sub)); +} + +/* Subtract "multi2" from "multi1" and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),sub)(__isl_take MULTI(BASE) *multi1, + __isl_take MULTI(BASE) *multi2) +{ + return FN(MULTI(BASE),align_params_multi_multi_and)(multi1, multi2, + &FN(MULTI(BASE),sub_aligned)); +} + +/* Multiply the elements of "multi" by "v" and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_val)(__isl_take MULTI(BASE) *multi, + __isl_take isl_val *v) +{ + int i; + + if (!multi || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return multi; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational factor", goto error); + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,scale_val)(multi->p[i], isl_val_copy(v)); + if (!multi->p[i]) + goto error; + } + + isl_val_free(v); + return multi; +error: + isl_val_free(v); + return FN(MULTI(BASE),free)(multi); +} + +/* Divide the elements of "multi" by "v" and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_down_val)( + __isl_take MULTI(BASE) *multi, __isl_take isl_val *v) +{ + int i; + + if (!multi || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return multi; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational factor", goto error); + if (isl_val_is_zero(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "cannot scale down by zero", goto error); + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,scale_down_val)(multi->p[i], + isl_val_copy(v)); + if (!multi->p[i]) + goto error; + } + + isl_val_free(v); + return multi; +error: + isl_val_free(v); + return FN(MULTI(BASE),free)(multi); +} + +/* Multiply the elements of "multi" by the corresponding element of "mv" + * and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_multi_val)( + __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv) +{ + int i; + + if (!multi || !mv) + goto error; + + if (!isl_space_tuple_is_equal(multi->space, isl_dim_out, + mv->space, isl_dim_set)) + isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid, + "spaces don't match", goto error); + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + goto error; + + for (i = 0; i < multi->n; ++i) { + isl_val *v; + + v = isl_multi_val_get_val(mv, i); + multi->p[i] = FN(EL,scale_val)(multi->p[i], v); + if (!multi->p[i]) + goto error; + } + + isl_multi_val_free(mv); + return multi; +error: + isl_multi_val_free(mv); + return FN(MULTI(BASE),free)(multi); +} + +/* Divide the elements of "multi" by the corresponding element of "mv" + * and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_down_multi_val)( + __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv) +{ + int i; + + if (!multi || !mv) + goto error; + + if (!isl_space_tuple_is_equal(multi->space, isl_dim_out, + mv->space, isl_dim_set)) + isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid, + "spaces don't match", goto error); + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + isl_val *v; + + v = isl_multi_val_get_val(mv, i); + multi->p[i] = FN(EL,scale_down_val)(multi->p[i], v); + if (!multi->p[i]) + goto error; + } + + isl_multi_val_free(mv); + return multi; +error: + isl_multi_val_free(mv); + return FN(MULTI(BASE),free)(multi); +} + +/* Compute the residues of the elements of "multi" modulo + * the corresponding element of "mv" and return the result. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),mod_multi_val)( + __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv) +{ + int i; + + if (!multi || !mv) + goto error; + + if (!isl_space_tuple_is_equal(multi->space, isl_dim_out, + mv->space, isl_dim_set)) + isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid, + "spaces don't match", goto error); + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + isl_val *v; + + v = isl_multi_val_get_val(mv, i); + multi->p[i] = FN(EL,mod_val)(multi->p[i], v); + if (!multi->p[i]) + goto error; + } + + isl_multi_val_free(mv); + return multi; +error: + isl_multi_val_free(mv); + return FN(MULTI(BASE),free)(multi); +} + +#ifndef NO_MOVE_DIMS +/* Move the "n" dimensions of "src_type" starting at "src_pos" of "multi" + * to dimensions of "dst_type" at "dst_pos". + * + * We only support moving input dimensions to parameters and vice versa. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),move_dims)(__isl_take MULTI(BASE) *multi, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + int i; + + if (!multi) + return NULL; + + if (n == 0 && + !isl_space_is_named_or_nested(multi->space, src_type) && + !isl_space_is_named_or_nested(multi->space, dst_type)) + return multi; + + if (dst_type == isl_dim_out || src_type == isl_dim_out) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "cannot move output/set dimension", + return FN(MULTI(BASE),free)(multi)); + if (dst_type == isl_dim_div || src_type == isl_dim_div) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "cannot move divs", + return FN(MULTI(BASE),free)(multi)); + if (src_pos + n > isl_space_dim(multi->space, src_type)) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "range out of bounds", + return FN(MULTI(BASE),free)(multi)); + if (dst_type == src_type) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_unsupported, + "moving dims within the same type not supported", + return FN(MULTI(BASE),free)(multi)); + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + multi->space = isl_space_move_dims(multi->space, dst_type, dst_pos, + src_type, src_pos, n); + if (!multi->space) + return FN(MULTI(BASE),free)(multi); + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,move_dims)(multi->p[i], dst_type, dst_pos, + src_type, src_pos, n); + if (!multi->p[i]) + return FN(MULTI(BASE),free)(multi); + } + + return multi; +} +#endif + +/* Convert a multiple expression defined over a parameter domain + * into one that is defined over a zero-dimensional set. + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),from_range)( + __isl_take MULTI(BASE) *multi) +{ + isl_space *space; + + if (!multi) + return NULL; + if (!isl_space_is_set(multi->space)) + isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid, + "not living in a set space", + return FN(MULTI(BASE),free)(multi)); + + space = FN(MULTI(BASE),get_space)(multi); + space = isl_space_from_range(space); + multi = FN(MULTI(BASE),reset_space)(multi, space); + + return multi; +} + +/* Are "multi1" and "multi2" obviously equal? + */ +isl_bool FN(MULTI(BASE),plain_is_equal)(__isl_keep MULTI(BASE) *multi1, + __isl_keep MULTI(BASE) *multi2) +{ + int i; + isl_bool equal; + + if (!multi1 || !multi2) + return isl_bool_error; + if (multi1->n != multi2->n) + return isl_bool_false; + equal = isl_space_is_equal(multi1->space, multi2->space); + if (equal < 0 || !equal) + return equal; + + for (i = 0; i < multi1->n; ++i) { + equal = FN(EL,plain_is_equal)(multi1->p[i], multi2->p[i]); + if (equal < 0 || !equal) + return equal; + } + + return isl_bool_true; +} + +#ifndef NO_DOMAIN +/* Return the shared domain of the elements of "multi". + */ +__isl_give isl_set *FN(MULTI(BASE),domain)(__isl_take MULTI(BASE) *multi) +{ + int i; + isl_set *dom; + + if (!multi) + return NULL; + + dom = isl_set_universe(FN(MULTI(BASE),get_domain_space)(multi)); + for (i = 0; i < multi->n; ++i) { + isl_set *dom_i; + + dom_i = FN(EL,domain)(FN(FN(MULTI(BASE),get),BASE)(multi, i)); + dom = isl_set_intersect(dom, dom_i); + } + + FN(MULTI(BASE),free)(multi); + return dom; +} +#endif + +#ifndef NO_NEG +/* Return the opposite of "multi". + */ +__isl_give MULTI(BASE) *FN(MULTI(BASE),neg)(__isl_take MULTI(BASE) *multi) +{ + int i; + + multi = FN(MULTI(BASE),cow)(multi); + if (!multi) + return NULL; + + for (i = 0; i < multi->n; ++i) { + multi->p[i] = FN(EL,neg)(multi->p[i]); + if (!multi->p[i]) + return FN(MULTI(BASE),free)(multi); + } + + return multi; +} +#endif Index: lib/Analysis/isl/isl_obj.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_obj.c @@ -0,0 +1,364 @@ +/* + * Copyright 2010 INRIA Saclay + * Copyright 2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void *isl_obj_val_copy(void *v) +{ + return isl_val_copy((isl_val *)v); +} + +static void isl_obj_val_free(void *v) +{ + isl_val_free((isl_val *)v); +} + +static __isl_give isl_printer *isl_obj_val_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_val(p, (isl_val *)v); +} + +static void *isl_obj_val_add(void *v1, void *v2) +{ + return isl_val_add((isl_val *) v1, (isl_val *) v2); +} + +struct isl_obj_vtable isl_obj_val_vtable = { + isl_obj_val_copy, + isl_obj_val_add, + isl_obj_val_print, + isl_obj_val_free +}; + +static void *isl_obj_map_copy(void *v) +{ + return isl_map_copy((struct isl_map *)v); +} + +static void isl_obj_map_free(void *v) +{ + isl_map_free((struct isl_map *)v); +} + +static __isl_give isl_printer *isl_obj_map_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_map(p, (struct isl_map *)v); +} + +static void *isl_obj_map_add(void *v1, void *v2) +{ + return isl_map_union((struct isl_map *)v1, (struct isl_map *)v2); +} + +struct isl_obj_vtable isl_obj_map_vtable = { + isl_obj_map_copy, + isl_obj_map_add, + isl_obj_map_print, + isl_obj_map_free +}; + +static void *isl_obj_union_map_copy(void *v) +{ + return isl_union_map_copy((isl_union_map *)v); +} + +static void isl_obj_union_map_free(void *v) +{ + isl_union_map_free((isl_union_map *)v); +} + +static __isl_give isl_printer *isl_obj_union_map_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_union_map(p, (isl_union_map *)v); +} + +static void *isl_obj_union_map_add(void *v1, void *v2) +{ + return isl_union_map_union((isl_union_map *)v1, (isl_union_map *)v2); +} + +struct isl_obj_vtable isl_obj_union_map_vtable = { + isl_obj_union_map_copy, + isl_obj_union_map_add, + isl_obj_union_map_print, + isl_obj_union_map_free +}; + +static void *isl_obj_set_copy(void *v) +{ + return isl_set_copy((struct isl_set *)v); +} + +static void isl_obj_set_free(void *v) +{ + isl_set_free((struct isl_set *)v); +} + +static __isl_give isl_printer *isl_obj_set_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_set(p, (struct isl_set *)v); +} + +static void *isl_obj_set_add(void *v1, void *v2) +{ + return isl_set_union((struct isl_set *)v1, (struct isl_set *)v2); +} + +struct isl_obj_vtable isl_obj_set_vtable = { + isl_obj_set_copy, + isl_obj_set_add, + isl_obj_set_print, + isl_obj_set_free +}; + +static void *isl_obj_union_set_copy(void *v) +{ + return isl_union_set_copy((isl_union_set *)v); +} + +static void isl_obj_union_set_free(void *v) +{ + isl_union_set_free((isl_union_set *)v); +} + +static __isl_give isl_printer *isl_obj_union_set_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_union_set(p, (isl_union_set *)v); +} + +static void *isl_obj_union_set_add(void *v1, void *v2) +{ + return isl_union_set_union((isl_union_set *)v1, (isl_union_set *)v2); +} + +struct isl_obj_vtable isl_obj_union_set_vtable = { + isl_obj_union_set_copy, + isl_obj_union_set_add, + isl_obj_union_set_print, + isl_obj_union_set_free +}; + +static void *isl_obj_pw_multi_aff_copy(void *v) +{ + return isl_pw_multi_aff_copy((isl_pw_multi_aff *) v); +} + +static void isl_obj_pw_multi_aff_free(void *v) +{ + isl_pw_multi_aff_free((isl_pw_multi_aff *) v); +} + +static __isl_give isl_printer *isl_obj_pw_multi_aff_print( + __isl_take isl_printer *p, void *v) +{ + return isl_printer_print_pw_multi_aff(p, (isl_pw_multi_aff *) v); +} + +static void *isl_obj_pw_multi_aff_add(void *v1, void *v2) +{ + return isl_pw_multi_aff_add((isl_pw_multi_aff *) v1, + (isl_pw_multi_aff *) v2); +} + +struct isl_obj_vtable isl_obj_pw_multi_aff_vtable = { + isl_obj_pw_multi_aff_copy, + isl_obj_pw_multi_aff_add, + isl_obj_pw_multi_aff_print, + isl_obj_pw_multi_aff_free +}; + +static void *isl_obj_none_copy(void *v) +{ + return v; +} + +static void isl_obj_none_free(void *v) +{ +} + +static __isl_give isl_printer *isl_obj_none_print(__isl_take isl_printer *p, + void *v) +{ + return p; +} + +static void *isl_obj_none_add(void *v1, void *v2) +{ + return NULL; +} + +struct isl_obj_vtable isl_obj_none_vtable = { + isl_obj_none_copy, + isl_obj_none_add, + isl_obj_none_print, + isl_obj_none_free +}; + +static void *isl_obj_pw_qp_copy(void *v) +{ + return isl_pw_qpolynomial_copy((struct isl_pw_qpolynomial *)v); +} + +static void isl_obj_pw_qp_free(void *v) +{ + isl_pw_qpolynomial_free((struct isl_pw_qpolynomial *)v); +} + +static __isl_give isl_printer *isl_obj_pw_qp_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_pw_qpolynomial(p, + (struct isl_pw_qpolynomial *)v); +} + +static void *isl_obj_pw_qp_add(void *v1, void *v2) +{ + return isl_pw_qpolynomial_add((struct isl_pw_qpolynomial *)v1, + (struct isl_pw_qpolynomial *)v2); +} + +struct isl_obj_vtable isl_obj_pw_qpolynomial_vtable = { + isl_obj_pw_qp_copy, + isl_obj_pw_qp_add, + isl_obj_pw_qp_print, + isl_obj_pw_qp_free +}; + +static void *isl_obj_union_pw_qp_copy(void *v) +{ + return isl_union_pw_qpolynomial_copy((struct isl_union_pw_qpolynomial *)v); +} + +static void isl_obj_union_pw_qp_free(void *v) +{ + isl_union_pw_qpolynomial_free((struct isl_union_pw_qpolynomial *)v); +} + +static __isl_give isl_printer *isl_obj_union_pw_qp_print( + __isl_take isl_printer *p, void *v) +{ + return isl_printer_print_union_pw_qpolynomial(p, + (struct isl_union_pw_qpolynomial *)v); +} + +static void *isl_obj_union_pw_qp_add(void *v1, void *v2) +{ + return isl_union_pw_qpolynomial_add( + (struct isl_union_pw_qpolynomial *)v1, + (struct isl_union_pw_qpolynomial *)v2); +} + +struct isl_obj_vtable isl_obj_union_pw_qpolynomial_vtable = { + isl_obj_union_pw_qp_copy, + isl_obj_union_pw_qp_add, + isl_obj_union_pw_qp_print, + isl_obj_union_pw_qp_free +}; + +static void *isl_obj_pw_qpf_copy(void *v) +{ + return isl_pw_qpolynomial_fold_copy((struct isl_pw_qpolynomial_fold *)v); +} + +static void isl_obj_pw_qpf_free(void *v) +{ + isl_pw_qpolynomial_fold_free((struct isl_pw_qpolynomial_fold *)v); +} + +static __isl_give isl_printer *isl_obj_pw_qpf_print(__isl_take isl_printer *p, + void *v) +{ + return isl_printer_print_pw_qpolynomial_fold(p, + (struct isl_pw_qpolynomial_fold *)v); +} + +static void *isl_obj_pw_qpf_add(void *v1, void *v2) +{ + return isl_pw_qpolynomial_fold_fold((struct isl_pw_qpolynomial_fold *)v1, + (struct isl_pw_qpolynomial_fold *)v2); +} + +struct isl_obj_vtable isl_obj_pw_qpolynomial_fold_vtable = { + isl_obj_pw_qpf_copy, + isl_obj_pw_qpf_add, + isl_obj_pw_qpf_print, + isl_obj_pw_qpf_free +}; + +static void *isl_obj_union_pw_qpf_copy(void *v) +{ + return isl_union_pw_qpolynomial_fold_copy((struct isl_union_pw_qpolynomial_fold *)v); +} + +static void isl_obj_union_pw_qpf_free(void *v) +{ + isl_union_pw_qpolynomial_fold_free((struct isl_union_pw_qpolynomial_fold *)v); +} + +static __isl_give isl_printer *isl_obj_union_pw_qpf_print( + __isl_take isl_printer *p, void *v) +{ + return isl_printer_print_union_pw_qpolynomial_fold(p, + (struct isl_union_pw_qpolynomial_fold *)v); +} + +static void *isl_obj_union_pw_qpf_add(void *v1, void *v2) +{ + return isl_union_pw_qpolynomial_fold_fold( + (struct isl_union_pw_qpolynomial_fold *)v1, + (struct isl_union_pw_qpolynomial_fold *)v2); +} + +struct isl_obj_vtable isl_obj_union_pw_qpolynomial_fold_vtable = { + isl_obj_union_pw_qpf_copy, + isl_obj_union_pw_qpf_add, + isl_obj_union_pw_qpf_print, + isl_obj_union_pw_qpf_free +}; + +static void *isl_obj_schedule_copy(void *v) +{ + return isl_schedule_copy((isl_schedule *) v); +} + +static void isl_obj_schedule_free(void *v) +{ + isl_schedule_free((isl_schedule *) v); +} + +static __isl_give isl_printer *isl_obj_schedule_print( + __isl_take isl_printer *p, void *v) +{ + return isl_printer_print_schedule(p, (isl_schedule *) v); +} + +struct isl_obj_vtable isl_obj_schedule_vtable = { + isl_obj_schedule_copy, + NULL, + isl_obj_schedule_print, + isl_obj_schedule_free +}; Index: lib/Analysis/isl/isl_options.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_options.c @@ -0,0 +1,370 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +struct isl_arg_choice isl_pip_context_choice[] = { + {"gbr", ISL_CONTEXT_GBR}, + {"lexmin", ISL_CONTEXT_LEXMIN}, + {0} +}; + +struct isl_arg_choice isl_gbr_choice[] = { + {"never", ISL_GBR_NEVER}, + {"once", ISL_GBR_ONCE}, + {"always", ISL_GBR_ALWAYS}, + {0} +}; + +struct isl_arg_choice isl_closure_choice[] = { + {"isl", ISL_CLOSURE_ISL}, + {"box", ISL_CLOSURE_BOX}, + {0} +}; + +static struct isl_arg_choice bound[] = { + {"bernstein", ISL_BOUND_BERNSTEIN}, + {"range", ISL_BOUND_RANGE}, + {0} +}; + +static struct isl_arg_choice on_error[] = { + {"warn", ISL_ON_ERROR_WARN}, + {"continue", ISL_ON_ERROR_CONTINUE}, + {"abort", ISL_ON_ERROR_ABORT}, + {0} +}; + +static struct isl_arg_choice isl_schedule_algorithm_choice[] = { + {"isl", ISL_SCHEDULE_ALGORITHM_ISL}, + {"feautrier", ISL_SCHEDULE_ALGORITHM_FEAUTRIER}, + {0} +}; + +static struct isl_arg_flags bernstein_recurse[] = { + {"none", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS, 0}, + {"factors", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS, + ISL_BERNSTEIN_FACTORS}, + {"intervals", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS, + ISL_BERNSTEIN_INTERVALS}, + {"full", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS, + ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS}, + {0} +}; + +static struct isl_arg_choice convex[] = { + {"wrap", ISL_CONVEX_HULL_WRAP}, + {"fm", ISL_CONVEX_HULL_FM}, + {0} +}; + +#define ISL_SCHEDULE_FUSE_MAX 0 +#define ISL_SCHEDULE_FUSE_MIN 1 + +static struct isl_arg_choice fuse[] = { + {"max", ISL_SCHEDULE_FUSE_MAX}, + {"min", ISL_SCHEDULE_FUSE_MIN}, + {0} +}; + +/* Callback for setting the "schedule-fuse" option. + * This (now hidden) option tries to mimic an option that was + * replaced by the schedule-serialize-sccs option. + * Setting the old option to ISL_SCHEDULE_FUSE_MIN is now + * expressed by turning on the schedule-serialize-sccs option. + */ +static int set_fuse(void *opt, unsigned val) +{ + struct isl_options *options = opt; + + options->schedule_serialize_sccs = (val == ISL_SCHEDULE_FUSE_MIN); + + return 0; +} + +static struct isl_arg_choice separation_bounds[] = { + {"explicit", ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT}, + {"implicit", ISL_AST_BUILD_SEPARATION_BOUNDS_IMPLICIT}, + {0} +}; + +static void print_version(void) +{ + printf("%s", isl_version()); +} + +ISL_ARGS_START(struct isl_options, isl_options_args) +ISL_ARG_CHOICE(struct isl_options, context, 0, "context", \ + isl_pip_context_choice, ISL_CONTEXT_GBR, + "how to handle the pip context tableau") +ISL_ARG_CHOICE(struct isl_options, gbr, 0, "gbr", \ + isl_gbr_choice, ISL_GBR_ALWAYS, + "how often to use generalized basis reduction") +ISL_ARG_CHOICE(struct isl_options, closure, 0, "closure", \ + isl_closure_choice, ISL_CLOSURE_ISL, + "closure operation to use") +ISL_ARG_BOOL(struct isl_options, gbr_only_first, 0, "gbr-only-first", 0, + "only perform basis reduction in first direction") +ISL_ARG_CHOICE(struct isl_options, bound, 0, "bound", bound, + ISL_BOUND_BERNSTEIN, "algorithm to use for computing bounds") +ISL_ARG_CHOICE(struct isl_options, on_error, 0, "on-error", on_error, + ISL_ON_ERROR_WARN, "how to react if an error is detected") +ISL_ARG_FLAGS(struct isl_options, bernstein_recurse, 0, + "bernstein-recurse", bernstein_recurse, ISL_BERNSTEIN_FACTORS, NULL) +ISL_ARG_BOOL(struct isl_options, bernstein_triangulate, 0, + "bernstein-triangulate", 1, + "triangulate domains during Bernstein expansion") +ISL_ARG_BOOL(struct isl_options, pip_symmetry, 0, "pip-symmetry", 1, + "detect simple symmetries in PIP input") +ISL_ARG_CHOICE(struct isl_options, convex, 0, "convex-hull", \ + convex, ISL_CONVEX_HULL_WRAP, "convex hull algorithm to use") +ISL_ARG_BOOL(struct isl_options, coalesce_bounded_wrapping, 0, + "coalesce-bounded-wrapping", 1, "bound wrapping during coalescing") +ISL_ARG_INT(struct isl_options, schedule_max_coefficient, 0, + "schedule-max-coefficient", "limit", -1, "Only consider schedules " + "where the coefficients of the variable and parameter dimensions " + "do not exceed . A value of -1 allows arbitrary coefficients.") +ISL_ARG_INT(struct isl_options, schedule_max_constant_term, 0, + "schedule-max-constant-term", "limit", -1, "Only consider schedules " + "where the coefficients of the constant dimension do not exceed " + ". A value of -1 allows arbitrary coefficients.") +ISL_ARG_BOOL(struct isl_options, schedule_parametric, 0, + "schedule-parametric", 1, "construct possibly parametric schedules") +ISL_ARG_BOOL(struct isl_options, schedule_outer_coincidence, 0, + "schedule-outer-coincidence", 0, + "try to construct schedules where the outer member of each band " + "satisfies the coincidence constraints") +ISL_ARG_BOOL(struct isl_options, schedule_maximize_band_depth, 0, + "schedule-maximize-band-depth", 0, + "maximize the number of scheduling dimensions in a band") +ISL_ARG_BOOL(struct isl_options, schedule_maximize_coincidence, 0, + "schedule-maximize-coincidence", 0, + "maximize the number of coincident dimensions in a band") +ISL_ARG_BOOL(struct isl_options, schedule_split_scaled, 0, + "schedule-split-scaled", 1, + "split non-tilable bands with scaled schedules") +ISL_ARG_BOOL(struct isl_options, schedule_treat_coalescing, 0, + "schedule-treat-coalescing", 1, + "try and prevent or adjust schedules that perform loop coalescing") +ISL_ARG_BOOL(struct isl_options, schedule_separate_components, 0, + "schedule-separate-components", 1, + "separate components in dependence graph") +ISL_ARG_BOOL(struct isl_options, schedule_whole_component, 0, + "schedule-whole-component", 1, + "try and compute schedule for entire component first") +ISL_ARG_CHOICE(struct isl_options, schedule_algorithm, 0, + "schedule-algorithm", isl_schedule_algorithm_choice, + ISL_SCHEDULE_ALGORITHM_ISL, "scheduling algorithm to use") +ISL_ARG_BOOL(struct isl_options, schedule_serialize_sccs, 0, + "schedule-serialize-sccs", 0, + "serialize strongly connected components in dependence graph") +ISL_ARG_PHANTOM_USER_CHOICE_F(0, "schedule-fuse", fuse, &set_fuse, + ISL_SCHEDULE_FUSE_MAX, "level of fusion during scheduling", + ISL_ARG_HIDDEN) +ISL_ARG_BOOL(struct isl_options, tile_scale_tile_loops, 0, + "tile-scale-tile-loops", 1, "scale tile loops") +ISL_ARG_BOOL(struct isl_options, tile_shift_point_loops, 0, + "tile-shift-point-loops", 1, "shift point loops to start at zero") +ISL_ARG_STR(struct isl_options, ast_iterator_type, 0, + "ast-iterator-type", "type", "int", + "type used for iterators during printing of AST") +ISL_ARG_BOOL(struct isl_options, ast_always_print_block, 0, + "ast-always-print-block", 0, "print for and if bodies as a block " + "regardless of the number of statements in the body") +ISL_ARG_BOOL(struct isl_options, ast_print_macro_once, 0, + "ast-print-macro-once", 0, "only print macro definitions once") +ISL_ARG_BOOL(struct isl_options, ast_build_atomic_upper_bound, 0, + "ast-build-atomic-upper-bound", 1, "generate atomic upper bounds") +ISL_ARG_BOOL(struct isl_options, ast_build_prefer_pdiv, 0, + "ast-build-prefer-pdiv", 1, "prefer pdiv operation over fdiv") +ISL_ARG_BOOL(struct isl_options, ast_build_detect_min_max, 0, + "ast-build-detect-min-max", 0, "detect min/max expressions") +ISL_ARG_BOOL(struct isl_options, ast_build_exploit_nested_bounds, 0, + "ast-build-exploit-nested-bounds", 1, + "simplify conditions based on bounds of nested for loops") +ISL_ARG_BOOL(struct isl_options, ast_build_group_coscheduled, 0, + "ast-build-group-coscheduled", 0, + "keep coscheduled domain elements together") +ISL_ARG_CHOICE(struct isl_options, ast_build_separation_bounds, 0, + "ast-build-separation-bounds", separation_bounds, + ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT, + "bounds to use during separation") +ISL_ARG_BOOL(struct isl_options, ast_build_scale_strides, 0, + "ast-build-scale-strides", 1, + "allow iterators of strided loops to be scaled down") +ISL_ARG_BOOL(struct isl_options, ast_build_allow_else, 0, + "ast-build-allow-else", 1, "generate if statements with else branches") +ISL_ARG_BOOL(struct isl_options, ast_build_allow_or, 0, + "ast-build-allow-or", 1, "generate if conditions with disjunctions") +ISL_ARG_BOOL(struct isl_options, print_stats, 0, "print-stats", 0, + "print statistics for every isl_ctx") +ISL_ARG_ULONG(struct isl_options, max_operations, 0, + "max-operations", 0, "default number of maximal operations per isl_ctx") +ISL_ARG_VERSION(print_version) +ISL_ARGS_END + +ISL_ARG_DEF(isl_options, struct isl_options, isl_options_args) + +ISL_ARG_CTX_DEF(isl_options, struct isl_options, isl_options_args) + +ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, bound) +ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, bound) + +ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, + on_error) +ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, + on_error) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + pip_symmetry) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + pip_symmetry) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + coalesce_bounded_wrapping) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + coalesce_bounded_wrapping) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + gbr_only_first) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + gbr_only_first) + +ISL_CTX_SET_INT_DEF(isl_options, struct isl_options, isl_options_args, + schedule_max_coefficient) +ISL_CTX_GET_INT_DEF(isl_options, struct isl_options, isl_options_args, + schedule_max_coefficient) + +ISL_CTX_SET_INT_DEF(isl_options, struct isl_options, isl_options_args, + schedule_max_constant_term) +ISL_CTX_GET_INT_DEF(isl_options, struct isl_options, isl_options_args, + schedule_max_constant_term) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_maximize_band_depth) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_maximize_band_depth) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_maximize_coincidence) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_maximize_coincidence) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_split_scaled) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_split_scaled) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_treat_coalescing) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_treat_coalescing) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_separate_components) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_separate_components) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_whole_component) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_whole_component) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_outer_coincidence) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_outer_coincidence) + +ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, + schedule_algorithm) +ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, + schedule_algorithm) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_serialize_sccs) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + schedule_serialize_sccs) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + tile_scale_tile_loops) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + tile_scale_tile_loops) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + tile_shift_point_loops) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + tile_shift_point_loops) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_atomic_upper_bound) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_atomic_upper_bound) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_prefer_pdiv) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_prefer_pdiv) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_detect_min_max) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_detect_min_max) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_exploit_nested_bounds) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_exploit_nested_bounds) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_group_coscheduled) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_group_coscheduled) + +ISL_CTX_SET_STR_DEF(isl_options, struct isl_options, isl_options_args, + ast_iterator_type) +ISL_CTX_GET_STR_DEF(isl_options, struct isl_options, isl_options_args, + ast_iterator_type) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_always_print_block) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_always_print_block) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_print_macro_once) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_print_macro_once) + +ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_separation_bounds) +ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_separation_bounds) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_scale_strides) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_scale_strides) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_allow_else) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_allow_else) + +ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_allow_or) +ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args, + ast_build_allow_or) Index: lib/Analysis/isl/isl_options_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_options_private.h @@ -0,0 +1,72 @@ +#ifndef ISL_OPTIONS_PRIVATE_H +#define ISL_OPTIONS_PRIVATE_H + +#include + +struct isl_options { + #define ISL_CONTEXT_GBR 0 + #define ISL_CONTEXT_LEXMIN 1 + unsigned context; + + #define ISL_GBR_NEVER 0 + #define ISL_GBR_ONCE 1 + #define ISL_GBR_ALWAYS 2 + unsigned gbr; + unsigned gbr_only_first; + + #define ISL_CLOSURE_ISL 0 + #define ISL_CLOSURE_BOX 1 + unsigned closure; + + int bound; + unsigned on_error; + + #define ISL_BERNSTEIN_FACTORS 1 + #define ISL_BERNSTEIN_INTERVALS 2 + int bernstein_recurse; + + int bernstein_triangulate; + + int pip_symmetry; + + #define ISL_CONVEX_HULL_WRAP 0 + #define ISL_CONVEX_HULL_FM 1 + int convex; + + int coalesce_bounded_wrapping; + + int schedule_max_coefficient; + int schedule_max_constant_term; + int schedule_parametric; + int schedule_outer_coincidence; + int schedule_maximize_band_depth; + int schedule_maximize_coincidence; + int schedule_split_scaled; + int schedule_treat_coalescing; + int schedule_separate_components; + int schedule_whole_component; + unsigned schedule_algorithm; + int schedule_serialize_sccs; + + int tile_scale_tile_loops; + int tile_shift_point_loops; + + char *ast_iterator_type; + int ast_always_print_block; + int ast_print_macro_once; + + int ast_build_atomic_upper_bound; + int ast_build_prefer_pdiv; + int ast_build_detect_min_max; + int ast_build_exploit_nested_bounds; + int ast_build_group_coscheduled; + int ast_build_separation_bounds; + int ast_build_scale_strides; + int ast_build_allow_else; + int ast_build_allow_or; + + int print_stats; + unsigned long max_operations; +}; + +#endif Index: lib/Analysis/isl/isl_output.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_output.c @@ -0,0 +1,3086 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012-2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *s_to[2] = { " -> ", " \\to " }; +static const char *s_and[2] = { " and ", " \\wedge " }; +static const char *s_or[2] = { " or ", " \\vee " }; +static const char *s_le[2] = { "<=", "\\le" }; +static const char *s_ge[2] = { ">=", "\\ge" }; +static const char *s_open_set[2] = { "{ ", "\\{\\, " }; +static const char *s_close_set[2] = { " }", " \\,\\}" }; +static const char *s_open_list[2] = { "[", "(" }; +static const char *s_close_list[2] = { "]", ")" }; +static const char *s_such_that[2] = { " : ", " \\mid " }; +static const char *s_open_exists[2] = { "exists (", "\\exists \\, " }; +static const char *s_close_exists[2] = { ")", "" }; +static const char *s_div_prefix[2] = { "e", "\\alpha_" }; +static const char *s_param_prefix[2] = { "p", "p_" }; +static const char *s_input_prefix[2] = { "i", "i_" }; +static const char *s_output_prefix[2] = { "o", "o_" }; + +static __isl_give isl_printer *print_constraint_polylib( + struct isl_basic_map *bmap, int ineq, int n, __isl_take isl_printer *p) +{ + int i; + unsigned n_in = isl_basic_map_dim(bmap, isl_dim_in); + unsigned n_out = isl_basic_map_dim(bmap, isl_dim_out); + unsigned nparam = isl_basic_map_dim(bmap, isl_dim_param); + isl_int *c = ineq ? bmap->ineq[n] : bmap->eq[n]; + + p = isl_printer_start_line(p); + p = isl_printer_print_int(p, ineq); + for (i = 0; i < n_out; ++i) { + p = isl_printer_print_str(p, " "); + p = isl_printer_print_isl_int(p, c[1+nparam+n_in+i]); + } + for (i = 0; i < n_in; ++i) { + p = isl_printer_print_str(p, " "); + p = isl_printer_print_isl_int(p, c[1+nparam+i]); + } + for (i = 0; i < bmap->n_div; ++i) { + p = isl_printer_print_str(p, " "); + p = isl_printer_print_isl_int(p, c[1+nparam+n_in+n_out+i]); + } + for (i = 0; i < nparam; ++i) { + p = isl_printer_print_str(p, " "); + p = isl_printer_print_isl_int(p, c[1+i]); + } + p = isl_printer_print_str(p, " "); + p = isl_printer_print_isl_int(p, c[0]); + p = isl_printer_end_line(p); + return p; +} + +static __isl_give isl_printer *print_constraints_polylib( + struct isl_basic_map *bmap, __isl_take isl_printer *p) +{ + int i; + + p = isl_printer_set_isl_int_width(p, 5); + + for (i = 0; i < bmap->n_eq; ++i) + p = print_constraint_polylib(bmap, 0, i, p); + for (i = 0; i < bmap->n_ineq; ++i) + p = print_constraint_polylib(bmap, 1, i, p); + + return p; +} + +static __isl_give isl_printer *bset_print_constraints_polylib( + struct isl_basic_set *bset, __isl_take isl_printer *p) +{ + return print_constraints_polylib((struct isl_basic_map *)bset, p); +} + +static __isl_give isl_printer *isl_basic_map_print_polylib( + __isl_keep isl_basic_map *bmap, __isl_take isl_printer *p, int ext) +{ + unsigned total = isl_basic_map_total_dim(bmap); + p = isl_printer_start_line(p); + p = isl_printer_print_int(p, bmap->n_eq + bmap->n_ineq); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_int(p, 1 + total + 1); + if (ext) { + p = isl_printer_print_str(p, " "); + p = isl_printer_print_int(p, + isl_basic_map_dim(bmap, isl_dim_out)); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_int(p, + isl_basic_map_dim(bmap, isl_dim_in)); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_int(p, + isl_basic_map_dim(bmap, isl_dim_div)); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_int(p, + isl_basic_map_dim(bmap, isl_dim_param)); + } + p = isl_printer_end_line(p); + return print_constraints_polylib(bmap, p); +} + +static __isl_give isl_printer *isl_basic_set_print_polylib( + __isl_keep isl_basic_set *bset, __isl_take isl_printer *p, int ext) +{ + return isl_basic_map_print_polylib((struct isl_basic_map *)bset, p, ext); +} + +static __isl_give isl_printer *isl_map_print_polylib(__isl_keep isl_map *map, + __isl_take isl_printer *p, int ext) +{ + int i; + + p = isl_printer_start_line(p); + p = isl_printer_print_int(p, map->n); + p = isl_printer_end_line(p); + for (i = 0; i < map->n; ++i) { + p = isl_printer_start_line(p); + p = isl_printer_end_line(p); + p = isl_basic_map_print_polylib(map->p[i], p, ext); + } + return p; +} + +static __isl_give isl_printer *isl_set_print_polylib(__isl_keep isl_set *set, + __isl_take isl_printer *p, int ext) +{ + return isl_map_print_polylib((struct isl_map *)set, p, ext); +} + +static int count_same_name(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos, const char *name) +{ + enum isl_dim_type t; + unsigned p, s; + int count = 0; + + for (t = isl_dim_param; t <= type && t <= isl_dim_out; ++t) { + s = t == type ? pos : isl_space_dim(dim, t); + for (p = 0; p < s; ++p) { + const char *n = isl_space_get_dim_name(dim, t, p); + if (n && !strcmp(n, name)) + count++; + } + } + return count; +} + +/* Print the name of the variable of type "type" and position "pos" + * in "space" to "p". + */ +static __isl_give isl_printer *print_name(__isl_keep isl_space *space, + __isl_take isl_printer *p, enum isl_dim_type type, unsigned pos, + int latex) +{ + const char *name; + char buffer[20]; + int primes; + + name = type == isl_dim_div ? NULL + : isl_space_get_dim_name(space, type, pos); + + if (!name) { + const char *prefix; + if (type == isl_dim_param) + prefix = s_param_prefix[latex]; + else if (type == isl_dim_div) + prefix = s_div_prefix[latex]; + else if (isl_space_is_set(space) || type == isl_dim_in) + prefix = s_input_prefix[latex]; + else + prefix = s_output_prefix[latex]; + snprintf(buffer, sizeof(buffer), "%s%d", prefix, pos); + name = buffer; + } + primes = count_same_name(space, name == buffer ? isl_dim_div : type, + pos, name); + p = isl_printer_print_str(p, name); + while (primes-- > 0) + p = isl_printer_print_str(p, "'"); + return p; +} + +static enum isl_dim_type pos2type(__isl_keep isl_space *dim, unsigned *pos) +{ + enum isl_dim_type type; + unsigned n_in = isl_space_dim(dim, isl_dim_in); + unsigned n_out = isl_space_dim(dim, isl_dim_out); + unsigned nparam = isl_space_dim(dim, isl_dim_param); + + if (*pos < 1 + nparam) { + type = isl_dim_param; + *pos -= 1; + } else if (*pos < 1 + nparam + n_in) { + type = isl_dim_in; + *pos -= 1 + nparam; + } else if (*pos < 1 + nparam + n_in + n_out) { + type = isl_dim_out; + *pos -= 1 + nparam + n_in; + } else { + type = isl_dim_div; + *pos -= 1 + nparam + n_in + n_out; + } + + return type; +} + +/* Can the div expression of the integer division at position "row" of "div" + * be printed? + * In particular, are the div expressions available and does the selected + * variable have a known explicit representation? + * Furthermore, the Omega format does not allow any div expressions + * to be printed. + */ +static isl_bool can_print_div_expr(__isl_keep isl_printer *p, + __isl_keep isl_mat *div, int pos) +{ + if (p->output_format == ISL_FORMAT_OMEGA) + return isl_bool_false; + if (!div) + return isl_bool_false; + return !isl_int_is_zero(div->row[pos][0]); +} + +static __isl_give isl_printer *print_div(__isl_keep isl_space *dim, + __isl_keep isl_mat *div, int pos, __isl_take isl_printer *p); + +static __isl_give isl_printer *print_term(__isl_keep isl_space *space, + __isl_keep isl_mat *div, + isl_int c, unsigned pos, __isl_take isl_printer *p, int latex) +{ + enum isl_dim_type type; + int print_div_def; + + if (pos == 0) + return isl_printer_print_isl_int(p, c); + + type = pos2type(space, &pos); + print_div_def = type == isl_dim_div && can_print_div_expr(p, div, pos); + + if (isl_int_is_one(c)) + ; + else if (isl_int_is_negone(c)) + p = isl_printer_print_str(p, "-"); + else { + p = isl_printer_print_isl_int(p, c); + if (p->output_format == ISL_FORMAT_C || print_div_def) + p = isl_printer_print_str(p, "*"); + } + if (print_div_def) + p = print_div(space, div, pos, p); + else + p = print_name(space, p, type, pos, latex); + return p; +} + +static __isl_give isl_printer *print_affine_of_len(__isl_keep isl_space *dim, + __isl_keep isl_mat *div, + __isl_take isl_printer *p, isl_int *c, int len) +{ + int i; + int first; + + for (i = 0, first = 1; i < len; ++i) { + int flip = 0; + if (isl_int_is_zero(c[i])) + continue; + if (!first) { + if (isl_int_is_neg(c[i])) { + flip = 1; + isl_int_neg(c[i], c[i]); + p = isl_printer_print_str(p, " - "); + } else + p = isl_printer_print_str(p, " + "); + } + first = 0; + p = print_term(dim, div, c[i], i, p, 0); + if (flip) + isl_int_neg(c[i], c[i]); + } + if (first) + p = isl_printer_print_str(p, "0"); + return p; +} + +/* Print an affine expression "c" corresponding to a constraint in "bmap" + * to "p", with the variable names taken from "space" and + * the integer division definitions taken from "div". + */ +static __isl_give isl_printer *print_affine(__isl_keep isl_basic_map *bmap, + __isl_keep isl_space *space, __isl_keep isl_mat *div, + __isl_take isl_printer *p, isl_int *c) +{ + unsigned len = 1 + isl_basic_map_total_dim(bmap); + return print_affine_of_len(space, div, p, c, len); +} + +/* offset is the offset of local_dim inside data->type of data->space. + */ +static __isl_give isl_printer *print_nested_var_list(__isl_take isl_printer *p, + __isl_keep isl_space *local_dim, enum isl_dim_type local_type, + struct isl_print_space_data *data, int offset) +{ + int i; + + if (data->space != local_dim && local_type == isl_dim_out) + offset += local_dim->n_in; + + for (i = 0; i < isl_space_dim(local_dim, local_type); ++i) { + if (i) + p = isl_printer_print_str(p, ", "); + if (data->print_dim) + p = data->print_dim(p, data, offset + i); + else + p = print_name(data->space, p, data->type, offset + i, + data->latex); + } + return p; +} + +static __isl_give isl_printer *print_var_list(__isl_take isl_printer *p, + __isl_keep isl_space *space, enum isl_dim_type type) +{ + struct isl_print_space_data data = { .space = space, .type = type }; + + return print_nested_var_list(p, space, type, &data, 0); +} + +static __isl_give isl_printer *print_nested_map_dim(__isl_take isl_printer *p, + __isl_keep isl_space *local_dim, + struct isl_print_space_data *data, int offset); + +static __isl_give isl_printer *print_nested_tuple(__isl_take isl_printer *p, + __isl_keep isl_space *local_dim, enum isl_dim_type local_type, + struct isl_print_space_data *data, int offset) +{ + const char *name = NULL; + unsigned n = isl_space_dim(local_dim, local_type); + if ((local_type == isl_dim_in || local_type == isl_dim_out)) { + name = isl_space_get_tuple_name(local_dim, local_type); + if (name) { + if (data->latex) + p = isl_printer_print_str(p, "\\mathrm{"); + p = isl_printer_print_str(p, name); + if (data->latex) + p = isl_printer_print_str(p, "}"); + } + } + if (!data->latex || n != 1 || name) + p = isl_printer_print_str(p, s_open_list[data->latex]); + if ((local_type == isl_dim_in || local_type == isl_dim_out) && + local_dim->nested[local_type - isl_dim_in]) { + if (data->space != local_dim && local_type == isl_dim_out) + offset += local_dim->n_in; + p = print_nested_map_dim(p, + local_dim->nested[local_type - isl_dim_in], + data, offset); + } else + p = print_nested_var_list(p, local_dim, local_type, data, + offset); + if (!data->latex || n != 1 || name) + p = isl_printer_print_str(p, s_close_list[data->latex]); + return p; +} + +static __isl_give isl_printer *print_tuple(__isl_keep isl_space *dim, + __isl_take isl_printer *p, enum isl_dim_type type, + struct isl_print_space_data *data) +{ + data->space = dim; + data->type = type; + return print_nested_tuple(p, dim, type, data, 0); +} + +static __isl_give isl_printer *print_nested_map_dim(__isl_take isl_printer *p, + __isl_keep isl_space *local_dim, + struct isl_print_space_data *data, int offset) +{ + p = print_nested_tuple(p, local_dim, isl_dim_in, data, offset); + p = isl_printer_print_str(p, s_to[data->latex]); + p = print_nested_tuple(p, local_dim, isl_dim_out, data, offset); + + return p; +} + +__isl_give isl_printer *isl_print_space(__isl_keep isl_space *space, + __isl_take isl_printer *p, int rational, + struct isl_print_space_data *data) +{ + if (rational && !data->latex) + p = isl_printer_print_str(p, "rat: "); + if (isl_space_is_params(space)) + ; + else if (isl_space_is_set(space)) + p = print_tuple(space, p, isl_dim_set, data); + else { + p = print_tuple(space, p, isl_dim_in, data); + p = isl_printer_print_str(p, s_to[data->latex]); + p = print_tuple(space, p, isl_dim_out, data); + } + + return p; +} + +static __isl_give isl_printer *print_omega_parameters(__isl_keep isl_space *dim, + __isl_take isl_printer *p) +{ + if (isl_space_dim(dim, isl_dim_param) == 0) + return p; + + p = isl_printer_start_line(p); + p = isl_printer_print_str(p, "symbolic "); + p = print_var_list(p, dim, isl_dim_param); + p = isl_printer_print_str(p, ";"); + p = isl_printer_end_line(p); + return p; +} + +/* Does the inequality constraint following "i" in "bmap" + * have an opposite value for the same last coefficient? + * "last" is the position of the last coefficient of inequality "i". + * If the next constraint is a div constraint, then it is ignored + * since div constraints are not printed. + */ +static int next_is_opposite(__isl_keep isl_basic_map *bmap, int i, int last) +{ + unsigned total = isl_basic_map_total_dim(bmap); + unsigned o_div = isl_basic_map_offset(bmap, isl_dim_div); + + if (i + 1 >= bmap->n_ineq) + return 0; + if (isl_seq_last_non_zero(bmap->ineq[i + 1], 1 + total) != last) + return 0; + if (last >= o_div && + isl_basic_map_is_div_constraint(bmap, bmap->ineq[i + 1], + last - o_div)) + return 0; + return isl_int_abs_eq(bmap->ineq[i][last], bmap->ineq[i + 1][last]) && + !isl_int_eq(bmap->ineq[i][last], bmap->ineq[i + 1][last]); +} + +/* Return a string representation of the operator used when + * printing a constraint where the LHS is greater than or equal to the LHS + * (sign > 0) or smaller than or equal to the LHS (sign < 0). + * If "strict" is set, then return the strict version of the comparison + * operator. + */ +static const char *constraint_op(int sign, int strict, int latex) +{ + if (strict) + return sign < 0 ? "<" : ">"; + if (sign < 0) + return s_le[latex]; + else + return s_ge[latex]; +} + +/* Print one side of a constraint "c" from "bmap" to "p", with + * the variable names taken from "space" and the integer division definitions + * taken from "div". + * "last" is the position of the last non-zero coefficient. + * Let c' be the result of zeroing out this coefficient, then + * the partial constraint + * + * c' op + * + * is printed. + * "first_constraint" is set if this is the first constraint + * in the conjunction. + */ +static __isl_give isl_printer *print_half_constraint(struct isl_basic_map *bmap, + __isl_keep isl_space *space, __isl_keep isl_mat *div, + __isl_take isl_printer *p, isl_int *c, int last, const char *op, + int first_constraint, int latex) +{ + if (!first_constraint) + p = isl_printer_print_str(p, s_and[latex]); + + isl_int_set_si(c[last], 0); + p = print_affine(bmap, space, div, p, c); + + p = isl_printer_print_str(p, " "); + p = isl_printer_print_str(p, op); + p = isl_printer_print_str(p, " "); + + return p; +} + +/* Print a constraint "c" from "bmap" to "p", with the variable names + * taken from "space" and the integer division definitions taken from "div". + * "last" is the position of the last non-zero coefficient, which is + * moreover assumed to be negative. + * Let c' be the result of zeroing out this coefficient, then + * the constraint is printed in the form + * + * -c[last] op c' + * + * "first_constraint" is set if this is the first constraint + * in the conjunction. + */ +static __isl_give isl_printer *print_constraint(struct isl_basic_map *bmap, + __isl_keep isl_space *space, __isl_keep isl_mat *div, + __isl_take isl_printer *p, + isl_int *c, int last, const char *op, int first_constraint, int latex) +{ + if (!first_constraint) + p = isl_printer_print_str(p, s_and[latex]); + + isl_int_abs(c[last], c[last]); + + p = print_term(space, div, c[last], last, p, latex); + + p = isl_printer_print_str(p, " "); + p = isl_printer_print_str(p, op); + p = isl_printer_print_str(p, " "); + + isl_int_set_si(c[last], 0); + p = print_affine(bmap, space, div, p, c); + + return p; +} + +/* Print the constraints of "bmap" to "p". + * The names of the variables are taken from "space" and + * the integer division definitions are taken from "div". + * Div constraints are only printed in "dump" mode. + * The constraints are sorted prior to printing (except in "dump" mode). + * + * If x is the last variable with a non-zero coefficient, + * then a lower bound + * + * f - a x >= 0 + * + * is printed as + * + * a x <= f + * + * while an upper bound + * + * f + a x >= 0 + * + * is printed as + * + * a x >= -f + * + * If the next constraint has an opposite sign for the same last coefficient, + * then it is printed as + * + * f >= a x + * + * or + * + * -f <= a x + * + * instead. In fact, the "a x" part is not printed explicitly, but + * reused from the next constraint, which is therefore treated as + * a first constraint in the conjunction. + * + * If the constant term of "f" is -1, then "f" is replaced by "f + 1" and + * the comparison operator is replaced by the strict variant. + * Essentially, ">= 1" is replaced by "> 0". + */ +static __isl_give isl_printer *print_constraints(__isl_keep isl_basic_map *bmap, + __isl_keep isl_space *space, __isl_keep isl_mat *div, + __isl_take isl_printer *p, int latex) +{ + int i; + isl_vec *c = NULL; + int rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + unsigned total = isl_basic_map_total_dim(bmap); + unsigned o_div = isl_basic_map_offset(bmap, isl_dim_div); + int first = 1; + + bmap = isl_basic_map_copy(bmap); + if (!p->dump) + bmap = isl_basic_map_sort_constraints(bmap); + if (!bmap) + goto error; + + c = isl_vec_alloc(bmap->ctx, 1 + total); + if (!c) + goto error; + + for (i = bmap->n_eq - 1; i >= 0; --i) { + int l = isl_seq_last_non_zero(bmap->eq[i], 1 + total); + if (l < 0) { + if (i != bmap->n_eq - 1) + p = isl_printer_print_str(p, s_and[latex]); + p = isl_printer_print_str(p, "0 = 0"); + continue; + } + if (isl_int_is_neg(bmap->eq[i][l])) + isl_seq_cpy(c->el, bmap->eq[i], 1 + total); + else + isl_seq_neg(c->el, bmap->eq[i], 1 + total); + p = print_constraint(bmap, space, div, p, c->el, l, + "=", first, latex); + first = 0; + } + for (i = 0; i < bmap->n_ineq; ++i) { + int l = isl_seq_last_non_zero(bmap->ineq[i], 1 + total); + int strict; + int s; + const char *op; + if (l < 0) + continue; + if (!p->dump && l >= o_div && + can_print_div_expr(p, div, l - o_div) && + isl_basic_map_is_div_constraint(bmap, bmap->ineq[i], + l - o_div)) + continue; + s = isl_int_sgn(bmap->ineq[i][l]); + strict = !rational && isl_int_is_negone(bmap->ineq[i][0]); + if (s < 0) + isl_seq_cpy(c->el, bmap->ineq[i], 1 + total); + else + isl_seq_neg(c->el, bmap->ineq[i], 1 + total); + if (strict) + isl_int_set_si(c->el[0], 0); + if (!p->dump && next_is_opposite(bmap, i, l)) { + op = constraint_op(-s, strict, latex); + p = print_half_constraint(bmap, space, div, p, c->el, l, + op, first, latex); + first = 1; + } else { + op = constraint_op(s, strict, latex); + p = print_constraint(bmap, space, div, p, c->el, l, + op, first, latex); + first = 0; + } + } + + isl_basic_map_free(bmap); + isl_vec_free(c); + + return p; +error: + isl_basic_map_free(bmap); + isl_vec_free(c); + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_div(__isl_keep isl_space *dim, + __isl_keep isl_mat *div, int pos, __isl_take isl_printer *p) +{ + int c; + + if (!p || !div) + return isl_printer_free(p); + + c = p->output_format == ISL_FORMAT_C; + p = isl_printer_print_str(p, c ? "floord(" : "floor(("); + p = print_affine_of_len(dim, div, p, + div->row[pos] + 1, div->n_col - 1); + p = isl_printer_print_str(p, c ? ", " : ")/"); + p = isl_printer_print_isl_int(p, div->row[pos][0]); + p = isl_printer_print_str(p, ")"); + return p; +} + +/* Print a comma separated list of div names, except those that have + * a definition that can be printed. + * If "print_defined_divs" is set, then those div names are printed + * as well, along with their definitions. + */ +static __isl_give isl_printer *print_div_list(__isl_take isl_printer *p, + __isl_keep isl_space *space, __isl_keep isl_mat *div, int latex, + int print_defined_divs) +{ + int i; + int first = 1; + unsigned n_div; + + if (!p || !space || !div) + return isl_printer_free(p); + + n_div = isl_mat_rows(div); + + for (i = 0; i < n_div; ++i) { + if (!print_defined_divs && can_print_div_expr(p, div, i)) + continue; + if (!first) + p = isl_printer_print_str(p, ", "); + p = print_name(space, p, isl_dim_div, i, latex); + first = 0; + if (!can_print_div_expr(p, div, i)) + continue; + p = isl_printer_print_str(p, " = "); + p = print_div(space, div, i, p); + } + + return p; +} + +/* Does printing "bmap" require an "exists" clause? + * That is, are there any local variables without an explicit representation? + */ +static isl_bool need_exists(__isl_keep isl_printer *p, + __isl_keep isl_basic_map *bmap, __isl_keep isl_mat *div) +{ + int i; + + if (!p || !bmap) + return isl_bool_error; + if (bmap->n_div == 0) + return isl_bool_false; + for (i = 0; i < bmap->n_div; ++i) + if (!can_print_div_expr(p, div, i)) + return isl_bool_true; + return isl_bool_false; +} + +/* Print the constraints of "bmap" to "p". + * The names of the variables are taken from "space". + * "latex" is set if the constraints should be printed in LaTeX format. + * Do not print inline explicit div representations in "dump" mode. + */ +static __isl_give isl_printer *print_disjunct(__isl_keep isl_basic_map *bmap, + __isl_keep isl_space *space, __isl_take isl_printer *p, int latex) +{ + isl_mat *div; + isl_bool exists; + + if (!p) + return NULL; + div = isl_basic_map_get_divs(bmap); + if (p->dump) + exists = bmap->n_div > 0; + else + exists = need_exists(p, bmap, div); + if (exists >= 0 && exists) { + p = isl_printer_print_str(p, s_open_exists[latex]); + p = print_div_list(p, space, div, latex, p->dump); + p = isl_printer_print_str(p, ": "); + } + + if (p->dump) + div = isl_mat_free(div); + p = print_constraints(bmap, space, div, p, latex); + isl_mat_free(div); + + if (exists >= 0 && exists) + p = isl_printer_print_str(p, s_close_exists[latex]); + return p; +} + +/* Print a colon followed by the constraints of "bmap" + * to "p", provided there are any constraints. + * The names of the variables are taken from "space". + * "latex" is set if the constraints should be printed in LaTeX format. + */ +static __isl_give isl_printer *print_optional_disjunct( + __isl_keep isl_basic_map *bmap, __isl_keep isl_space *space, + __isl_take isl_printer *p, int latex) +{ + if (isl_basic_map_plain_is_universe(bmap)) + return p; + + p = isl_printer_print_str(p, ": "); + p = print_disjunct(bmap, space, p, latex); + + return p; +} + +static __isl_give isl_printer *basic_map_print_omega( + __isl_keep isl_basic_map *bmap, __isl_take isl_printer *p) +{ + p = isl_printer_print_str(p, "{ ["); + p = print_var_list(p, bmap->dim, isl_dim_in); + p = isl_printer_print_str(p, "] -> ["); + p = print_var_list(p, bmap->dim, isl_dim_out); + p = isl_printer_print_str(p, "] "); + p = print_optional_disjunct(bmap, bmap->dim, p, 0); + p = isl_printer_print_str(p, " }"); + return p; +} + +static __isl_give isl_printer *basic_set_print_omega( + __isl_keep isl_basic_set *bset, __isl_take isl_printer *p) +{ + p = isl_printer_print_str(p, "{ ["); + p = print_var_list(p, bset->dim, isl_dim_set); + p = isl_printer_print_str(p, "] "); + p = print_optional_disjunct(bset, bset->dim, p, 0); + p = isl_printer_print_str(p, " }"); + return p; +} + +static __isl_give isl_printer *isl_map_print_omega(__isl_keep isl_map *map, + __isl_take isl_printer *p) +{ + int i; + + for (i = 0; i < map->n; ++i) { + if (i) + p = isl_printer_print_str(p, " union "); + p = basic_map_print_omega(map->p[i], p); + } + return p; +} + +static __isl_give isl_printer *isl_set_print_omega(__isl_keep isl_set *set, + __isl_take isl_printer *p) +{ + int i; + + for (i = 0; i < set->n; ++i) { + if (i) + p = isl_printer_print_str(p, " union "); + p = basic_set_print_omega(set->p[i], p); + } + return p; +} + +static __isl_give isl_printer *isl_basic_map_print_isl( + __isl_keep isl_basic_map *bmap, __isl_take isl_printer *p, + int latex) +{ + struct isl_print_space_data data = { .latex = latex }; + int rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + + if (isl_basic_map_dim(bmap, isl_dim_param) > 0) { + p = print_tuple(bmap->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + p = isl_print_space(bmap->dim, p, rational, &data); + p = isl_printer_print_str(p, " : "); + p = print_disjunct(bmap, bmap->dim, p, latex); + p = isl_printer_print_str(p, " }"); + return p; +} + +/* Print the disjuncts of a map (or set) "map" to "p". + * The names of the variables are taken from "space". + * "latex" is set if the constraints should be printed in LaTeX format. + */ +static __isl_give isl_printer *print_disjuncts_core(__isl_keep isl_map *map, + __isl_keep isl_space *space, __isl_take isl_printer *p, int latex) +{ + int i; + + if (map->n == 0) + p = isl_printer_print_str(p, "1 = 0"); + for (i = 0; i < map->n; ++i) { + if (i) + p = isl_printer_print_str(p, s_or[latex]); + if (map->n > 1 && map->p[i]->n_eq + map->p[i]->n_ineq > 1) + p = isl_printer_print_str(p, "("); + p = print_disjunct(map->p[i], space, p, latex); + if (map->n > 1 && map->p[i]->n_eq + map->p[i]->n_ineq > 1) + p = isl_printer_print_str(p, ")"); + } + return p; +} + +/* Print the disjuncts of a map (or set) "map" to "p". + * The names of the variables are taken from "space". + * "hull" describes constraints shared by all disjuncts of "map". + * "latex" is set if the constraints should be printed in LaTeX format. + * + * Print the disjuncts as a conjunction of "hull" and + * the result of removing the constraints of "hull" from "map". + * If this result turns out to be the universe, then simply print "hull". + */ +static __isl_give isl_printer *print_disjuncts_in_hull(__isl_keep isl_map *map, + __isl_keep isl_space *space, __isl_take isl_basic_map *hull, + __isl_take isl_printer *p, int latex) +{ + isl_bool is_universe; + + p = print_disjunct(hull, space, p, latex); + map = isl_map_plain_gist_basic_map(isl_map_copy(map), hull); + is_universe = isl_map_plain_is_universe(map); + if (is_universe < 0) + goto error; + if (!is_universe) { + p = isl_printer_print_str(p, s_and[latex]); + p = isl_printer_print_str(p, "("); + p = print_disjuncts_core(map, space, p, latex); + p = isl_printer_print_str(p, ")"); + } + isl_map_free(map); + + return p; +error: + isl_map_free(map); + isl_printer_free(p); + return NULL; +} + +/* Print the disjuncts of a map (or set) "map" to "p". + * The names of the variables are taken from "space". + * "latex" is set if the constraints should be printed in LaTeX format. + * + * If there are at least two disjuncts and "dump" mode is not turned out, + * check for any shared constraints among all disjuncts. + * If there are any, then print them separately in print_disjuncts_in_hull. + */ +static __isl_give isl_printer *print_disjuncts(__isl_keep isl_map *map, + __isl_keep isl_space *space, __isl_take isl_printer *p, int latex) +{ + if (isl_map_plain_is_universe(map)) + return p; + + p = isl_printer_print_str(p, s_such_that[latex]); + if (!p) + return NULL; + + if (!p->dump && map->n >= 2) { + isl_basic_map *hull; + isl_bool is_universe; + + hull = isl_map_plain_unshifted_simple_hull(isl_map_copy(map)); + is_universe = isl_basic_map_plain_is_universe(hull); + if (is_universe < 0) + p = isl_printer_free(p); + else if (!is_universe) + return print_disjuncts_in_hull(map, space, hull, + p, latex); + isl_basic_map_free(hull); + } + + return print_disjuncts_core(map, space, p, latex); +} + +/* Print the disjuncts of a map (or set). + * The names of the variables are taken from "space". + * "latex" is set if the constraints should be printed in LaTeX format. + * + * If the map turns out to be a universal parameter domain, then + * we need to print the colon. Otherwise, the output looks identical + * to the empty set. + */ +static __isl_give isl_printer *print_disjuncts_map(__isl_keep isl_map *map, + __isl_keep isl_space *space, __isl_take isl_printer *p, int latex) +{ + if (isl_map_plain_is_universe(map) && isl_space_is_params(map->dim)) + return isl_printer_print_str(p, s_such_that[latex]); + else + return print_disjuncts(map, space, p, latex); +} + +struct isl_aff_split { + isl_basic_map *aff; + isl_map *map; +}; + +static void free_split(__isl_take struct isl_aff_split *split, int n) +{ + int i; + + if (!split) + return; + + for (i = 0; i < n; ++i) { + isl_basic_map_free(split[i].aff); + isl_map_free(split[i].map); + } + + free(split); +} + +static __isl_give isl_basic_map *get_aff(__isl_take isl_basic_map *bmap) +{ + int i, j; + unsigned nparam, n_in, n_out, total; + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + return NULL; + if (isl_basic_map_free_inequality(bmap, bmap->n_ineq) < 0) + goto error; + + nparam = isl_basic_map_dim(bmap, isl_dim_param); + n_in = isl_basic_map_dim(bmap, isl_dim_in); + n_out = isl_basic_map_dim(bmap, isl_dim_out); + total = isl_basic_map_dim(bmap, isl_dim_all); + for (i = bmap->n_eq - 1; i >= 0; --i) { + j = isl_seq_last_non_zero(bmap->eq[i] + 1, total); + if (j >= nparam && j < nparam + n_in + n_out && + (isl_int_is_one(bmap->eq[i][1 + j]) || + isl_int_is_negone(bmap->eq[i][1 + j]))) + continue; + if (isl_basic_map_drop_equality(bmap, i) < 0) + goto error; + } + + bmap = isl_basic_map_finalize(bmap); + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +static int aff_split_cmp(const void *p1, const void *p2, void *user) +{ + const struct isl_aff_split *s1, *s2; + s1 = (const struct isl_aff_split *) p1; + s2 = (const struct isl_aff_split *) p2; + + return isl_basic_map_plain_cmp(s1->aff, s2->aff); +} + +static __isl_give isl_basic_map *drop_aff(__isl_take isl_basic_map *bmap, + __isl_keep isl_basic_map *aff) +{ + int i, j; + unsigned total; + + if (!bmap || !aff) + goto error; + + total = isl_space_dim(bmap->dim, isl_dim_all); + + for (i = bmap->n_eq - 1; i >= 0; --i) { + if (isl_seq_first_non_zero(bmap->eq[i] + 1 + total, + bmap->n_div) != -1) + continue; + for (j = 0; j < aff->n_eq; ++j) { + if (!isl_seq_eq(bmap->eq[i], aff->eq[j], 1 + total) && + !isl_seq_is_neg(bmap->eq[i], aff->eq[j], 1 + total)) + continue; + if (isl_basic_map_drop_equality(bmap, i) < 0) + goto error; + break; + } + } + + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +static __isl_give struct isl_aff_split *split_aff(__isl_keep isl_map *map) +{ + int i, n; + struct isl_aff_split *split; + isl_ctx *ctx; + + ctx = isl_map_get_ctx(map); + split = isl_calloc_array(ctx, struct isl_aff_split, map->n); + if (!split) + return NULL; + + for (i = 0; i < map->n; ++i) { + isl_basic_map *bmap; + split[i].aff = get_aff(isl_basic_map_copy(map->p[i])); + bmap = isl_basic_map_copy(map->p[i]); + bmap = isl_basic_map_cow(bmap); + bmap = drop_aff(bmap, split[i].aff); + split[i].map = isl_map_from_basic_map(bmap); + if (!split[i].aff || !split[i].map) + goto error; + } + + if (isl_sort(split, map->n, sizeof(struct isl_aff_split), + &aff_split_cmp, NULL) < 0) + goto error; + + n = map->n; + for (i = n - 1; i >= 1; --i) { + if (!isl_basic_map_plain_is_equal(split[i - 1].aff, + split[i].aff)) + continue; + isl_basic_map_free(split[i].aff); + split[i - 1].map = isl_map_union(split[i - 1].map, + split[i].map); + if (i != n - 1) + split[i] = split[n - 1]; + split[n - 1].aff = NULL; + split[n - 1].map = NULL; + --n; + } + + return split; +error: + free_split(split, map->n); + return NULL; +} + +static int defining_equality(__isl_keep isl_basic_map *eq, + __isl_keep isl_space *dim, enum isl_dim_type type, int pos) +{ + int i; + unsigned total; + + if (!eq) + return -1; + + pos += isl_space_offset(dim, type); + total = isl_basic_map_total_dim(eq); + + for (i = 0; i < eq->n_eq; ++i) { + if (isl_seq_last_non_zero(eq->eq[i] + 1, total) != pos) + continue; + if (isl_int_is_one(eq->eq[i][1 + pos])) + isl_seq_neg(eq->eq[i], eq->eq[i], 1 + total); + return i; + } + + return -1; +} + +/* Print dimension "pos" of data->space to "p". + * + * data->user is assumed to be an isl_basic_map keeping track of equalities. + * + * If the current dimension is defined by these equalities, then print + * the corresponding expression, assigned to the name of the dimension + * if there is any. Otherwise, print the name of the dimension. + */ +static __isl_give isl_printer *print_dim_eq(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos) +{ + isl_basic_map *eq = data->user; + int j; + + j = defining_equality(eq, data->space, data->type, pos); + if (j >= 0) { + if (isl_space_has_dim_name(data->space, data->type, pos)) { + p = print_name(data->space, p, data->type, pos, + data->latex); + p = isl_printer_print_str(p, " = "); + } + pos += 1 + isl_space_offset(data->space, data->type); + p = print_affine_of_len(data->space, NULL, p, eq->eq[j], pos); + } else { + p = print_name(data->space, p, data->type, pos, data->latex); + } + + return p; +} + +static __isl_give isl_printer *print_split_map(__isl_take isl_printer *p, + struct isl_aff_split *split, int n, __isl_keep isl_space *space) +{ + struct isl_print_space_data data = { 0 }; + int i; + int rational; + + data.print_dim = &print_dim_eq; + for (i = 0; i < n; ++i) { + if (!split[i].map) + break; + rational = split[i].map->n > 0 && + ISL_F_ISSET(split[i].map->p[0], ISL_BASIC_MAP_RATIONAL); + if (i) + p = isl_printer_print_str(p, "; "); + data.user = split[i].aff; + p = isl_print_space(space, p, rational, &data); + p = print_disjuncts_map(split[i].map, space, p, 0); + } + + return p; +} + +static __isl_give isl_printer *isl_map_print_isl_body(__isl_keep isl_map *map, + __isl_take isl_printer *p) +{ + struct isl_print_space_data data = { 0 }; + struct isl_aff_split *split = NULL; + int rational; + + if (!p->dump && map->n > 0) + split = split_aff(map); + if (split) { + p = print_split_map(p, split, map->n, map->dim); + } else { + rational = map->n > 0 && + ISL_F_ISSET(map->p[0], ISL_BASIC_MAP_RATIONAL); + p = isl_print_space(map->dim, p, rational, &data); + p = print_disjuncts_map(map, map->dim, p, 0); + } + free_split(split, map->n); + return p; +} + +static __isl_give isl_printer *isl_map_print_isl(__isl_keep isl_map *map, + __isl_take isl_printer *p) +{ + struct isl_print_space_data data = { 0 }; + + if (isl_map_dim(map, isl_dim_param) > 0) { + p = print_tuple(map->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, s_to[0]); + } + p = isl_printer_print_str(p, s_open_set[0]); + p = isl_map_print_isl_body(map, p); + p = isl_printer_print_str(p, s_close_set[0]); + return p; +} + +static __isl_give isl_printer *print_latex_map(__isl_keep isl_map *map, + __isl_take isl_printer *p, __isl_keep isl_basic_map *aff) +{ + struct isl_print_space_data data = { 0 }; + + data.latex = 1; + if (isl_map_dim(map, isl_dim_param) > 0) { + p = print_tuple(map->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, s_to[1]); + } + p = isl_printer_print_str(p, s_open_set[1]); + data.print_dim = &print_dim_eq; + data.user = aff; + p = isl_print_space(map->dim, p, 0, &data); + p = print_disjuncts_map(map, map->dim, p, 1); + p = isl_printer_print_str(p, s_close_set[1]); + + return p; +} + +static __isl_give isl_printer *isl_map_print_latex(__isl_keep isl_map *map, + __isl_take isl_printer *p) +{ + int i; + struct isl_aff_split *split = NULL; + + if (map->n > 0) + split = split_aff(map); + + if (!split) + return print_latex_map(map, p, NULL); + + for (i = 0; i < map->n; ++i) { + if (!split[i].map) + break; + if (i) + p = isl_printer_print_str(p, " \\cup "); + p = print_latex_map(split[i].map, p, split[i].aff); + } + + free_split(split, map->n); + return p; +} + +__isl_give isl_printer *isl_printer_print_basic_map(__isl_take isl_printer *p, + __isl_keep isl_basic_map *bmap) +{ + if (!p || !bmap) + goto error; + if (p->output_format == ISL_FORMAT_ISL) + return isl_basic_map_print_isl(bmap, p, 0); + else if (p->output_format == ISL_FORMAT_OMEGA) + return basic_map_print_omega(bmap, p); + isl_assert(bmap->ctx, 0, goto error); +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_basic_set(__isl_take isl_printer *p, + __isl_keep isl_basic_set *bset) +{ + if (!p || !bset) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return isl_basic_map_print_isl(bset, p, 0); + else if (p->output_format == ISL_FORMAT_POLYLIB) + return isl_basic_set_print_polylib(bset, p, 0); + else if (p->output_format == ISL_FORMAT_EXT_POLYLIB) + return isl_basic_set_print_polylib(bset, p, 1); + else if (p->output_format == ISL_FORMAT_POLYLIB_CONSTRAINTS) + return bset_print_constraints_polylib(bset, p); + else if (p->output_format == ISL_FORMAT_OMEGA) + return basic_set_print_omega(bset, p); + isl_assert(p->ctx, 0, goto error); +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_set(__isl_take isl_printer *p, + __isl_keep isl_set *set) +{ + if (!p || !set) + goto error; + if (p->output_format == ISL_FORMAT_ISL) + return isl_map_print_isl((isl_map *)set, p); + else if (p->output_format == ISL_FORMAT_POLYLIB) + return isl_set_print_polylib(set, p, 0); + else if (p->output_format == ISL_FORMAT_EXT_POLYLIB) + return isl_set_print_polylib(set, p, 1); + else if (p->output_format == ISL_FORMAT_OMEGA) + return isl_set_print_omega(set, p); + else if (p->output_format == ISL_FORMAT_LATEX) + return isl_map_print_latex((isl_map *)set, p); + isl_assert(set->ctx, 0, goto error); +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_map(__isl_take isl_printer *p, + __isl_keep isl_map *map) +{ + if (!p || !map) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return isl_map_print_isl(map, p); + else if (p->output_format == ISL_FORMAT_POLYLIB) + return isl_map_print_polylib(map, p, 0); + else if (p->output_format == ISL_FORMAT_EXT_POLYLIB) + return isl_map_print_polylib(map, p, 1); + else if (p->output_format == ISL_FORMAT_OMEGA) + return isl_map_print_omega(map, p); + else if (p->output_format == ISL_FORMAT_LATEX) + return isl_map_print_latex(map, p); + isl_assert(map->ctx, 0, goto error); +error: + isl_printer_free(p); + return NULL; +} + +struct isl_union_print_data { + isl_printer *p; + int first; +}; + +static isl_stat print_map_body(__isl_take isl_map *map, void *user) +{ + struct isl_union_print_data *data; + data = (struct isl_union_print_data *)user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, "; "); + data->first = 0; + + data->p = isl_map_print_isl_body(map, data->p); + isl_map_free(map); + + return isl_stat_ok; +} + +static __isl_give isl_printer *isl_union_map_print_isl( + __isl_keep isl_union_map *umap, __isl_take isl_printer *p) +{ + struct isl_union_print_data data; + struct isl_print_space_data space_data = { 0 }; + isl_space *dim; + + dim = isl_union_map_get_space(umap); + if (isl_space_dim(dim, isl_dim_param) > 0) { + p = print_tuple(dim, p, isl_dim_param, &space_data); + p = isl_printer_print_str(p, s_to[0]); + } + isl_space_free(dim); + p = isl_printer_print_str(p, s_open_set[0]); + data.p = p; + data.first = 1; + isl_union_map_foreach_map(umap, &print_map_body, &data); + p = data.p; + p = isl_printer_print_str(p, s_close_set[0]); + return p; +} + +static isl_stat print_latex_map_body(__isl_take isl_map *map, void *user) +{ + struct isl_union_print_data *data; + data = (struct isl_union_print_data *)user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, " \\cup "); + data->first = 0; + + data->p = isl_map_print_latex(map, data->p); + isl_map_free(map); + + return isl_stat_ok; +} + +static __isl_give isl_printer *isl_union_map_print_latex( + __isl_keep isl_union_map *umap, __isl_take isl_printer *p) +{ + struct isl_union_print_data data = { p, 1 }; + isl_union_map_foreach_map(umap, &print_latex_map_body, &data); + p = data.p; + return p; +} + +__isl_give isl_printer *isl_printer_print_union_map(__isl_take isl_printer *p, + __isl_keep isl_union_map *umap) +{ + if (!p || !umap) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return isl_union_map_print_isl(umap, p); + if (p->output_format == ISL_FORMAT_LATEX) + return isl_union_map_print_latex(umap, p); + + isl_die(p->ctx, isl_error_invalid, + "invalid output format for isl_union_map", goto error); +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_union_set(__isl_take isl_printer *p, + __isl_keep isl_union_set *uset) +{ + if (!p || !uset) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return isl_union_map_print_isl((isl_union_map *)uset, p); + if (p->output_format == ISL_FORMAT_LATEX) + return isl_union_map_print_latex((isl_union_map *)uset, p); + + isl_die(p->ctx, isl_error_invalid, + "invalid output format for isl_union_set", goto error); +error: + isl_printer_free(p); + return NULL; +} + +static int upoly_rec_n_non_zero(__isl_keep struct isl_upoly_rec *rec) +{ + int i; + int n; + + for (i = 0, n = 0; i < rec->n; ++i) + if (!isl_upoly_is_zero(rec->p[i])) + ++n; + + return n; +} + +static __isl_give isl_printer *upoly_print_cst(__isl_keep struct isl_upoly *up, + __isl_take isl_printer *p, int first) +{ + struct isl_upoly_cst *cst; + int neg; + + cst = isl_upoly_as_cst(up); + if (!cst) + goto error; + neg = !first && isl_int_is_neg(cst->n); + if (!first) + p = isl_printer_print_str(p, neg ? " - " : " + "); + if (neg) + isl_int_neg(cst->n, cst->n); + if (isl_int_is_zero(cst->d)) { + int sgn = isl_int_sgn(cst->n); + p = isl_printer_print_str(p, sgn < 0 ? "-infty" : + sgn == 0 ? "NaN" : "infty"); + } else + p = isl_printer_print_isl_int(p, cst->n); + if (neg) + isl_int_neg(cst->n, cst->n); + if (!isl_int_is_zero(cst->d) && !isl_int_is_one(cst->d)) { + p = isl_printer_print_str(p, "/"); + p = isl_printer_print_isl_int(p, cst->d); + } + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_base(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_mat *div, int var) +{ + unsigned total; + + total = isl_space_dim(dim, isl_dim_all); + if (var < total) + p = print_term(dim, NULL, dim->ctx->one, 1 + var, p, 0); + else + p = print_div(dim, div, var - total, p); + return p; +} + +static __isl_give isl_printer *print_pow(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_mat *div, int var, int exp) +{ + p = print_base(p, dim, div, var); + if (exp == 1) + return p; + if (p->output_format == ISL_FORMAT_C) { + int i; + for (i = 1; i < exp; ++i) { + p = isl_printer_print_str(p, "*"); + p = print_base(p, dim, div, var); + } + } else { + p = isl_printer_print_str(p, "^"); + p = isl_printer_print_int(p, exp); + } + return p; +} + +static __isl_give isl_printer *upoly_print(__isl_keep struct isl_upoly *up, + __isl_keep isl_space *dim, __isl_keep isl_mat *div, + __isl_take isl_printer *p, int outer) +{ + int i, n, first, print_parens; + struct isl_upoly_rec *rec; + + if (!p || !up || !dim || !div) + goto error; + + if (isl_upoly_is_cst(up)) + return upoly_print_cst(up, p, 1); + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + n = upoly_rec_n_non_zero(rec); + print_parens = n > 1 || + (outer && rec->up.var >= isl_space_dim(dim, isl_dim_all)); + if (print_parens) + p = isl_printer_print_str(p, "("); + for (i = 0, first = 1; i < rec->n; ++i) { + if (isl_upoly_is_zero(rec->p[i])) + continue; + if (isl_upoly_is_negone(rec->p[i])) { + if (!i) + p = isl_printer_print_str(p, "-1"); + else if (first) + p = isl_printer_print_str(p, "-"); + else + p = isl_printer_print_str(p, " - "); + } else if (isl_upoly_is_cst(rec->p[i]) && + !isl_upoly_is_one(rec->p[i])) + p = upoly_print_cst(rec->p[i], p, first); + else { + if (!first) + p = isl_printer_print_str(p, " + "); + if (i == 0 || !isl_upoly_is_one(rec->p[i])) + p = upoly_print(rec->p[i], dim, div, p, 0); + } + first = 0; + if (i == 0) + continue; + if (!isl_upoly_is_one(rec->p[i]) && + !isl_upoly_is_negone(rec->p[i])) + p = isl_printer_print_str(p, " * "); + p = print_pow(p, dim, div, rec->up.var, i); + } + if (print_parens) + p = isl_printer_print_str(p, ")"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_qpolynomial(__isl_take isl_printer *p, + __isl_keep isl_qpolynomial *qp) +{ + if (!p || !qp) + goto error; + p = upoly_print(qp->upoly, qp->dim, qp->div, p, 1); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_qpolynomial_isl(__isl_take isl_printer *p, + __isl_keep isl_qpolynomial *qp) +{ + struct isl_print_space_data data = { 0 }; + + if (!p || !qp) + goto error; + + if (isl_space_dim(qp->dim, isl_dim_param) > 0) { + p = print_tuple(qp->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + if (!isl_space_is_params(qp->dim)) { + p = isl_print_space(qp->dim, p, 0, &data); + p = isl_printer_print_str(p, " -> "); + } + p = print_qpolynomial(p, qp); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_qpolynomial_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_qpolynomial *qp) +{ + isl_int den; + + isl_int_init(den); + isl_qpolynomial_get_den(qp, &den); + if (!isl_int_is_one(den)) { + isl_qpolynomial *f; + p = isl_printer_print_str(p, "("); + qp = isl_qpolynomial_copy(qp); + f = isl_qpolynomial_rat_cst_on_domain(isl_space_copy(qp->dim), + den, qp->dim->ctx->one); + qp = isl_qpolynomial_mul(qp, f); + } + if (qp) + p = upoly_print(qp->upoly, dim, qp->div, p, 0); + else + p = isl_printer_free(p); + if (!isl_int_is_one(den)) { + p = isl_printer_print_str(p, ")/"); + p = isl_printer_print_isl_int(p, den); + isl_qpolynomial_free(qp); + } + isl_int_clear(den); + return p; +} + +__isl_give isl_printer *isl_printer_print_qpolynomial( + __isl_take isl_printer *p, __isl_keep isl_qpolynomial *qp) +{ + if (!p || !qp) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_qpolynomial_isl(p, qp); + else if (p->output_format == ISL_FORMAT_C) + return print_qpolynomial_c(p, qp->dim, qp); + else + isl_die(qp->dim->ctx, isl_error_unsupported, + "output format not supported for isl_qpolynomials", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +void isl_qpolynomial_print(__isl_keep isl_qpolynomial *qp, FILE *out, + unsigned output_format) +{ + isl_printer *p; + + if (!qp) + return; + + isl_assert(qp->dim->ctx, output_format == ISL_FORMAT_ISL, return); + p = isl_printer_to_file(qp->dim->ctx, out); + p = isl_printer_print_qpolynomial(p, qp); + isl_printer_free(p); +} + +static __isl_give isl_printer *qpolynomial_fold_print( + __isl_keep isl_qpolynomial_fold *fold, __isl_take isl_printer *p) +{ + int i; + + if (fold->type == isl_fold_min) + p = isl_printer_print_str(p, "min"); + else if (fold->type == isl_fold_max) + p = isl_printer_print_str(p, "max"); + p = isl_printer_print_str(p, "("); + for (i = 0; i < fold->n; ++i) { + if (i) + p = isl_printer_print_str(p, ", "); + p = print_qpolynomial(p, fold->qp[i]); + } + p = isl_printer_print_str(p, ")"); + return p; +} + +void isl_qpolynomial_fold_print(__isl_keep isl_qpolynomial_fold *fold, + FILE *out, unsigned output_format) +{ + isl_printer *p; + + if (!fold) + return; + + isl_assert(fold->dim->ctx, output_format == ISL_FORMAT_ISL, return); + + p = isl_printer_to_file(fold->dim->ctx, out); + p = isl_printer_print_qpolynomial_fold(p, fold); + + isl_printer_free(p); +} + +static __isl_give isl_printer *isl_pwqp_print_isl_body( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp) +{ + struct isl_print_space_data data = { 0 }; + int i = 0; + + for (i = 0; i < pwqp->n; ++i) { + isl_space *space; + + if (i) + p = isl_printer_print_str(p, "; "); + space = isl_qpolynomial_get_domain_space(pwqp->p[i].qp); + if (!isl_space_is_params(space)) { + p = isl_print_space(space, p, 0, &data); + p = isl_printer_print_str(p, " -> "); + } + p = print_qpolynomial(p, pwqp->p[i].qp); + p = print_disjuncts((isl_map *)pwqp->p[i].set, space, p, 0); + isl_space_free(space); + } + + return p; +} + +static __isl_give isl_printer *print_pw_qpolynomial_isl( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp) +{ + struct isl_print_space_data data = { 0 }; + + if (!p || !pwqp) + goto error; + + if (isl_space_dim(pwqp->dim, isl_dim_param) > 0) { + p = print_tuple(pwqp->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + if (pwqp->n == 0) { + if (!isl_space_is_set(pwqp->dim)) { + p = print_tuple(pwqp->dim, p, isl_dim_in, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "0"); + } + p = isl_pwqp_print_isl_body(p, pwqp); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +void isl_pw_qpolynomial_print(__isl_keep isl_pw_qpolynomial *pwqp, FILE *out, + unsigned output_format) +{ + isl_printer *p; + + if (!pwqp) + return; + + p = isl_printer_to_file(pwqp->dim->ctx, out); + p = isl_printer_set_output_format(p, output_format); + p = isl_printer_print_pw_qpolynomial(p, pwqp); + + isl_printer_free(p); +} + +static __isl_give isl_printer *isl_pwf_print_isl_body( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf) +{ + struct isl_print_space_data data = { 0 }; + int i = 0; + + for (i = 0; i < pwf->n; ++i) { + isl_space *space; + + if (i) + p = isl_printer_print_str(p, "; "); + space = isl_qpolynomial_fold_get_domain_space(pwf->p[i].fold); + if (!isl_space_is_params(space)) { + p = isl_print_space(space, p, 0, &data); + p = isl_printer_print_str(p, " -> "); + } + p = qpolynomial_fold_print(pwf->p[i].fold, p); + p = print_disjuncts((isl_map *)pwf->p[i].set, space, p, 0); + isl_space_free(space); + } + + return p; +} + +static __isl_give isl_printer *print_pw_qpolynomial_fold_isl( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf) +{ + struct isl_print_space_data data = { 0 }; + + if (isl_space_dim(pwf->dim, isl_dim_param) > 0) { + p = print_tuple(pwf->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + if (pwf->n == 0) { + if (!isl_space_is_set(pwf->dim)) { + p = print_tuple(pwf->dim, p, isl_dim_in, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "0"); + } + p = isl_pwf_print_isl_body(p, pwf); + p = isl_printer_print_str(p, " }"); + return p; +} + +static __isl_give isl_printer *print_affine_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_basic_set *bset, isl_int *c); + +static __isl_give isl_printer *print_name_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, + __isl_keep isl_basic_set *bset, enum isl_dim_type type, unsigned pos) +{ + if (type == isl_dim_div) { + p = isl_printer_print_str(p, "floord("); + p = print_affine_c(p, dim, bset, bset->div[pos] + 1); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_isl_int(p, bset->div[pos][0]); + p = isl_printer_print_str(p, ")"); + } else { + const char *name; + + name = isl_space_get_dim_name(dim, type, pos); + if (!name) + name = "UNNAMED"; + p = isl_printer_print_str(p, name); + } + return p; +} + +static __isl_give isl_printer *print_term_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, + __isl_keep isl_basic_set *bset, isl_int c, unsigned pos) +{ + enum isl_dim_type type; + + if (pos == 0) + return isl_printer_print_isl_int(p, c); + + if (isl_int_is_one(c)) + ; + else if (isl_int_is_negone(c)) + p = isl_printer_print_str(p, "-"); + else { + p = isl_printer_print_isl_int(p, c); + p = isl_printer_print_str(p, "*"); + } + type = pos2type(dim, &pos); + p = print_name_c(p, dim, bset, type, pos); + return p; +} + +static __isl_give isl_printer *print_partial_affine_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, + __isl_keep isl_basic_set *bset, isl_int *c, unsigned len) +{ + int i; + int first; + + for (i = 0, first = 1; i < len; ++i) { + int flip = 0; + if (isl_int_is_zero(c[i])) + continue; + if (!first) { + if (isl_int_is_neg(c[i])) { + flip = 1; + isl_int_neg(c[i], c[i]); + p = isl_printer_print_str(p, " - "); + } else + p = isl_printer_print_str(p, " + "); + } + first = 0; + p = print_term_c(p, dim, bset, c[i], i); + if (flip) + isl_int_neg(c[i], c[i]); + } + if (first) + p = isl_printer_print_str(p, "0"); + return p; +} + +static __isl_give isl_printer *print_affine_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_basic_set *bset, isl_int *c) +{ + unsigned len = 1 + isl_basic_set_total_dim(bset); + return print_partial_affine_c(p, dim, bset, c, len); +} + +/* We skip the constraint if it is implied by the div expression. + * + * *first indicates whether this is the first constraint in the conjunction and + * is updated if the constraint is actually printed. + */ +static __isl_give isl_printer *print_constraint_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, + __isl_keep isl_basic_set *bset, isl_int *c, const char *op, int *first) +{ + unsigned o_div; + unsigned n_div; + int div; + + o_div = isl_basic_set_offset(bset, isl_dim_div); + n_div = isl_basic_set_dim(bset, isl_dim_div); + div = isl_seq_last_non_zero(c + o_div, n_div); + if (div >= 0 && isl_basic_set_is_div_constraint(bset, c, div)) + return p; + + if (!*first) + p = isl_printer_print_str(p, " && "); + + p = print_affine_c(p, dim, bset, c); + p = isl_printer_print_str(p, " "); + p = isl_printer_print_str(p, op); + p = isl_printer_print_str(p, " 0"); + + *first = 0; + + return p; +} + +static __isl_give isl_printer *print_basic_set_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_basic_set *bset) +{ + int i, j; + int first = 1; + unsigned n_div = isl_basic_set_dim(bset, isl_dim_div); + unsigned total = isl_basic_set_total_dim(bset) - n_div; + + for (i = 0; i < bset->n_eq; ++i) { + j = isl_seq_last_non_zero(bset->eq[i] + 1 + total, n_div); + if (j < 0) + p = print_constraint_c(p, dim, bset, + bset->eq[i], "==", &first); + else { + if (i) + p = isl_printer_print_str(p, " && "); + p = isl_printer_print_str(p, "("); + p = print_partial_affine_c(p, dim, bset, bset->eq[i], + 1 + total + j); + p = isl_printer_print_str(p, ") % "); + p = isl_printer_print_isl_int(p, + bset->eq[i][1 + total + j]); + p = isl_printer_print_str(p, " == 0"); + first = 0; + } + } + for (i = 0; i < bset->n_ineq; ++i) + p = print_constraint_c(p, dim, bset, bset->ineq[i], ">=", + &first); + return p; +} + +static __isl_give isl_printer *print_set_c(__isl_take isl_printer *p, + __isl_keep isl_space *dim, __isl_keep isl_set *set) +{ + int i; + + if (!set) + return isl_printer_free(p); + + if (set->n == 0) + p = isl_printer_print_str(p, "0"); + + for (i = 0; i < set->n; ++i) { + if (i) + p = isl_printer_print_str(p, " || "); + if (set->n > 1) + p = isl_printer_print_str(p, "("); + p = print_basic_set_c(p, dim, set->p[i]); + if (set->n > 1) + p = isl_printer_print_str(p, ")"); + } + return p; +} + +static __isl_give isl_printer *print_pw_qpolynomial_c( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp) +{ + int i; + + if (pwqp->n == 1 && isl_set_plain_is_universe(pwqp->p[0].set)) + return print_qpolynomial_c(p, pwqp->dim, pwqp->p[0].qp); + + for (i = 0; i < pwqp->n; ++i) { + p = isl_printer_print_str(p, "("); + p = print_set_c(p, pwqp->dim, pwqp->p[i].set); + p = isl_printer_print_str(p, ") ? ("); + p = print_qpolynomial_c(p, pwqp->dim, pwqp->p[i].qp); + p = isl_printer_print_str(p, ") : "); + } + + p = isl_printer_print_str(p, "0"); + return p; +} + +__isl_give isl_printer *isl_printer_print_pw_qpolynomial( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp) +{ + if (!p || !pwqp) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_pw_qpolynomial_isl(p, pwqp); + else if (p->output_format == ISL_FORMAT_C) + return print_pw_qpolynomial_c(p, pwqp); + isl_assert(p->ctx, 0, goto error); +error: + isl_printer_free(p); + return NULL; +} + +static isl_stat print_pwqp_body(__isl_take isl_pw_qpolynomial *pwqp, void *user) +{ + struct isl_union_print_data *data; + data = (struct isl_union_print_data *)user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, "; "); + data->first = 0; + + data->p = isl_pwqp_print_isl_body(data->p, pwqp); + isl_pw_qpolynomial_free(pwqp); + + return isl_stat_ok; +} + +static __isl_give isl_printer *print_union_pw_qpolynomial_isl( + __isl_take isl_printer *p, __isl_keep isl_union_pw_qpolynomial *upwqp) +{ + struct isl_union_print_data data; + struct isl_print_space_data space_data = { 0 }; + isl_space *dim; + + dim = isl_union_pw_qpolynomial_get_space(upwqp); + if (isl_space_dim(dim, isl_dim_param) > 0) { + p = print_tuple(dim, p, isl_dim_param, &space_data); + p = isl_printer_print_str(p, " -> "); + } + isl_space_free(dim); + p = isl_printer_print_str(p, "{ "); + data.p = p; + data.first = 1; + isl_union_pw_qpolynomial_foreach_pw_qpolynomial(upwqp, &print_pwqp_body, + &data); + p = data.p; + p = isl_printer_print_str(p, " }"); + return p; +} + +__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial( + __isl_take isl_printer *p, __isl_keep isl_union_pw_qpolynomial *upwqp) +{ + if (!p || !upwqp) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_union_pw_qpolynomial_isl(p, upwqp); + isl_die(p->ctx, isl_error_invalid, + "invalid output format for isl_union_pw_qpolynomial", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_qpolynomial_fold_c( + __isl_take isl_printer *p, __isl_keep isl_space *dim, + __isl_keep isl_qpolynomial_fold *fold) +{ + int i; + + for (i = 0; i < fold->n - 1; ++i) + if (fold->type == isl_fold_min) + p = isl_printer_print_str(p, "min("); + else if (fold->type == isl_fold_max) + p = isl_printer_print_str(p, "max("); + + for (i = 0; i < fold->n; ++i) { + if (i) + p = isl_printer_print_str(p, ", "); + p = print_qpolynomial_c(p, dim, fold->qp[i]); + if (i) + p = isl_printer_print_str(p, ")"); + } + return p; +} + +__isl_give isl_printer *isl_printer_print_qpolynomial_fold( + __isl_take isl_printer *p, __isl_keep isl_qpolynomial_fold *fold) +{ + if (!p || !fold) + goto error; + if (p->output_format == ISL_FORMAT_ISL) + return qpolynomial_fold_print(fold, p); + else if (p->output_format == ISL_FORMAT_C) + return print_qpolynomial_fold_c(p, fold->dim, fold); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_pw_qpolynomial_fold_c( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf) +{ + int i; + + if (pwf->n == 1 && isl_set_plain_is_universe(pwf->p[0].set)) + return print_qpolynomial_fold_c(p, pwf->dim, pwf->p[0].fold); + + for (i = 0; i < pwf->n; ++i) { + p = isl_printer_print_str(p, "("); + p = print_set_c(p, pwf->dim, pwf->p[i].set); + p = isl_printer_print_str(p, ") ? ("); + p = print_qpolynomial_fold_c(p, pwf->dim, pwf->p[i].fold); + p = isl_printer_print_str(p, ") : "); + } + + p = isl_printer_print_str(p, "0"); + return p; +} + +__isl_give isl_printer *isl_printer_print_pw_qpolynomial_fold( + __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf) +{ + if (!p || !pwf) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_pw_qpolynomial_fold_isl(p, pwf); + else if (p->output_format == ISL_FORMAT_C) + return print_pw_qpolynomial_fold_c(p, pwf); + isl_assert(p->ctx, 0, goto error); +error: + isl_printer_free(p); + return NULL; +} + +void isl_pw_qpolynomial_fold_print(__isl_keep isl_pw_qpolynomial_fold *pwf, + FILE *out, unsigned output_format) +{ + isl_printer *p; + + if (!pwf) + return; + + p = isl_printer_to_file(pwf->dim->ctx, out); + p = isl_printer_set_output_format(p, output_format); + p = isl_printer_print_pw_qpolynomial_fold(p, pwf); + + isl_printer_free(p); +} + +static isl_stat print_pwf_body(__isl_take isl_pw_qpolynomial_fold *pwf, + void *user) +{ + struct isl_union_print_data *data; + data = (struct isl_union_print_data *)user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, "; "); + data->first = 0; + + data->p = isl_pwf_print_isl_body(data->p, pwf); + isl_pw_qpolynomial_fold_free(pwf); + + return isl_stat_ok; +} + +static __isl_give isl_printer *print_union_pw_qpolynomial_fold_isl( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_qpolynomial_fold *upwf) +{ + struct isl_union_print_data data; + struct isl_print_space_data space_data = { 0 }; + isl_space *dim; + + dim = isl_union_pw_qpolynomial_fold_get_space(upwf); + if (isl_space_dim(dim, isl_dim_param) > 0) { + p = print_tuple(dim, p, isl_dim_param, &space_data); + p = isl_printer_print_str(p, " -> "); + } + isl_space_free(dim); + p = isl_printer_print_str(p, "{ "); + data.p = p; + data.first = 1; + isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold(upwf, + &print_pwf_body, &data); + p = data.p; + p = isl_printer_print_str(p, " }"); + return p; +} + +__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial_fold( + __isl_take isl_printer *p, + __isl_keep isl_union_pw_qpolynomial_fold *upwf) +{ + if (!p || !upwf) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_union_pw_qpolynomial_fold_isl(p, upwf); + isl_die(p->ctx, isl_error_invalid, + "invalid output format for isl_union_pw_qpolynomial_fold", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_constraint(__isl_take isl_printer *p, + __isl_keep isl_constraint *c) +{ + isl_basic_map *bmap; + + if (!p || !c) + goto error; + + bmap = isl_basic_map_from_constraint(isl_constraint_copy(c)); + p = isl_printer_print_basic_map(p, bmap); + isl_basic_map_free(bmap); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *isl_printer_print_space_isl( + __isl_take isl_printer *p, __isl_keep isl_space *space) +{ + struct isl_print_space_data data = { 0 }; + + if (!space) + goto error; + + if (isl_space_dim(space, isl_dim_param) > 0) { + p = print_tuple(space, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + + p = isl_printer_print_str(p, "{ "); + if (isl_space_is_params(space)) + p = isl_printer_print_str(p, s_such_that[0]); + else + p = isl_print_space(space, p, 0, &data); + p = isl_printer_print_str(p, " }"); + + return p; +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_space(__isl_take isl_printer *p, + __isl_keep isl_space *space) +{ + if (!p || !space) + return isl_printer_free(p); + if (p->output_format == ISL_FORMAT_ISL) + return isl_printer_print_space_isl(p, space); + else if (p->output_format == ISL_FORMAT_OMEGA) + return print_omega_parameters(space, p); + + isl_die(isl_space_get_ctx(space), isl_error_unsupported, + "output format not supported for space", + return isl_printer_free(p)); +} + +__isl_give isl_printer *isl_printer_print_local_space(__isl_take isl_printer *p, + __isl_keep isl_local_space *ls) +{ + struct isl_print_space_data data = { 0 }; + unsigned n_div; + + if (!ls) + goto error; + + if (isl_local_space_dim(ls, isl_dim_param) > 0) { + p = print_tuple(ls->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + p = isl_print_space(ls->dim, p, 0, &data); + n_div = isl_local_space_dim(ls, isl_dim_div); + if (n_div > 0) { + p = isl_printer_print_str(p, " : "); + p = isl_printer_print_str(p, s_open_exists[0]); + p = print_div_list(p, ls->dim, ls->div, 0, 1); + p = isl_printer_print_str(p, s_close_exists[0]); + } else if (isl_space_is_params(ls->dim)) + p = isl_printer_print_str(p, s_such_that[0]); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_aff_body(__isl_take isl_printer *p, + __isl_keep isl_aff *aff) +{ + unsigned total; + + if (isl_aff_is_nan(aff)) + return isl_printer_print_str(p, "NaN"); + + total = isl_local_space_dim(aff->ls, isl_dim_all); + p = isl_printer_print_str(p, "("); + p = print_affine_of_len(aff->ls->dim, aff->ls->div, p, + aff->v->el + 1, 1 + total); + if (isl_int_is_one(aff->v->el[0])) + p = isl_printer_print_str(p, ")"); + else { + p = isl_printer_print_str(p, ")/"); + p = isl_printer_print_isl_int(p, aff->v->el[0]); + } + + return p; +} + +static __isl_give isl_printer *print_aff(__isl_take isl_printer *p, + __isl_keep isl_aff *aff) +{ + struct isl_print_space_data data = { 0 }; + + if (isl_space_is_params(aff->ls->dim)) + ; + else { + p = print_tuple(aff->ls->dim, p, isl_dim_set, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "["); + p = print_aff_body(p, aff); + p = isl_printer_print_str(p, "]"); + + return p; +} + +static __isl_give isl_printer *print_aff_isl(__isl_take isl_printer *p, + __isl_keep isl_aff *aff) +{ + struct isl_print_space_data data = { 0 }; + + if (!aff) + goto error; + + if (isl_local_space_dim(aff->ls, isl_dim_param) > 0) { + p = print_tuple(aff->ls->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + p = print_aff(p, aff); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +/* Print the body of an isl_pw_aff, i.e., a semicolon delimited + * sequence of affine expressions, each followed by constraints. + */ +static __isl_give isl_printer *print_pw_aff_body( + __isl_take isl_printer *p, __isl_keep isl_pw_aff *pa) +{ + int i; + + if (!pa) + return isl_printer_free(p); + + for (i = 0; i < pa->n; ++i) { + isl_space *space; + + if (i) + p = isl_printer_print_str(p, "; "); + p = print_aff(p, pa->p[i].aff); + space = isl_aff_get_domain_space(pa->p[i].aff); + p = print_disjuncts((isl_map *)pa->p[i].set, space, p, 0); + isl_space_free(space); + } + + return p; +} + +static __isl_give isl_printer *print_pw_aff_isl(__isl_take isl_printer *p, + __isl_keep isl_pw_aff *pwaff) +{ + struct isl_print_space_data data = { 0 }; + + if (!pwaff) + goto error; + + if (isl_space_dim(pwaff->dim, isl_dim_param) > 0) { + p = print_tuple(pwaff->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + p = print_pw_aff_body(p, pwaff); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_ls_affine_c(__isl_take isl_printer *p, + __isl_keep isl_local_space *ls, isl_int *c); + +static __isl_give isl_printer *print_ls_name_c(__isl_take isl_printer *p, + __isl_keep isl_local_space *ls, enum isl_dim_type type, unsigned pos) +{ + if (type == isl_dim_div) { + p = isl_printer_print_str(p, "floord("); + p = print_ls_affine_c(p, ls, ls->div->row[pos] + 1); + p = isl_printer_print_str(p, ", "); + p = isl_printer_print_isl_int(p, ls->div->row[pos][0]); + p = isl_printer_print_str(p, ")"); + } else { + const char *name; + + name = isl_space_get_dim_name(ls->dim, type, pos); + if (!name) + name = "UNNAMED"; + p = isl_printer_print_str(p, name); + } + return p; +} + +static __isl_give isl_printer *print_ls_term_c(__isl_take isl_printer *p, + __isl_keep isl_local_space *ls, isl_int c, unsigned pos) +{ + enum isl_dim_type type; + + if (pos == 0) + return isl_printer_print_isl_int(p, c); + + if (isl_int_is_one(c)) + ; + else if (isl_int_is_negone(c)) + p = isl_printer_print_str(p, "-"); + else { + p = isl_printer_print_isl_int(p, c); + p = isl_printer_print_str(p, "*"); + } + type = pos2type(ls->dim, &pos); + p = print_ls_name_c(p, ls, type, pos); + return p; +} + +static __isl_give isl_printer *print_ls_partial_affine_c( + __isl_take isl_printer *p, __isl_keep isl_local_space *ls, + isl_int *c, unsigned len) +{ + int i; + int first; + + for (i = 0, first = 1; i < len; ++i) { + int flip = 0; + if (isl_int_is_zero(c[i])) + continue; + if (!first) { + if (isl_int_is_neg(c[i])) { + flip = 1; + isl_int_neg(c[i], c[i]); + p = isl_printer_print_str(p, " - "); + } else + p = isl_printer_print_str(p, " + "); + } + first = 0; + p = print_ls_term_c(p, ls, c[i], i); + if (flip) + isl_int_neg(c[i], c[i]); + } + if (first) + p = isl_printer_print_str(p, "0"); + return p; +} + +static __isl_give isl_printer *print_ls_affine_c(__isl_take isl_printer *p, + __isl_keep isl_local_space *ls, isl_int *c) +{ + unsigned len = 1 + isl_local_space_dim(ls, isl_dim_all); + return print_ls_partial_affine_c(p, ls, c, len); +} + +static __isl_give isl_printer *print_aff_c(__isl_take isl_printer *p, + __isl_keep isl_aff *aff) +{ + unsigned total; + + total = isl_local_space_dim(aff->ls, isl_dim_all); + if (!isl_int_is_one(aff->v->el[0])) + p = isl_printer_print_str(p, "("); + p = print_ls_partial_affine_c(p, aff->ls, aff->v->el + 1, 1 + total); + if (!isl_int_is_one(aff->v->el[0])) { + p = isl_printer_print_str(p, ")/"); + p = isl_printer_print_isl_int(p, aff->v->el[0]); + } + return p; +} + +/* In the C format, we cannot express that "pwaff" may be undefined + * on parts of the domain space. We therefore assume that the expression + * will only be evaluated on its definition domain and compute the gist + * of each cell with respect to this domain. + */ +static __isl_give isl_printer *print_pw_aff_c(__isl_take isl_printer *p, + __isl_keep isl_pw_aff *pwaff) +{ + isl_set *domain; + isl_ast_build *build; + isl_ast_expr *expr; + + if (pwaff->n < 1) + isl_die(p->ctx, isl_error_unsupported, + "cannot print empty isl_pw_aff in C format", + return isl_printer_free(p)); + + domain = isl_pw_aff_domain(isl_pw_aff_copy(pwaff)); + build = isl_ast_build_from_context(domain); + expr = isl_ast_build_expr_from_pw_aff(build, isl_pw_aff_copy(pwaff)); + p = isl_printer_print_ast_expr(p, expr); + isl_ast_expr_free(expr); + isl_ast_build_free(build); + + return p; +} + +__isl_give isl_printer *isl_printer_print_aff(__isl_take isl_printer *p, + __isl_keep isl_aff *aff) +{ + if (!p || !aff) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_aff_isl(p, aff); + else if (p->output_format == ISL_FORMAT_C) + return print_aff_c(p, aff); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_pw_aff(__isl_take isl_printer *p, + __isl_keep isl_pw_aff *pwaff) +{ + if (!p || !pwaff) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_pw_aff_isl(p, pwaff); + else if (p->output_format == ISL_FORMAT_C) + return print_pw_aff_c(p, pwaff); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +/* Print "pa" in a sequence of isl_pw_affs delimited by semicolons. + * Each isl_pw_aff itself is also printed as semicolon delimited + * sequence of pieces. + * If data->first = 1, then this is the first in the sequence. + * Update data->first to tell the next element that it is not the first. + */ +static isl_stat print_pw_aff_body_wrap(__isl_take isl_pw_aff *pa, + void *user) +{ + struct isl_union_print_data *data; + data = (struct isl_union_print_data *) user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, "; "); + data->first = 0; + + data->p = print_pw_aff_body(data->p, pa); + isl_pw_aff_free(pa); + + return data->p ? isl_stat_ok : isl_stat_error; +} + +/* Print the body of an isl_union_pw_aff, i.e., a semicolon delimited + * sequence of affine expressions, each followed by constraints, + * with the sequence enclosed in braces. + */ +static __isl_give isl_printer *print_union_pw_aff_body( + __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa) +{ + struct isl_union_print_data data = { p, 1 }; + + p = isl_printer_print_str(p, s_open_set[0]); + data.p = p; + if (isl_union_pw_aff_foreach_pw_aff(upa, + &print_pw_aff_body_wrap, &data) < 0) + data.p = isl_printer_free(p); + p = data.p; + p = isl_printer_print_str(p, s_close_set[0]); + + return p; +} + +/* Print the isl_union_pw_aff "upa" to "p" in isl format. + * + * The individual isl_pw_affs are delimited by a semicolon. + */ +static __isl_give isl_printer *print_union_pw_aff_isl( + __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa) +{ + struct isl_print_space_data data = { 0 }; + isl_space *space; + + space = isl_union_pw_aff_get_space(upa); + if (isl_space_dim(space, isl_dim_param) > 0) { + p = print_tuple(space, p, isl_dim_param, &data); + p = isl_printer_print_str(p, s_to[0]); + } + isl_space_free(space); + p = print_union_pw_aff_body(p, upa); + return p; +} + +/* Print the isl_union_pw_aff "upa" to "p". + * + * We currently only support an isl format. + */ +__isl_give isl_printer *isl_printer_print_union_pw_aff( + __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa) +{ + if (!p || !upa) + return isl_printer_free(p); + + if (p->output_format == ISL_FORMAT_ISL) + return print_union_pw_aff_isl(p, upa); + isl_die(isl_printer_get_ctx(p), isl_error_unsupported, + "unsupported output format", return isl_printer_free(p)); +} + +/* Print dimension "pos" of data->space to "p". + * + * data->user is assumed to be an isl_multi_aff. + * + * If the current dimension is an output dimension, then print + * the corresponding expression. Otherwise, print the name of the dimension. + */ +static __isl_give isl_printer *print_dim_ma(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos) +{ + isl_multi_aff *ma = data->user; + + if (data->type == isl_dim_out) + p = print_aff_body(p, ma->p[pos]); + else + p = print_name(data->space, p, data->type, pos, data->latex); + + return p; +} + +static __isl_give isl_printer *print_multi_aff(__isl_take isl_printer *p, + __isl_keep isl_multi_aff *maff) +{ + struct isl_print_space_data data = { 0 }; + + data.print_dim = &print_dim_ma; + data.user = maff; + return isl_print_space(maff->space, p, 0, &data); +} + +static __isl_give isl_printer *print_multi_aff_isl(__isl_take isl_printer *p, + __isl_keep isl_multi_aff *maff) +{ + struct isl_print_space_data data = { 0 }; + + if (!maff) + goto error; + + if (isl_space_dim(maff->space, isl_dim_param) > 0) { + p = print_tuple(maff->space, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + p = print_multi_aff(p, maff); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_multi_aff(__isl_take isl_printer *p, + __isl_keep isl_multi_aff *maff) +{ + if (!p || !maff) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_multi_aff_isl(p, maff); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_pw_multi_aff_body( + __isl_take isl_printer *p, __isl_keep isl_pw_multi_aff *pma) +{ + int i; + + if (!pma) + goto error; + + for (i = 0; i < pma->n; ++i) { + isl_space *space; + + if (i) + p = isl_printer_print_str(p, "; "); + p = print_multi_aff(p, pma->p[i].maff); + space = isl_multi_aff_get_domain_space(pma->p[i].maff); + p = print_disjuncts((isl_map *)pma->p[i].set, space, p, 0); + isl_space_free(space); + } + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_pw_multi_aff_isl(__isl_take isl_printer *p, + __isl_keep isl_pw_multi_aff *pma) +{ + struct isl_print_space_data data = { 0 }; + + if (!pma) + goto error; + + if (isl_space_dim(pma->dim, isl_dim_param) > 0) { + p = print_tuple(pma->dim, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + p = print_pw_multi_aff_body(p, pma); + p = isl_printer_print_str(p, " }"); + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *print_unnamed_pw_multi_aff_c( + __isl_take isl_printer *p, __isl_keep isl_pw_multi_aff *pma) +{ + int i; + + for (i = 0; i < pma->n - 1; ++i) { + p = isl_printer_print_str(p, "("); + p = print_set_c(p, pma->dim, pma->p[i].set); + p = isl_printer_print_str(p, ") ? ("); + p = print_aff_c(p, pma->p[i].maff->p[0]); + p = isl_printer_print_str(p, ") : "); + } + + return print_aff_c(p, pma->p[pma->n - 1].maff->p[0]); +} + +static __isl_give isl_printer *print_pw_multi_aff_c(__isl_take isl_printer *p, + __isl_keep isl_pw_multi_aff *pma) +{ + int n; + const char *name; + + if (!pma) + goto error; + if (pma->n < 1) + isl_die(p->ctx, isl_error_unsupported, + "cannot print empty isl_pw_multi_aff in C format", + goto error); + name = isl_pw_multi_aff_get_tuple_name(pma, isl_dim_out); + if (!name && isl_pw_multi_aff_dim(pma, isl_dim_out) == 1) + return print_unnamed_pw_multi_aff_c(p, pma); + if (!name) + isl_die(p->ctx, isl_error_unsupported, + "cannot print unnamed isl_pw_multi_aff in C format", + goto error); + + p = isl_printer_print_str(p, name); + n = isl_pw_multi_aff_dim(pma, isl_dim_out); + if (n != 0) + isl_die(p->ctx, isl_error_unsupported, + "not supported yet", goto error); + + return p; +error: + isl_printer_free(p); + return NULL; +} + +__isl_give isl_printer *isl_printer_print_pw_multi_aff( + __isl_take isl_printer *p, __isl_keep isl_pw_multi_aff *pma) +{ + if (!p || !pma) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_pw_multi_aff_isl(p, pma); + if (p->output_format == ISL_FORMAT_C) + return print_pw_multi_aff_c(p, pma); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +static isl_stat print_pw_multi_aff_body_wrap(__isl_take isl_pw_multi_aff *pma, + void *user) +{ + struct isl_union_print_data *data; + data = (struct isl_union_print_data *) user; + + if (!data->first) + data->p = isl_printer_print_str(data->p, "; "); + data->first = 0; + + data->p = print_pw_multi_aff_body(data->p, pma); + isl_pw_multi_aff_free(pma); + + return isl_stat_ok; +} + +static __isl_give isl_printer *print_union_pw_multi_aff_isl( + __isl_take isl_printer *p, __isl_keep isl_union_pw_multi_aff *upma) +{ + struct isl_union_print_data data; + struct isl_print_space_data space_data = { 0 }; + isl_space *space; + + space = isl_union_pw_multi_aff_get_space(upma); + if (isl_space_dim(space, isl_dim_param) > 0) { + p = print_tuple(space, p, isl_dim_param, &space_data); + p = isl_printer_print_str(p, s_to[0]); + } + isl_space_free(space); + p = isl_printer_print_str(p, s_open_set[0]); + data.p = p; + data.first = 1; + isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, + &print_pw_multi_aff_body_wrap, &data); + p = data.p; + p = isl_printer_print_str(p, s_close_set[0]); + return p; +} + +__isl_give isl_printer *isl_printer_print_union_pw_multi_aff( + __isl_take isl_printer *p, __isl_keep isl_union_pw_multi_aff *upma) +{ + if (!p || !upma) + goto error; + + if (p->output_format == ISL_FORMAT_ISL) + return print_union_pw_multi_aff_isl(p, upma); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + goto error); +error: + isl_printer_free(p); + return NULL; +} + +/* Print dimension "pos" of data->space to "p". + * + * data->user is assumed to be an isl_multi_pw_aff. + * + * If the current dimension is an output dimension, then print + * the corresponding piecewise affine expression. + * Otherwise, print the name of the dimension. + */ +static __isl_give isl_printer *print_dim_mpa(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos) +{ + int i; + int need_parens; + isl_multi_pw_aff *mpa = data->user; + isl_pw_aff *pa; + + if (data->type != isl_dim_out) + return print_name(data->space, p, data->type, pos, data->latex); + + pa = mpa->p[pos]; + if (pa->n == 0) + return isl_printer_print_str(p, "(0 : 1 = 0)"); + + need_parens = pa->n != 1 || !isl_set_plain_is_universe(pa->p[0].set); + if (need_parens) + p = isl_printer_print_str(p, "("); + for (i = 0; i < pa->n; ++i) { + isl_space *space; + + if (i) + p = isl_printer_print_str(p, "; "); + p = print_aff_body(p, pa->p[i].aff); + space = isl_aff_get_domain_space(pa->p[i].aff); + p = print_disjuncts(pa->p[i].set, space, p, 0); + isl_space_free(space); + } + if (need_parens) + p = isl_printer_print_str(p, ")"); + + return p; +} + +/* Print "mpa" to "p" in isl format. + */ +static __isl_give isl_printer *print_multi_pw_aff_isl(__isl_take isl_printer *p, + __isl_keep isl_multi_pw_aff *mpa) +{ + struct isl_print_space_data data = { 0 }; + + if (!mpa) + return isl_printer_free(p); + + if (isl_space_dim(mpa->space, isl_dim_param) > 0) { + p = print_tuple(mpa->space, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + data.print_dim = &print_dim_mpa; + data.user = mpa; + p = isl_print_space(mpa->space, p, 0, &data); + p = isl_printer_print_str(p, " }"); + return p; +} + +__isl_give isl_printer *isl_printer_print_multi_pw_aff( + __isl_take isl_printer *p, __isl_keep isl_multi_pw_aff *mpa) +{ + if (!p || !mpa) + return isl_printer_free(p); + + if (p->output_format == ISL_FORMAT_ISL) + return print_multi_pw_aff_isl(p, mpa); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + return isl_printer_free(p)); +} + +/* Print dimension "pos" of data->space to "p". + * + * data->user is assumed to be an isl_multi_val. + * + * If the current dimension is an output dimension, then print + * the corresponding value. Otherwise, print the name of the dimension. + */ +static __isl_give isl_printer *print_dim_mv(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos) +{ + isl_multi_val *mv = data->user; + + if (data->type == isl_dim_out) + return isl_printer_print_val(p, mv->p[pos]); + else + return print_name(data->space, p, data->type, pos, data->latex); +} + +/* Print the isl_multi_val "mv" to "p" in isl format. + */ +static __isl_give isl_printer *print_multi_val_isl(__isl_take isl_printer *p, + __isl_keep isl_multi_val *mv) +{ + struct isl_print_space_data data = { 0 }; + + if (!mv) + return isl_printer_free(p); + + if (isl_space_dim(mv->space, isl_dim_param) > 0) { + p = print_tuple(mv->space, p, isl_dim_param, &data); + p = isl_printer_print_str(p, " -> "); + } + p = isl_printer_print_str(p, "{ "); + data.print_dim = &print_dim_mv; + data.user = mv; + p = isl_print_space(mv->space, p, 0, &data); + p = isl_printer_print_str(p, " }"); + return p; +} + +/* Print the isl_multi_val "mv" to "p". + * + * Currently only supported in isl format. + */ +__isl_give isl_printer *isl_printer_print_multi_val( + __isl_take isl_printer *p, __isl_keep isl_multi_val *mv) +{ + if (!p || !mv) + return isl_printer_free(p); + + if (p->output_format == ISL_FORMAT_ISL) + return print_multi_val_isl(p, mv); + isl_die(p->ctx, isl_error_unsupported, "unsupported output format", + return isl_printer_free(p)); +} + +/* Print dimension "pos" of data->space to "p". + * + * data->user is assumed to be an isl_multi_union_pw_aff. + * + * The current dimension is necessarily a set dimension, so + * we print the corresponding isl_union_pw_aff, including + * the braces. + */ +static __isl_give isl_printer *print_union_pw_aff_dim(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos) +{ + isl_multi_union_pw_aff *mupa = data->user; + isl_union_pw_aff *upa; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, pos); + p = print_union_pw_aff_body(p, upa); + isl_union_pw_aff_free(upa); + + return p; +} + +/* Print the isl_multi_union_pw_aff "mupa" to "p" in isl format. + */ +static __isl_give isl_printer *print_multi_union_pw_aff_isl( + __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa) +{ + struct isl_print_space_data data = { 0 }; + isl_space *space; + + space = isl_multi_union_pw_aff_get_space(mupa); + if (isl_space_dim(space, isl_dim_param) > 0) { + struct isl_print_space_data space_data = { 0 }; + p = print_tuple(space, p, isl_dim_param, &space_data); + p = isl_printer_print_str(p, s_to[0]); + } + + data.print_dim = &print_union_pw_aff_dim; + data.user = mupa; + + p = isl_print_space(space, p, 0, &data); + isl_space_free(space); + + return p; +} + +/* Print the isl_multi_union_pw_aff "mupa" to "p" in isl format. + * + * We currently only support an isl format. + */ +__isl_give isl_printer *isl_printer_print_multi_union_pw_aff( + __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa) +{ + if (!p || !mupa) + return isl_printer_free(p); + + if (p->output_format == ISL_FORMAT_ISL) + return print_multi_union_pw_aff_isl(p, mupa); + isl_die(isl_printer_get_ctx(p), isl_error_unsupported, + "unsupported output format", return isl_printer_free(p)); +} Index: lib/Analysis/isl/isl_output_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_output_private.h @@ -0,0 +1,27 @@ +#include +#include + +/* Internal data structure for isl_print_space. + * + * latex is set if that is the output format. + * print_dim (if not NULL) is called on each dimension. + * user is set by the caller of print_space and may be used inside print_dim. + * + * space is the global space that is being printed. This field is set by + * print_space. + * type is the tuple of the global space that is currently being printed. + * This field is set by print_space. + */ +struct isl_print_space_data { + int latex; + __isl_give isl_printer *(*print_dim)(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos); + void *user; + + isl_space *space; + enum isl_dim_type type; +}; + +__isl_give isl_printer *isl_print_space(__isl_keep isl_space *space, + __isl_take isl_printer *p, int rational, + struct isl_print_space_data *data); Index: lib/Analysis/isl/isl_point.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_point.c @@ -0,0 +1,619 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +isl_ctx *isl_point_get_ctx(__isl_keep isl_point *pnt) +{ + return pnt ? isl_space_get_ctx(pnt->dim) : NULL; +} + +__isl_give isl_space *isl_point_get_space(__isl_keep isl_point *pnt) +{ + return pnt ? isl_space_copy(pnt->dim) : NULL; +} + +__isl_give isl_point *isl_point_alloc(__isl_take isl_space *dim, + __isl_take isl_vec *vec) +{ + struct isl_point *pnt; + + if (!dim || !vec) + goto error; + + if (vec->size > 1 + isl_space_dim(dim, isl_dim_all)) { + vec = isl_vec_cow(vec); + if (!vec) + goto error; + vec->size = 1 + isl_space_dim(dim, isl_dim_all); + } + + pnt = isl_alloc_type(dim->ctx, struct isl_point); + if (!pnt) + goto error; + + pnt->ref = 1; + pnt->dim = dim; + pnt->vec = vec; + + return pnt; +error: + isl_space_free(dim); + isl_vec_free(vec); + return NULL; +} + +__isl_give isl_point *isl_point_zero(__isl_take isl_space *dim) +{ + isl_vec *vec; + + if (!dim) + return NULL; + vec = isl_vec_alloc(dim->ctx, 1 + isl_space_dim(dim, isl_dim_all)); + if (!vec) + goto error; + isl_int_set_si(vec->el[0], 1); + isl_seq_clr(vec->el + 1, vec->size - 1); + return isl_point_alloc(dim, vec); +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_point *isl_point_dup(__isl_keep isl_point *pnt) +{ + struct isl_point *pnt2; + + if (!pnt) + return NULL; + pnt2 = isl_point_alloc(isl_space_copy(pnt->dim), isl_vec_copy(pnt->vec)); + return pnt2; +} + +__isl_give isl_point *isl_point_cow(__isl_take isl_point *pnt) +{ + struct isl_point *pnt2; + if (!pnt) + return NULL; + + if (pnt->ref == 1) + return pnt; + + pnt2 = isl_point_dup(pnt); + isl_point_free(pnt); + return pnt2; +} + +__isl_give isl_point *isl_point_copy(__isl_keep isl_point *pnt) +{ + if (!pnt) + return NULL; + + pnt->ref++; + return pnt; +} + +void isl_point_free(__isl_take isl_point *pnt) +{ + if (!pnt) + return; + + if (--pnt->ref > 0) + return; + + isl_space_free(pnt->dim); + isl_vec_free(pnt->vec); + free(pnt); +} + +__isl_give isl_point *isl_point_void(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + + return isl_point_alloc(dim, isl_vec_alloc(dim->ctx, 0)); +} + +isl_bool isl_point_is_void(__isl_keep isl_point *pnt) +{ + if (!pnt) + return isl_bool_error; + + return pnt->vec->size == 0; +} + +int isl_point_get_coordinate(__isl_keep isl_point *pnt, + enum isl_dim_type type, int pos, isl_int *v) +{ + if (!pnt || isl_point_is_void(pnt)) + return -1; + + if (pos < 0 || pos >= isl_space_dim(pnt->dim, type)) + isl_die(isl_point_get_ctx(pnt), isl_error_invalid, + "position out of bounds", return -1); + + if (type == isl_dim_set) + pos += isl_space_dim(pnt->dim, isl_dim_param); + isl_int_set(*v, pnt->vec->el[1 + pos]); + + return 0; +} + +/* Return the value of coordinate "pos" of type "type" of "pnt". + */ +__isl_give isl_val *isl_point_get_coordinate_val(__isl_keep isl_point *pnt, + enum isl_dim_type type, int pos) +{ + isl_ctx *ctx; + isl_val *v; + + if (!pnt) + return NULL; + + ctx = isl_point_get_ctx(pnt); + if (isl_point_is_void(pnt)) + isl_die(ctx, isl_error_invalid, + "void point does not have coordinates", return NULL); + if (pos < 0 || pos >= isl_space_dim(pnt->dim, type)) + isl_die(ctx, isl_error_invalid, + "position out of bounds", return NULL); + + if (type == isl_dim_set) + pos += isl_space_dim(pnt->dim, isl_dim_param); + + v = isl_val_rat_from_isl_int(ctx, pnt->vec->el[1 + pos], + pnt->vec->el[0]); + return isl_val_normalize(v); +} + +__isl_give isl_point *isl_point_set_coordinate(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, isl_int v) +{ + if (!pnt || isl_point_is_void(pnt)) + return pnt; + + pnt = isl_point_cow(pnt); + if (!pnt) + return NULL; + pnt->vec = isl_vec_cow(pnt->vec); + if (!pnt->vec) + goto error; + + if (type == isl_dim_set) + pos += isl_space_dim(pnt->dim, isl_dim_param); + + isl_int_set(pnt->vec->el[1 + pos], v); + + return pnt; +error: + isl_point_free(pnt); + return NULL; +} + +/* Replace coordinate "pos" of type "type" of "pnt" by "v". + */ +__isl_give isl_point *isl_point_set_coordinate_val(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, __isl_take isl_val *v) +{ + if (!pnt || !v) + goto error; + if (isl_point_is_void(pnt)) + isl_die(isl_point_get_ctx(pnt), isl_error_invalid, + "void point does not have coordinates", goto error); + if (pos < 0 || pos >= isl_space_dim(pnt->dim, type)) + isl_die(isl_point_get_ctx(pnt), isl_error_invalid, + "position out of bounds", goto error); + if (!isl_val_is_rat(v)) + isl_die(isl_point_get_ctx(pnt), isl_error_invalid, + "expecting rational value", goto error); + + if (isl_int_eq(pnt->vec->el[1 + pos], v->n) && + isl_int_eq(pnt->vec->el[0], v->d)) { + isl_val_free(v); + return pnt; + } + + pnt = isl_point_cow(pnt); + if (!pnt) + goto error; + pnt->vec = isl_vec_cow(pnt->vec); + if (!pnt->vec) + goto error; + + if (isl_int_eq(pnt->vec->el[0], v->d)) { + isl_int_set(pnt->vec->el[1 + pos], v->n); + } else if (isl_int_is_one(v->d)) { + isl_int_mul(pnt->vec->el[1 + pos], pnt->vec->el[0], v->n); + } else { + isl_seq_scale(pnt->vec->el + 1, + pnt->vec->el + 1, v->d, pnt->vec->size - 1); + isl_int_mul(pnt->vec->el[1 + pos], pnt->vec->el[0], v->n); + isl_int_mul(pnt->vec->el[0], pnt->vec->el[0], v->d); + pnt->vec = isl_vec_normalize(pnt->vec); + if (!pnt->vec) + goto error; + } + + isl_val_free(v); + return pnt; +error: + isl_val_free(v); + isl_point_free(pnt); + return NULL; +} + +__isl_give isl_point *isl_point_add_ui(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, unsigned val) +{ + if (!pnt || isl_point_is_void(pnt)) + return pnt; + + pnt = isl_point_cow(pnt); + if (!pnt) + return NULL; + pnt->vec = isl_vec_cow(pnt->vec); + if (!pnt->vec) + goto error; + + if (type == isl_dim_set) + pos += isl_space_dim(pnt->dim, isl_dim_param); + + isl_int_add_ui(pnt->vec->el[1 + pos], pnt->vec->el[1 + pos], val); + + return pnt; +error: + isl_point_free(pnt); + return NULL; +} + +__isl_give isl_point *isl_point_sub_ui(__isl_take isl_point *pnt, + enum isl_dim_type type, int pos, unsigned val) +{ + if (!pnt || isl_point_is_void(pnt)) + return pnt; + + pnt = isl_point_cow(pnt); + if (!pnt) + return NULL; + pnt->vec = isl_vec_cow(pnt->vec); + if (!pnt->vec) + goto error; + + if (type == isl_dim_set) + pos += isl_space_dim(pnt->dim, isl_dim_param); + + isl_int_sub_ui(pnt->vec->el[1 + pos], pnt->vec->el[1 + pos], val); + + return pnt; +error: + isl_point_free(pnt); + return NULL; +} + +struct isl_foreach_point { + struct isl_scan_callback callback; + isl_stat (*fn)(__isl_take isl_point *pnt, void *user); + void *user; + isl_space *dim; +}; + +static isl_stat foreach_point(struct isl_scan_callback *cb, + __isl_take isl_vec *sample) +{ + struct isl_foreach_point *fp = (struct isl_foreach_point *)cb; + isl_point *pnt; + + pnt = isl_point_alloc(isl_space_copy(fp->dim), sample); + + return fp->fn(pnt, fp->user); +} + +isl_stat isl_set_foreach_point(__isl_keep isl_set *set, + isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user) +{ + struct isl_foreach_point fp = { { &foreach_point }, fn, user }; + int i; + + if (!set) + return isl_stat_error; + + fp.dim = isl_set_get_space(set); + if (!fp.dim) + return isl_stat_error; + + set = isl_set_copy(set); + set = isl_set_cow(set); + set = isl_set_make_disjoint(set); + set = isl_set_compute_divs(set); + if (!set) + goto error; + + for (i = 0; i < set->n; ++i) + if (isl_basic_set_scan(isl_basic_set_copy(set->p[i]), + &fp.callback) < 0) + goto error; + + isl_set_free(set); + isl_space_free(fp.dim); + + return isl_stat_ok; +error: + isl_set_free(set); + isl_space_free(fp.dim); + return isl_stat_error; +} + +/* Return 1 if "bmap" contains the point "point". + * "bmap" is assumed to have known divs. + * The point is first extended with the divs and then passed + * to basic_map_contains. + */ +isl_bool isl_basic_map_contains_point(__isl_keep isl_basic_map *bmap, + __isl_keep isl_point *point) +{ + int i; + struct isl_vec *vec; + unsigned dim; + isl_bool contains; + + if (!bmap || !point) + return isl_bool_error; + isl_assert(bmap->ctx, isl_space_is_equal(bmap->dim, point->dim), + return isl_bool_error); + if (bmap->n_div == 0) + return isl_basic_map_contains(bmap, point->vec); + + dim = isl_basic_map_total_dim(bmap) - bmap->n_div; + vec = isl_vec_alloc(bmap->ctx, 1 + dim + bmap->n_div); + if (!vec) + return isl_bool_error; + + isl_seq_cpy(vec->el, point->vec->el, point->vec->size); + for (i = 0; i < bmap->n_div; ++i) { + isl_seq_inner_product(bmap->div[i] + 1, vec->el, + 1 + dim + i, &vec->el[1+dim+i]); + isl_int_fdiv_q(vec->el[1+dim+i], vec->el[1+dim+i], + bmap->div[i][0]); + } + + contains = isl_basic_map_contains(bmap, vec); + + isl_vec_free(vec); + return contains; +} + +int isl_map_contains_point(__isl_keep isl_map *map, __isl_keep isl_point *point) +{ + int i; + int found = 0; + + if (!map || !point) + return -1; + + map = isl_map_copy(map); + map = isl_map_compute_divs(map); + if (!map) + return -1; + + for (i = 0; i < map->n; ++i) { + found = isl_basic_map_contains_point(map->p[i], point); + if (found < 0) + goto error; + if (found) + break; + } + isl_map_free(map); + + return found; +error: + isl_map_free(map); + return -1; +} + +isl_bool isl_set_contains_point(__isl_keep isl_set *set, + __isl_keep isl_point *point) +{ + return isl_map_contains_point((isl_map *)set, point); +} + +__isl_give isl_basic_set *isl_basic_set_from_point(__isl_take isl_point *pnt) +{ + isl_basic_set *bset; + isl_basic_set *model; + + if (!pnt) + return NULL; + + model = isl_basic_set_empty(isl_space_copy(pnt->dim)); + bset = isl_basic_set_from_vec(isl_vec_copy(pnt->vec)); + bset = isl_basic_set_from_underlying_set(bset, model); + isl_point_free(pnt); + + return bset; +} + +__isl_give isl_set *isl_set_from_point(__isl_take isl_point *pnt) +{ + isl_basic_set *bset; + bset = isl_basic_set_from_point(pnt); + return isl_set_from_basic_set(bset); +} + +/* Construct a union set, containing the single element "pnt". + * If "pnt" is void, then return an empty union set. + */ +__isl_give isl_union_set *isl_union_set_from_point(__isl_take isl_point *pnt) +{ + if (!pnt) + return NULL; + if (isl_point_is_void(pnt)) { + isl_space *space; + + space = isl_point_get_space(pnt); + isl_point_free(pnt); + return isl_union_set_empty(space); + } + + return isl_union_set_from_set(isl_set_from_point(pnt)); +} + +__isl_give isl_basic_set *isl_basic_set_box_from_points( + __isl_take isl_point *pnt1, __isl_take isl_point *pnt2) +{ + isl_basic_set *bset; + unsigned total; + int i; + int k; + isl_int t; + + isl_int_init(t); + + if (!pnt1 || !pnt2) + goto error; + + isl_assert(pnt1->dim->ctx, + isl_space_is_equal(pnt1->dim, pnt2->dim), goto error); + + if (isl_point_is_void(pnt1) && isl_point_is_void(pnt2)) { + isl_space *dim = isl_space_copy(pnt1->dim); + isl_point_free(pnt1); + isl_point_free(pnt2); + isl_int_clear(t); + return isl_basic_set_empty(dim); + } + if (isl_point_is_void(pnt1)) { + isl_point_free(pnt1); + isl_int_clear(t); + return isl_basic_set_from_point(pnt2); + } + if (isl_point_is_void(pnt2)) { + isl_point_free(pnt2); + isl_int_clear(t); + return isl_basic_set_from_point(pnt1); + } + + total = isl_space_dim(pnt1->dim, isl_dim_all); + bset = isl_basic_set_alloc_space(isl_space_copy(pnt1->dim), 0, 0, 2 * total); + + for (i = 0; i < total; ++i) { + isl_int_mul(t, pnt1->vec->el[1 + i], pnt2->vec->el[0]); + isl_int_submul(t, pnt2->vec->el[1 + i], pnt1->vec->el[0]); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_clr(bset->ineq[k] + 1, total); + if (isl_int_is_pos(t)) { + isl_int_set_si(bset->ineq[k][1 + i], -1); + isl_int_set(bset->ineq[k][0], pnt1->vec->el[1 + i]); + } else { + isl_int_set_si(bset->ineq[k][1 + i], 1); + isl_int_neg(bset->ineq[k][0], pnt1->vec->el[1 + i]); + } + isl_int_fdiv_q(bset->ineq[k][0], bset->ineq[k][0], pnt1->vec->el[0]); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_clr(bset->ineq[k] + 1, total); + if (isl_int_is_pos(t)) { + isl_int_set_si(bset->ineq[k][1 + i], 1); + isl_int_neg(bset->ineq[k][0], pnt2->vec->el[1 + i]); + } else { + isl_int_set_si(bset->ineq[k][1 + i], -1); + isl_int_set(bset->ineq[k][0], pnt2->vec->el[1 + i]); + } + isl_int_fdiv_q(bset->ineq[k][0], bset->ineq[k][0], pnt2->vec->el[0]); + } + + bset = isl_basic_set_finalize(bset); + + isl_point_free(pnt1); + isl_point_free(pnt2); + + isl_int_clear(t); + + return bset; +error: + isl_point_free(pnt1); + isl_point_free(pnt2); + isl_int_clear(t); + return NULL; +} + +__isl_give isl_set *isl_set_box_from_points(__isl_take isl_point *pnt1, + __isl_take isl_point *pnt2) +{ + isl_basic_set *bset; + bset = isl_basic_set_box_from_points(pnt1, pnt2); + return isl_set_from_basic_set(bset); +} + +/* Print the coordinate at position "pos" of the point "pnt". + */ +static __isl_give isl_printer *print_coordinate(__isl_take isl_printer *p, + struct isl_print_space_data *data, unsigned pos) +{ + isl_point *pnt = data->user; + + p = isl_printer_print_isl_int(p, pnt->vec->el[1 + pos]); + if (!isl_int_is_one(pnt->vec->el[0])) { + p = isl_printer_print_str(p, "/"); + p = isl_printer_print_isl_int(p, pnt->vec->el[0]); + } + + return p; +} + +__isl_give isl_printer *isl_printer_print_point( + __isl_take isl_printer *p, __isl_keep isl_point *pnt) +{ + struct isl_print_space_data data = { 0 }; + int i; + unsigned nparam; + unsigned dim; + + if (!pnt) + return p; + if (isl_point_is_void(pnt)) { + p = isl_printer_print_str(p, "void"); + return p; + } + + nparam = isl_space_dim(pnt->dim, isl_dim_param); + dim = isl_space_dim(pnt->dim, isl_dim_set); + if (nparam > 0) { + p = isl_printer_print_str(p, "["); + for (i = 0; i < nparam; ++i) { + const char *name; + if (i) + p = isl_printer_print_str(p, ", "); + name = isl_space_get_dim_name(pnt->dim, isl_dim_param, i); + if (name) { + p = isl_printer_print_str(p, name); + p = isl_printer_print_str(p, " = "); + } + p = isl_printer_print_isl_int(p, pnt->vec->el[1 + i]); + if (!isl_int_is_one(pnt->vec->el[0])) { + p = isl_printer_print_str(p, "/"); + p = isl_printer_print_isl_int(p, pnt->vec->el[0]); + } + } + p = isl_printer_print_str(p, "]"); + p = isl_printer_print_str(p, " -> "); + } + data.print_dim = &print_coordinate; + data.user = pnt; + p = isl_printer_print_str(p, "{ "); + p = isl_print_space(pnt->dim, p, 0, &data); + p = isl_printer_print_str(p, " }"); + return p; +} Index: lib/Analysis/isl/isl_point_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_point_private.h @@ -0,0 +1,12 @@ +#include +#include +#include + +struct isl_point { + int ref; + isl_space *dim; + struct isl_vec *vec; +}; + +__isl_give isl_point *isl_point_alloc(__isl_take isl_space *dim, + __isl_take isl_vec *vec); Index: lib/Analysis/isl/isl_polynomial.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_polynomial.c @@ -0,0 +1,4918 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include +#define ISL_DIM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned pos(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + switch (type) { + case isl_dim_param: return 0; + case isl_dim_in: return dim->nparam; + case isl_dim_out: return dim->nparam + dim->n_in; + default: return 0; + } +} + +int isl_upoly_is_cst(__isl_keep struct isl_upoly *up) +{ + if (!up) + return -1; + + return up->var < 0; +} + +__isl_keep struct isl_upoly_cst *isl_upoly_as_cst(__isl_keep struct isl_upoly *up) +{ + if (!up) + return NULL; + + isl_assert(up->ctx, up->var < 0, return NULL); + + return (struct isl_upoly_cst *)up; +} + +__isl_keep struct isl_upoly_rec *isl_upoly_as_rec(__isl_keep struct isl_upoly *up) +{ + if (!up) + return NULL; + + isl_assert(up->ctx, up->var >= 0, return NULL); + + return (struct isl_upoly_rec *)up; +} + +/* Compare two polynomials. + * + * Return -1 if "up1" is "smaller" than "up2", 1 if "up1" is "greater" + * than "up2" and 0 if they are equal. + */ +static int isl_upoly_plain_cmp(__isl_keep struct isl_upoly *up1, + __isl_keep struct isl_upoly *up2) +{ + int i; + struct isl_upoly_rec *rec1, *rec2; + + if (up1 == up2) + return 0; + if (!up1) + return -1; + if (!up2) + return 1; + if (up1->var != up2->var) + return up1->var - up2->var; + + if (isl_upoly_is_cst(up1)) { + struct isl_upoly_cst *cst1, *cst2; + int cmp; + + cst1 = isl_upoly_as_cst(up1); + cst2 = isl_upoly_as_cst(up2); + if (!cst1 || !cst2) + return 0; + cmp = isl_int_cmp(cst1->n, cst2->n); + if (cmp != 0) + return cmp; + return isl_int_cmp(cst1->d, cst2->d); + } + + rec1 = isl_upoly_as_rec(up1); + rec2 = isl_upoly_as_rec(up2); + if (!rec1 || !rec2) + return 0; + + if (rec1->n != rec2->n) + return rec1->n - rec2->n; + + for (i = 0; i < rec1->n; ++i) { + int cmp = isl_upoly_plain_cmp(rec1->p[i], rec2->p[i]); + if (cmp != 0) + return cmp; + } + + return 0; +} + +isl_bool isl_upoly_is_equal(__isl_keep struct isl_upoly *up1, + __isl_keep struct isl_upoly *up2) +{ + int i; + struct isl_upoly_rec *rec1, *rec2; + + if (!up1 || !up2) + return isl_bool_error; + if (up1 == up2) + return isl_bool_true; + if (up1->var != up2->var) + return isl_bool_false; + if (isl_upoly_is_cst(up1)) { + struct isl_upoly_cst *cst1, *cst2; + cst1 = isl_upoly_as_cst(up1); + cst2 = isl_upoly_as_cst(up2); + if (!cst1 || !cst2) + return isl_bool_error; + return isl_int_eq(cst1->n, cst2->n) && + isl_int_eq(cst1->d, cst2->d); + } + + rec1 = isl_upoly_as_rec(up1); + rec2 = isl_upoly_as_rec(up2); + if (!rec1 || !rec2) + return isl_bool_error; + + if (rec1->n != rec2->n) + return isl_bool_false; + + for (i = 0; i < rec1->n; ++i) { + isl_bool eq = isl_upoly_is_equal(rec1->p[i], rec2->p[i]); + if (eq < 0 || !eq) + return eq; + } + + return isl_bool_true; +} + +int isl_upoly_is_zero(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return -1; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + + return isl_int_is_zero(cst->n) && isl_int_is_pos(cst->d); +} + +int isl_upoly_sgn(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return 0; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return 0; + + return isl_int_sgn(cst->n); +} + +int isl_upoly_is_nan(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return -1; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + + return isl_int_is_zero(cst->n) && isl_int_is_zero(cst->d); +} + +int isl_upoly_is_infty(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return -1; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + + return isl_int_is_pos(cst->n) && isl_int_is_zero(cst->d); +} + +int isl_upoly_is_neginfty(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return -1; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + + return isl_int_is_neg(cst->n) && isl_int_is_zero(cst->d); +} + +int isl_upoly_is_one(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return -1; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + + return isl_int_eq(cst->n, cst->d) && isl_int_is_pos(cst->d); +} + +int isl_upoly_is_negone(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return -1; + if (!isl_upoly_is_cst(up)) + return 0; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + + return isl_int_is_negone(cst->n) && isl_int_is_one(cst->d); +} + +__isl_give struct isl_upoly_cst *isl_upoly_cst_alloc(struct isl_ctx *ctx) +{ + struct isl_upoly_cst *cst; + + cst = isl_alloc_type(ctx, struct isl_upoly_cst); + if (!cst) + return NULL; + + cst->up.ref = 1; + cst->up.ctx = ctx; + isl_ctx_ref(ctx); + cst->up.var = -1; + + isl_int_init(cst->n); + isl_int_init(cst->d); + + return cst; +} + +__isl_give struct isl_upoly *isl_upoly_zero(struct isl_ctx *ctx) +{ + struct isl_upoly_cst *cst; + + cst = isl_upoly_cst_alloc(ctx); + if (!cst) + return NULL; + + isl_int_set_si(cst->n, 0); + isl_int_set_si(cst->d, 1); + + return &cst->up; +} + +__isl_give struct isl_upoly *isl_upoly_one(struct isl_ctx *ctx) +{ + struct isl_upoly_cst *cst; + + cst = isl_upoly_cst_alloc(ctx); + if (!cst) + return NULL; + + isl_int_set_si(cst->n, 1); + isl_int_set_si(cst->d, 1); + + return &cst->up; +} + +__isl_give struct isl_upoly *isl_upoly_infty(struct isl_ctx *ctx) +{ + struct isl_upoly_cst *cst; + + cst = isl_upoly_cst_alloc(ctx); + if (!cst) + return NULL; + + isl_int_set_si(cst->n, 1); + isl_int_set_si(cst->d, 0); + + return &cst->up; +} + +__isl_give struct isl_upoly *isl_upoly_neginfty(struct isl_ctx *ctx) +{ + struct isl_upoly_cst *cst; + + cst = isl_upoly_cst_alloc(ctx); + if (!cst) + return NULL; + + isl_int_set_si(cst->n, -1); + isl_int_set_si(cst->d, 0); + + return &cst->up; +} + +__isl_give struct isl_upoly *isl_upoly_nan(struct isl_ctx *ctx) +{ + struct isl_upoly_cst *cst; + + cst = isl_upoly_cst_alloc(ctx); + if (!cst) + return NULL; + + isl_int_set_si(cst->n, 0); + isl_int_set_si(cst->d, 0); + + return &cst->up; +} + +__isl_give struct isl_upoly *isl_upoly_rat_cst(struct isl_ctx *ctx, + isl_int n, isl_int d) +{ + struct isl_upoly_cst *cst; + + cst = isl_upoly_cst_alloc(ctx); + if (!cst) + return NULL; + + isl_int_set(cst->n, n); + isl_int_set(cst->d, d); + + return &cst->up; +} + +__isl_give struct isl_upoly_rec *isl_upoly_alloc_rec(struct isl_ctx *ctx, + int var, int size) +{ + struct isl_upoly_rec *rec; + + isl_assert(ctx, var >= 0, return NULL); + isl_assert(ctx, size >= 0, return NULL); + rec = isl_calloc(ctx, struct isl_upoly_rec, + sizeof(struct isl_upoly_rec) + + size * sizeof(struct isl_upoly *)); + if (!rec) + return NULL; + + rec->up.ref = 1; + rec->up.ctx = ctx; + isl_ctx_ref(ctx); + rec->up.var = var; + + rec->n = 0; + rec->size = size; + + return rec; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_reset_domain_space( + __isl_take isl_qpolynomial *qp, __isl_take isl_space *dim) +{ + qp = isl_qpolynomial_cow(qp); + if (!qp || !dim) + goto error; + + isl_space_free(qp->dim); + qp->dim = dim; + + return qp; +error: + isl_qpolynomial_free(qp); + isl_space_free(dim); + return NULL; +} + +/* Reset the space of "qp". This function is called from isl_pw_templ.c + * and doesn't know if the space of an element object is represented + * directly or through its domain. It therefore passes along both. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_reset_space_and_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_space *space, + __isl_take isl_space *domain) +{ + isl_space_free(space); + return isl_qpolynomial_reset_domain_space(qp, domain); +} + +isl_ctx *isl_qpolynomial_get_ctx(__isl_keep isl_qpolynomial *qp) +{ + return qp ? qp->dim->ctx : NULL; +} + +__isl_give isl_space *isl_qpolynomial_get_domain_space( + __isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_space_copy(qp->dim) : NULL; +} + +__isl_give isl_space *isl_qpolynomial_get_space(__isl_keep isl_qpolynomial *qp) +{ + isl_space *space; + if (!qp) + return NULL; + space = isl_space_copy(qp->dim); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, 1); + return space; +} + +/* Externally, an isl_qpolynomial has a map space, but internally, the + * ls field corresponds to the domain of that space. + */ +unsigned isl_qpolynomial_dim(__isl_keep isl_qpolynomial *qp, + enum isl_dim_type type) +{ + if (!qp) + return 0; + if (type == isl_dim_out) + return 1; + if (type == isl_dim_in) + type = isl_dim_set; + return isl_space_dim(qp->dim, type); +} + +isl_bool isl_qpolynomial_is_zero(__isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_upoly_is_zero(qp->upoly) : isl_bool_error; +} + +isl_bool isl_qpolynomial_is_one(__isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_upoly_is_one(qp->upoly) : isl_bool_error; +} + +isl_bool isl_qpolynomial_is_nan(__isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_upoly_is_nan(qp->upoly) : isl_bool_error; +} + +isl_bool isl_qpolynomial_is_infty(__isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_upoly_is_infty(qp->upoly) : isl_bool_error; +} + +isl_bool isl_qpolynomial_is_neginfty(__isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_upoly_is_neginfty(qp->upoly) : isl_bool_error; +} + +int isl_qpolynomial_sgn(__isl_keep isl_qpolynomial *qp) +{ + return qp ? isl_upoly_sgn(qp->upoly) : 0; +} + +static void upoly_free_cst(__isl_take struct isl_upoly_cst *cst) +{ + isl_int_clear(cst->n); + isl_int_clear(cst->d); +} + +static void upoly_free_rec(__isl_take struct isl_upoly_rec *rec) +{ + int i; + + for (i = 0; i < rec->n; ++i) + isl_upoly_free(rec->p[i]); +} + +__isl_give struct isl_upoly *isl_upoly_copy(__isl_keep struct isl_upoly *up) +{ + if (!up) + return NULL; + + up->ref++; + return up; +} + +__isl_give struct isl_upoly *isl_upoly_dup_cst(__isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + struct isl_upoly_cst *dup; + + cst = isl_upoly_as_cst(up); + if (!cst) + return NULL; + + dup = isl_upoly_as_cst(isl_upoly_zero(up->ctx)); + if (!dup) + return NULL; + isl_int_set(dup->n, cst->n); + isl_int_set(dup->d, cst->d); + + return &dup->up; +} + +__isl_give struct isl_upoly *isl_upoly_dup_rec(__isl_keep struct isl_upoly *up) +{ + int i; + struct isl_upoly_rec *rec; + struct isl_upoly_rec *dup; + + rec = isl_upoly_as_rec(up); + if (!rec) + return NULL; + + dup = isl_upoly_alloc_rec(up->ctx, up->var, rec->n); + if (!dup) + return NULL; + + for (i = 0; i < rec->n; ++i) { + dup->p[i] = isl_upoly_copy(rec->p[i]); + if (!dup->p[i]) + goto error; + dup->n++; + } + + return &dup->up; +error: + isl_upoly_free(&dup->up); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_dup(__isl_keep struct isl_upoly *up) +{ + if (!up) + return NULL; + + if (isl_upoly_is_cst(up)) + return isl_upoly_dup_cst(up); + else + return isl_upoly_dup_rec(up); +} + +__isl_give struct isl_upoly *isl_upoly_cow(__isl_take struct isl_upoly *up) +{ + if (!up) + return NULL; + + if (up->ref == 1) + return up; + up->ref--; + return isl_upoly_dup(up); +} + +void isl_upoly_free(__isl_take struct isl_upoly *up) +{ + if (!up) + return; + + if (--up->ref > 0) + return; + + if (up->var < 0) + upoly_free_cst((struct isl_upoly_cst *)up); + else + upoly_free_rec((struct isl_upoly_rec *)up); + + isl_ctx_deref(up->ctx); + free(up); +} + +static void isl_upoly_cst_reduce(__isl_keep struct isl_upoly_cst *cst) +{ + isl_int gcd; + + isl_int_init(gcd); + isl_int_gcd(gcd, cst->n, cst->d); + if (!isl_int_is_zero(gcd) && !isl_int_is_one(gcd)) { + isl_int_divexact(cst->n, cst->n, gcd); + isl_int_divexact(cst->d, cst->d, gcd); + } + isl_int_clear(gcd); +} + +__isl_give struct isl_upoly *isl_upoly_sum_cst(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2) +{ + struct isl_upoly_cst *cst1; + struct isl_upoly_cst *cst2; + + up1 = isl_upoly_cow(up1); + if (!up1 || !up2) + goto error; + + cst1 = isl_upoly_as_cst(up1); + cst2 = isl_upoly_as_cst(up2); + + if (isl_int_eq(cst1->d, cst2->d)) + isl_int_add(cst1->n, cst1->n, cst2->n); + else { + isl_int_mul(cst1->n, cst1->n, cst2->d); + isl_int_addmul(cst1->n, cst2->n, cst1->d); + isl_int_mul(cst1->d, cst1->d, cst2->d); + } + + isl_upoly_cst_reduce(cst1); + + isl_upoly_free(up2); + return up1; +error: + isl_upoly_free(up1); + isl_upoly_free(up2); + return NULL; +} + +static __isl_give struct isl_upoly *replace_by_zero( + __isl_take struct isl_upoly *up) +{ + struct isl_ctx *ctx; + + if (!up) + return NULL; + ctx = up->ctx; + isl_upoly_free(up); + return isl_upoly_zero(ctx); +} + +static __isl_give struct isl_upoly *replace_by_constant_term( + __isl_take struct isl_upoly *up) +{ + struct isl_upoly_rec *rec; + struct isl_upoly *cst; + + if (!up) + return NULL; + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + cst = isl_upoly_copy(rec->p[0]); + isl_upoly_free(up); + return cst; +error: + isl_upoly_free(up); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_sum(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2) +{ + int i; + struct isl_upoly_rec *rec1, *rec2; + + if (!up1 || !up2) + goto error; + + if (isl_upoly_is_nan(up1)) { + isl_upoly_free(up2); + return up1; + } + + if (isl_upoly_is_nan(up2)) { + isl_upoly_free(up1); + return up2; + } + + if (isl_upoly_is_zero(up1)) { + isl_upoly_free(up1); + return up2; + } + + if (isl_upoly_is_zero(up2)) { + isl_upoly_free(up2); + return up1; + } + + if (up1->var < up2->var) + return isl_upoly_sum(up2, up1); + + if (up2->var < up1->var) { + struct isl_upoly_rec *rec; + if (isl_upoly_is_infty(up2) || isl_upoly_is_neginfty(up2)) { + isl_upoly_free(up1); + return up2; + } + up1 = isl_upoly_cow(up1); + rec = isl_upoly_as_rec(up1); + if (!rec) + goto error; + rec->p[0] = isl_upoly_sum(rec->p[0], up2); + if (rec->n == 1) + up1 = replace_by_constant_term(up1); + return up1; + } + + if (isl_upoly_is_cst(up1)) + return isl_upoly_sum_cst(up1, up2); + + rec1 = isl_upoly_as_rec(up1); + rec2 = isl_upoly_as_rec(up2); + if (!rec1 || !rec2) + goto error; + + if (rec1->n < rec2->n) + return isl_upoly_sum(up2, up1); + + up1 = isl_upoly_cow(up1); + rec1 = isl_upoly_as_rec(up1); + if (!rec1) + goto error; + + for (i = rec2->n - 1; i >= 0; --i) { + rec1->p[i] = isl_upoly_sum(rec1->p[i], + isl_upoly_copy(rec2->p[i])); + if (!rec1->p[i]) + goto error; + if (i == rec1->n - 1 && isl_upoly_is_zero(rec1->p[i])) { + isl_upoly_free(rec1->p[i]); + rec1->n--; + } + } + + if (rec1->n == 0) + up1 = replace_by_zero(up1); + else if (rec1->n == 1) + up1 = replace_by_constant_term(up1); + + isl_upoly_free(up2); + + return up1; +error: + isl_upoly_free(up1); + isl_upoly_free(up2); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_cst_add_isl_int( + __isl_take struct isl_upoly *up, isl_int v) +{ + struct isl_upoly_cst *cst; + + up = isl_upoly_cow(up); + if (!up) + return NULL; + + cst = isl_upoly_as_cst(up); + + isl_int_addmul(cst->n, cst->d, v); + + return up; +} + +__isl_give struct isl_upoly *isl_upoly_add_isl_int( + __isl_take struct isl_upoly *up, isl_int v) +{ + struct isl_upoly_rec *rec; + + if (!up) + return NULL; + + if (isl_upoly_is_cst(up)) + return isl_upoly_cst_add_isl_int(up, v); + + up = isl_upoly_cow(up); + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + rec->p[0] = isl_upoly_add_isl_int(rec->p[0], v); + if (!rec->p[0]) + goto error; + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_cst_mul_isl_int( + __isl_take struct isl_upoly *up, isl_int v) +{ + struct isl_upoly_cst *cst; + + if (isl_upoly_is_zero(up)) + return up; + + up = isl_upoly_cow(up); + if (!up) + return NULL; + + cst = isl_upoly_as_cst(up); + + isl_int_mul(cst->n, cst->n, v); + + return up; +} + +__isl_give struct isl_upoly *isl_upoly_mul_isl_int( + __isl_take struct isl_upoly *up, isl_int v) +{ + int i; + struct isl_upoly_rec *rec; + + if (!up) + return NULL; + + if (isl_upoly_is_cst(up)) + return isl_upoly_cst_mul_isl_int(up, v); + + up = isl_upoly_cow(up); + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + rec->p[i] = isl_upoly_mul_isl_int(rec->p[i], v); + if (!rec->p[i]) + goto error; + } + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +/* Multiply the constant polynomial "up" by "v". + */ +static __isl_give struct isl_upoly *isl_upoly_cst_scale_val( + __isl_take struct isl_upoly *up, __isl_keep isl_val *v) +{ + struct isl_upoly_cst *cst; + + if (isl_upoly_is_zero(up)) + return up; + + up = isl_upoly_cow(up); + if (!up) + return NULL; + + cst = isl_upoly_as_cst(up); + + isl_int_mul(cst->n, cst->n, v->n); + isl_int_mul(cst->d, cst->d, v->d); + isl_upoly_cst_reduce(cst); + + return up; +} + +/* Multiply the polynomial "up" by "v". + */ +static __isl_give struct isl_upoly *isl_upoly_scale_val( + __isl_take struct isl_upoly *up, __isl_keep isl_val *v) +{ + int i; + struct isl_upoly_rec *rec; + + if (!up) + return NULL; + + if (isl_upoly_is_cst(up)) + return isl_upoly_cst_scale_val(up, v); + + up = isl_upoly_cow(up); + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + rec->p[i] = isl_upoly_scale_val(rec->p[i], v); + if (!rec->p[i]) + goto error; + } + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_mul_cst(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2) +{ + struct isl_upoly_cst *cst1; + struct isl_upoly_cst *cst2; + + up1 = isl_upoly_cow(up1); + if (!up1 || !up2) + goto error; + + cst1 = isl_upoly_as_cst(up1); + cst2 = isl_upoly_as_cst(up2); + + isl_int_mul(cst1->n, cst1->n, cst2->n); + isl_int_mul(cst1->d, cst1->d, cst2->d); + + isl_upoly_cst_reduce(cst1); + + isl_upoly_free(up2); + return up1; +error: + isl_upoly_free(up1); + isl_upoly_free(up2); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_mul_rec(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2) +{ + struct isl_upoly_rec *rec1; + struct isl_upoly_rec *rec2; + struct isl_upoly_rec *res = NULL; + int i, j; + int size; + + rec1 = isl_upoly_as_rec(up1); + rec2 = isl_upoly_as_rec(up2); + if (!rec1 || !rec2) + goto error; + size = rec1->n + rec2->n - 1; + res = isl_upoly_alloc_rec(up1->ctx, up1->var, size); + if (!res) + goto error; + + for (i = 0; i < rec1->n; ++i) { + res->p[i] = isl_upoly_mul(isl_upoly_copy(rec2->p[0]), + isl_upoly_copy(rec1->p[i])); + if (!res->p[i]) + goto error; + res->n++; + } + for (; i < size; ++i) { + res->p[i] = isl_upoly_zero(up1->ctx); + if (!res->p[i]) + goto error; + res->n++; + } + for (i = 0; i < rec1->n; ++i) { + for (j = 1; j < rec2->n; ++j) { + struct isl_upoly *up; + up = isl_upoly_mul(isl_upoly_copy(rec2->p[j]), + isl_upoly_copy(rec1->p[i])); + res->p[i + j] = isl_upoly_sum(res->p[i + j], up); + if (!res->p[i + j]) + goto error; + } + } + + isl_upoly_free(up1); + isl_upoly_free(up2); + + return &res->up; +error: + isl_upoly_free(up1); + isl_upoly_free(up2); + isl_upoly_free(&res->up); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_mul(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2) +{ + if (!up1 || !up2) + goto error; + + if (isl_upoly_is_nan(up1)) { + isl_upoly_free(up2); + return up1; + } + + if (isl_upoly_is_nan(up2)) { + isl_upoly_free(up1); + return up2; + } + + if (isl_upoly_is_zero(up1)) { + isl_upoly_free(up2); + return up1; + } + + if (isl_upoly_is_zero(up2)) { + isl_upoly_free(up1); + return up2; + } + + if (isl_upoly_is_one(up1)) { + isl_upoly_free(up1); + return up2; + } + + if (isl_upoly_is_one(up2)) { + isl_upoly_free(up2); + return up1; + } + + if (up1->var < up2->var) + return isl_upoly_mul(up2, up1); + + if (up2->var < up1->var) { + int i; + struct isl_upoly_rec *rec; + if (isl_upoly_is_infty(up2) || isl_upoly_is_neginfty(up2)) { + isl_ctx *ctx = up1->ctx; + isl_upoly_free(up1); + isl_upoly_free(up2); + return isl_upoly_nan(ctx); + } + up1 = isl_upoly_cow(up1); + rec = isl_upoly_as_rec(up1); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + rec->p[i] = isl_upoly_mul(rec->p[i], + isl_upoly_copy(up2)); + if (!rec->p[i]) + goto error; + } + isl_upoly_free(up2); + return up1; + } + + if (isl_upoly_is_cst(up1)) + return isl_upoly_mul_cst(up1, up2); + + return isl_upoly_mul_rec(up1, up2); +error: + isl_upoly_free(up1); + isl_upoly_free(up2); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_pow(__isl_take struct isl_upoly *up, + unsigned power) +{ + struct isl_upoly *res; + + if (!up) + return NULL; + if (power == 1) + return up; + + if (power % 2) + res = isl_upoly_copy(up); + else + res = isl_upoly_one(up->ctx); + + while (power >>= 1) { + up = isl_upoly_mul(up, isl_upoly_copy(up)); + if (power % 2) + res = isl_upoly_mul(res, isl_upoly_copy(up)); + } + + isl_upoly_free(up); + return res; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_alloc(__isl_take isl_space *dim, + unsigned n_div, __isl_take struct isl_upoly *up) +{ + struct isl_qpolynomial *qp = NULL; + unsigned total; + + if (!dim || !up) + goto error; + + if (!isl_space_is_set(dim)) + isl_die(isl_space_get_ctx(dim), isl_error_invalid, + "domain of polynomial should be a set", goto error); + + total = isl_space_dim(dim, isl_dim_all); + + qp = isl_calloc_type(dim->ctx, struct isl_qpolynomial); + if (!qp) + goto error; + + qp->ref = 1; + qp->div = isl_mat_alloc(dim->ctx, n_div, 1 + 1 + total + n_div); + if (!qp->div) + goto error; + + qp->dim = dim; + qp->upoly = up; + + return qp; +error: + isl_space_free(dim); + isl_upoly_free(up); + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_copy(__isl_keep isl_qpolynomial *qp) +{ + if (!qp) + return NULL; + + qp->ref++; + return qp; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_dup(__isl_keep isl_qpolynomial *qp) +{ + struct isl_qpolynomial *dup; + + if (!qp) + return NULL; + + dup = isl_qpolynomial_alloc(isl_space_copy(qp->dim), qp->div->n_row, + isl_upoly_copy(qp->upoly)); + if (!dup) + return NULL; + isl_mat_free(dup->div); + dup->div = isl_mat_copy(qp->div); + if (!dup->div) + goto error; + + return dup; +error: + isl_qpolynomial_free(dup); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_cow(__isl_take isl_qpolynomial *qp) +{ + if (!qp) + return NULL; + + if (qp->ref == 1) + return qp; + qp->ref--; + return isl_qpolynomial_dup(qp); +} + +__isl_null isl_qpolynomial *isl_qpolynomial_free( + __isl_take isl_qpolynomial *qp) +{ + if (!qp) + return NULL; + + if (--qp->ref > 0) + return NULL; + + isl_space_free(qp->dim); + isl_mat_free(qp->div); + isl_upoly_free(qp->upoly); + + free(qp); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_var_pow(isl_ctx *ctx, int pos, int power) +{ + int i; + struct isl_upoly_rec *rec; + struct isl_upoly_cst *cst; + + rec = isl_upoly_alloc_rec(ctx, pos, 1 + power); + if (!rec) + return NULL; + for (i = 0; i < 1 + power; ++i) { + rec->p[i] = isl_upoly_zero(ctx); + if (!rec->p[i]) + goto error; + rec->n++; + } + cst = isl_upoly_as_cst(rec->p[power]); + isl_int_set_si(cst->n, 1); + + return &rec->up; +error: + isl_upoly_free(&rec->up); + return NULL; +} + +/* r array maps original positions to new positions. + */ +static __isl_give struct isl_upoly *reorder(__isl_take struct isl_upoly *up, + int *r) +{ + int i; + struct isl_upoly_rec *rec; + struct isl_upoly *base; + struct isl_upoly *res; + + if (isl_upoly_is_cst(up)) + return up; + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + isl_assert(up->ctx, rec->n >= 1, goto error); + + base = isl_upoly_var_pow(up->ctx, r[up->var], 1); + res = reorder(isl_upoly_copy(rec->p[rec->n - 1]), r); + + for (i = rec->n - 2; i >= 0; --i) { + res = isl_upoly_mul(res, isl_upoly_copy(base)); + res = isl_upoly_sum(res, reorder(isl_upoly_copy(rec->p[i]), r)); + } + + isl_upoly_free(base); + isl_upoly_free(up); + + return res; +error: + isl_upoly_free(up); + return NULL; +} + +static int compatible_divs(__isl_keep isl_mat *div1, __isl_keep isl_mat *div2) +{ + int n_row, n_col; + int equal; + + isl_assert(div1->ctx, div1->n_row >= div2->n_row && + div1->n_col >= div2->n_col, return -1); + + if (div1->n_row == div2->n_row) + return isl_mat_is_equal(div1, div2); + + n_row = div1->n_row; + n_col = div1->n_col; + div1->n_row = div2->n_row; + div1->n_col = div2->n_col; + + equal = isl_mat_is_equal(div1, div2); + + div1->n_row = n_row; + div1->n_col = n_col; + + return equal; +} + +static int cmp_row(__isl_keep isl_mat *div, int i, int j) +{ + int li, lj; + + li = isl_seq_last_non_zero(div->row[i], div->n_col); + lj = isl_seq_last_non_zero(div->row[j], div->n_col); + + if (li != lj) + return li - lj; + + return isl_seq_cmp(div->row[i], div->row[j], div->n_col); +} + +struct isl_div_sort_info { + isl_mat *div; + int row; +}; + +static int div_sort_cmp(const void *p1, const void *p2) +{ + const struct isl_div_sort_info *i1, *i2; + i1 = (const struct isl_div_sort_info *) p1; + i2 = (const struct isl_div_sort_info *) p2; + + return cmp_row(i1->div, i1->row, i2->row); +} + +/* Sort divs and remove duplicates. + */ +static __isl_give isl_qpolynomial *sort_divs(__isl_take isl_qpolynomial *qp) +{ + int i; + int skip; + int len; + struct isl_div_sort_info *array = NULL; + int *pos = NULL, *at = NULL; + int *reordering = NULL; + unsigned div_pos; + + if (!qp) + return NULL; + if (qp->div->n_row <= 1) + return qp; + + div_pos = isl_space_dim(qp->dim, isl_dim_all); + + array = isl_alloc_array(qp->div->ctx, struct isl_div_sort_info, + qp->div->n_row); + pos = isl_alloc_array(qp->div->ctx, int, qp->div->n_row); + at = isl_alloc_array(qp->div->ctx, int, qp->div->n_row); + len = qp->div->n_col - 2; + reordering = isl_alloc_array(qp->div->ctx, int, len); + if (!array || !pos || !at || !reordering) + goto error; + + for (i = 0; i < qp->div->n_row; ++i) { + array[i].div = qp->div; + array[i].row = i; + pos[i] = i; + at[i] = i; + } + + qsort(array, qp->div->n_row, sizeof(struct isl_div_sort_info), + div_sort_cmp); + + for (i = 0; i < div_pos; ++i) + reordering[i] = i; + + for (i = 0; i < qp->div->n_row; ++i) { + if (pos[array[i].row] == i) + continue; + qp->div = isl_mat_swap_rows(qp->div, i, pos[array[i].row]); + pos[at[i]] = pos[array[i].row]; + at[pos[array[i].row]] = at[i]; + at[i] = array[i].row; + pos[array[i].row] = i; + } + + skip = 0; + for (i = 0; i < len - div_pos; ++i) { + if (i > 0 && + isl_seq_eq(qp->div->row[i - skip - 1], + qp->div->row[i - skip], qp->div->n_col)) { + qp->div = isl_mat_drop_rows(qp->div, i - skip, 1); + isl_mat_col_add(qp->div, 2 + div_pos + i - skip - 1, + 2 + div_pos + i - skip); + qp->div = isl_mat_drop_cols(qp->div, + 2 + div_pos + i - skip, 1); + skip++; + } + reordering[div_pos + array[i].row] = div_pos + i - skip; + } + + qp->upoly = reorder(qp->upoly, reordering); + + if (!qp->upoly || !qp->div) + goto error; + + free(at); + free(pos); + free(array); + free(reordering); + + return qp; +error: + free(at); + free(pos); + free(array); + free(reordering); + isl_qpolynomial_free(qp); + return NULL; +} + +static __isl_give struct isl_upoly *expand(__isl_take struct isl_upoly *up, + int *exp, int first) +{ + int i; + struct isl_upoly_rec *rec; + + if (isl_upoly_is_cst(up)) + return up; + + if (up->var < first) + return up; + + if (exp[up->var - first] == up->var - first) + return up; + + up = isl_upoly_cow(up); + if (!up) + goto error; + + up->var = exp[up->var - first] + first; + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + rec->p[i] = expand(rec->p[i], exp, first); + if (!rec->p[i]) + goto error; + } + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +static __isl_give isl_qpolynomial *with_merged_divs( + __isl_give isl_qpolynomial *(*fn)(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2), + __isl_take isl_qpolynomial *qp1, __isl_take isl_qpolynomial *qp2) +{ + int *exp1 = NULL; + int *exp2 = NULL; + isl_mat *div = NULL; + int n_div1, n_div2; + + qp1 = isl_qpolynomial_cow(qp1); + qp2 = isl_qpolynomial_cow(qp2); + + if (!qp1 || !qp2) + goto error; + + isl_assert(qp1->div->ctx, qp1->div->n_row >= qp2->div->n_row && + qp1->div->n_col >= qp2->div->n_col, goto error); + + n_div1 = qp1->div->n_row; + n_div2 = qp2->div->n_row; + exp1 = isl_alloc_array(qp1->div->ctx, int, n_div1); + exp2 = isl_alloc_array(qp2->div->ctx, int, n_div2); + if ((n_div1 && !exp1) || (n_div2 && !exp2)) + goto error; + + div = isl_merge_divs(qp1->div, qp2->div, exp1, exp2); + if (!div) + goto error; + + isl_mat_free(qp1->div); + qp1->div = isl_mat_copy(div); + isl_mat_free(qp2->div); + qp2->div = isl_mat_copy(div); + + qp1->upoly = expand(qp1->upoly, exp1, div->n_col - div->n_row - 2); + qp2->upoly = expand(qp2->upoly, exp2, div->n_col - div->n_row - 2); + + if (!qp1->upoly || !qp2->upoly) + goto error; + + isl_mat_free(div); + free(exp1); + free(exp2); + + return fn(qp1, qp2); +error: + isl_mat_free(div); + free(exp1); + free(exp2); + isl_qpolynomial_free(qp1); + isl_qpolynomial_free(qp2); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_add(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2) +{ + qp1 = isl_qpolynomial_cow(qp1); + + if (!qp1 || !qp2) + goto error; + + if (qp1->div->n_row < qp2->div->n_row) + return isl_qpolynomial_add(qp2, qp1); + + isl_assert(qp1->dim->ctx, isl_space_is_equal(qp1->dim, qp2->dim), goto error); + if (!compatible_divs(qp1->div, qp2->div)) + return with_merged_divs(isl_qpolynomial_add, qp1, qp2); + + qp1->upoly = isl_upoly_sum(qp1->upoly, isl_upoly_copy(qp2->upoly)); + if (!qp1->upoly) + goto error; + + isl_qpolynomial_free(qp2); + + return qp1; +error: + isl_qpolynomial_free(qp1); + isl_qpolynomial_free(qp2); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_add_on_domain( + __isl_keep isl_set *dom, + __isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2) +{ + qp1 = isl_qpolynomial_add(qp1, qp2); + qp1 = isl_qpolynomial_gist(qp1, isl_set_copy(dom)); + return qp1; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_sub(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2) +{ + return isl_qpolynomial_add(qp1, isl_qpolynomial_neg(qp2)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_add_isl_int( + __isl_take isl_qpolynomial *qp, isl_int v) +{ + if (isl_int_is_zero(v)) + return qp; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + qp->upoly = isl_upoly_add_isl_int(qp->upoly, v); + if (!qp->upoly) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; + +} + +__isl_give isl_qpolynomial *isl_qpolynomial_neg(__isl_take isl_qpolynomial *qp) +{ + if (!qp) + return NULL; + + return isl_qpolynomial_mul_isl_int(qp, qp->dim->ctx->negone); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_mul_isl_int( + __isl_take isl_qpolynomial *qp, isl_int v) +{ + if (isl_int_is_one(v)) + return qp; + + if (qp && isl_int_is_zero(v)) { + isl_qpolynomial *zero; + zero = isl_qpolynomial_zero_on_domain(isl_space_copy(qp->dim)); + isl_qpolynomial_free(qp); + return zero; + } + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + qp->upoly = isl_upoly_mul_isl_int(qp->upoly, v); + if (!qp->upoly) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_scale( + __isl_take isl_qpolynomial *qp, isl_int v) +{ + return isl_qpolynomial_mul_isl_int(qp, v); +} + +/* Multiply "qp" by "v". + */ +__isl_give isl_qpolynomial *isl_qpolynomial_scale_val( + __isl_take isl_qpolynomial *qp, __isl_take isl_val *v) +{ + if (!qp || !v) + goto error; + + if (!isl_val_is_rat(v)) + isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid, + "expecting rational factor", goto error); + + if (isl_val_is_one(v)) { + isl_val_free(v); + return qp; + } + + if (isl_val_is_zero(v)) { + isl_space *space; + + space = isl_qpolynomial_get_domain_space(qp); + isl_qpolynomial_free(qp); + isl_val_free(v); + return isl_qpolynomial_zero_on_domain(space); + } + + qp = isl_qpolynomial_cow(qp); + if (!qp) + goto error; + + qp->upoly = isl_upoly_scale_val(qp->upoly, v); + if (!qp->upoly) + qp = isl_qpolynomial_free(qp); + + isl_val_free(v); + return qp; +error: + isl_val_free(v); + isl_qpolynomial_free(qp); + return NULL; +} + +/* Divide "qp" by "v". + */ +__isl_give isl_qpolynomial *isl_qpolynomial_scale_down_val( + __isl_take isl_qpolynomial *qp, __isl_take isl_val *v) +{ + if (!qp || !v) + goto error; + + if (!isl_val_is_rat(v)) + isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid, + "expecting rational factor", goto error); + if (isl_val_is_zero(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "cannot scale down by zero", goto error); + + return isl_qpolynomial_scale_val(qp, isl_val_inv(v)); +error: + isl_val_free(v); + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_mul(__isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2) +{ + qp1 = isl_qpolynomial_cow(qp1); + + if (!qp1 || !qp2) + goto error; + + if (qp1->div->n_row < qp2->div->n_row) + return isl_qpolynomial_mul(qp2, qp1); + + isl_assert(qp1->dim->ctx, isl_space_is_equal(qp1->dim, qp2->dim), goto error); + if (!compatible_divs(qp1->div, qp2->div)) + return with_merged_divs(isl_qpolynomial_mul, qp1, qp2); + + qp1->upoly = isl_upoly_mul(qp1->upoly, isl_upoly_copy(qp2->upoly)); + if (!qp1->upoly) + goto error; + + isl_qpolynomial_free(qp2); + + return qp1; +error: + isl_qpolynomial_free(qp1); + isl_qpolynomial_free(qp2); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_pow(__isl_take isl_qpolynomial *qp, + unsigned power) +{ + qp = isl_qpolynomial_cow(qp); + + if (!qp) + return NULL; + + qp->upoly = isl_upoly_pow(qp->upoly, power); + if (!qp->upoly) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_pow( + __isl_take isl_pw_qpolynomial *pwqp, unsigned power) +{ + int i; + + if (power == 1) + return pwqp; + + pwqp = isl_pw_qpolynomial_cow(pwqp); + if (!pwqp) + return NULL; + + for (i = 0; i < pwqp->n; ++i) { + pwqp->p[i].qp = isl_qpolynomial_pow(pwqp->p[i].qp, power); + if (!pwqp->p[i].qp) + return isl_pw_qpolynomial_free(pwqp); + } + + return pwqp; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_zero_on_domain( + __isl_take isl_space *dim) +{ + if (!dim) + return NULL; + return isl_qpolynomial_alloc(dim, 0, isl_upoly_zero(dim->ctx)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_one_on_domain( + __isl_take isl_space *dim) +{ + if (!dim) + return NULL; + return isl_qpolynomial_alloc(dim, 0, isl_upoly_one(dim->ctx)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_infty_on_domain( + __isl_take isl_space *dim) +{ + if (!dim) + return NULL; + return isl_qpolynomial_alloc(dim, 0, isl_upoly_infty(dim->ctx)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_neginfty_on_domain( + __isl_take isl_space *dim) +{ + if (!dim) + return NULL; + return isl_qpolynomial_alloc(dim, 0, isl_upoly_neginfty(dim->ctx)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_nan_on_domain( + __isl_take isl_space *dim) +{ + if (!dim) + return NULL; + return isl_qpolynomial_alloc(dim, 0, isl_upoly_nan(dim->ctx)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_cst_on_domain( + __isl_take isl_space *dim, + isl_int v) +{ + struct isl_qpolynomial *qp; + struct isl_upoly_cst *cst; + + if (!dim) + return NULL; + + qp = isl_qpolynomial_alloc(dim, 0, isl_upoly_zero(dim->ctx)); + if (!qp) + return NULL; + + cst = isl_upoly_as_cst(qp->upoly); + isl_int_set(cst->n, v); + + return qp; +} + +int isl_qpolynomial_is_cst(__isl_keep isl_qpolynomial *qp, + isl_int *n, isl_int *d) +{ + struct isl_upoly_cst *cst; + + if (!qp) + return -1; + + if (!isl_upoly_is_cst(qp->upoly)) + return 0; + + cst = isl_upoly_as_cst(qp->upoly); + if (!cst) + return -1; + + if (n) + isl_int_set(*n, cst->n); + if (d) + isl_int_set(*d, cst->d); + + return 1; +} + +/* Return the constant term of "up". + */ +static __isl_give isl_val *isl_upoly_get_constant_val( + __isl_keep struct isl_upoly *up) +{ + struct isl_upoly_cst *cst; + + if (!up) + return NULL; + + while (!isl_upoly_is_cst(up)) { + struct isl_upoly_rec *rec; + + rec = isl_upoly_as_rec(up); + if (!rec) + return NULL; + up = rec->p[0]; + } + + cst = isl_upoly_as_cst(up); + if (!cst) + return NULL; + return isl_val_rat_from_isl_int(cst->up.ctx, cst->n, cst->d); +} + +/* Return the constant term of "qp". + */ +__isl_give isl_val *isl_qpolynomial_get_constant_val( + __isl_keep isl_qpolynomial *qp) +{ + if (!qp) + return NULL; + + return isl_upoly_get_constant_val(qp->upoly); +} + +int isl_upoly_is_affine(__isl_keep struct isl_upoly *up) +{ + int is_cst; + struct isl_upoly_rec *rec; + + if (!up) + return -1; + + if (up->var < 0) + return 1; + + rec = isl_upoly_as_rec(up); + if (!rec) + return -1; + + if (rec->n > 2) + return 0; + + isl_assert(up->ctx, rec->n > 1, return -1); + + is_cst = isl_upoly_is_cst(rec->p[1]); + if (is_cst < 0) + return -1; + if (!is_cst) + return 0; + + return isl_upoly_is_affine(rec->p[0]); +} + +int isl_qpolynomial_is_affine(__isl_keep isl_qpolynomial *qp) +{ + if (!qp) + return -1; + + if (qp->div->n_row > 0) + return 0; + + return isl_upoly_is_affine(qp->upoly); +} + +static void update_coeff(__isl_keep isl_vec *aff, + __isl_keep struct isl_upoly_cst *cst, int pos) +{ + isl_int gcd; + isl_int f; + + if (isl_int_is_zero(cst->n)) + return; + + isl_int_init(gcd); + isl_int_init(f); + isl_int_gcd(gcd, cst->d, aff->el[0]); + isl_int_divexact(f, cst->d, gcd); + isl_int_divexact(gcd, aff->el[0], gcd); + isl_seq_scale(aff->el, aff->el, f, aff->size); + isl_int_mul(aff->el[1 + pos], gcd, cst->n); + isl_int_clear(gcd); + isl_int_clear(f); +} + +int isl_upoly_update_affine(__isl_keep struct isl_upoly *up, + __isl_keep isl_vec *aff) +{ + struct isl_upoly_cst *cst; + struct isl_upoly_rec *rec; + + if (!up || !aff) + return -1; + + if (up->var < 0) { + struct isl_upoly_cst *cst; + + cst = isl_upoly_as_cst(up); + if (!cst) + return -1; + update_coeff(aff, cst, 0); + return 0; + } + + rec = isl_upoly_as_rec(up); + if (!rec) + return -1; + isl_assert(up->ctx, rec->n == 2, return -1); + + cst = isl_upoly_as_cst(rec->p[1]); + if (!cst) + return -1; + update_coeff(aff, cst, 1 + up->var); + + return isl_upoly_update_affine(rec->p[0], aff); +} + +__isl_give isl_vec *isl_qpolynomial_extract_affine( + __isl_keep isl_qpolynomial *qp) +{ + isl_vec *aff; + unsigned d; + + if (!qp) + return NULL; + + d = isl_space_dim(qp->dim, isl_dim_all); + aff = isl_vec_alloc(qp->div->ctx, 2 + d + qp->div->n_row); + if (!aff) + return NULL; + + isl_seq_clr(aff->el + 1, 1 + d + qp->div->n_row); + isl_int_set_si(aff->el[0], 1); + + if (isl_upoly_update_affine(qp->upoly, aff) < 0) + goto error; + + return aff; +error: + isl_vec_free(aff); + return NULL; +} + +/* Compare two quasi-polynomials. + * + * Return -1 if "qp1" is "smaller" than "qp2", 1 if "qp1" is "greater" + * than "qp2" and 0 if they are equal. + */ +int isl_qpolynomial_plain_cmp(__isl_keep isl_qpolynomial *qp1, + __isl_keep isl_qpolynomial *qp2) +{ + int cmp; + + if (qp1 == qp2) + return 0; + if (!qp1) + return -1; + if (!qp2) + return 1; + + cmp = isl_space_cmp(qp1->dim, qp2->dim); + if (cmp != 0) + return cmp; + + cmp = isl_local_cmp(qp1->div, qp2->div); + if (cmp != 0) + return cmp; + + return isl_upoly_plain_cmp(qp1->upoly, qp2->upoly); +} + +/* Is "qp1" obviously equal to "qp2"? + * + * NaN is not equal to anything, not even to another NaN. + */ +isl_bool isl_qpolynomial_plain_is_equal(__isl_keep isl_qpolynomial *qp1, + __isl_keep isl_qpolynomial *qp2) +{ + isl_bool equal; + + if (!qp1 || !qp2) + return isl_bool_error; + + if (isl_qpolynomial_is_nan(qp1) || isl_qpolynomial_is_nan(qp2)) + return isl_bool_false; + + equal = isl_space_is_equal(qp1->dim, qp2->dim); + if (equal < 0 || !equal) + return equal; + + equal = isl_mat_is_equal(qp1->div, qp2->div); + if (equal < 0 || !equal) + return equal; + + return isl_upoly_is_equal(qp1->upoly, qp2->upoly); +} + +static void upoly_update_den(__isl_keep struct isl_upoly *up, isl_int *d) +{ + int i; + struct isl_upoly_rec *rec; + + if (isl_upoly_is_cst(up)) { + struct isl_upoly_cst *cst; + cst = isl_upoly_as_cst(up); + if (!cst) + return; + isl_int_lcm(*d, *d, cst->d); + return; + } + + rec = isl_upoly_as_rec(up); + if (!rec) + return; + + for (i = 0; i < rec->n; ++i) + upoly_update_den(rec->p[i], d); +} + +void isl_qpolynomial_get_den(__isl_keep isl_qpolynomial *qp, isl_int *d) +{ + isl_int_set_si(*d, 1); + if (!qp) + return; + upoly_update_den(qp->upoly, d); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_var_pow_on_domain( + __isl_take isl_space *dim, int pos, int power) +{ + struct isl_ctx *ctx; + + if (!dim) + return NULL; + + ctx = dim->ctx; + + return isl_qpolynomial_alloc(dim, 0, isl_upoly_var_pow(ctx, pos, power)); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_var_on_domain(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos) +{ + if (!dim) + return NULL; + + isl_assert(dim->ctx, isl_space_dim(dim, isl_dim_in) == 0, goto error); + isl_assert(dim->ctx, pos < isl_space_dim(dim, type), goto error); + + if (type == isl_dim_set) + pos += isl_space_dim(dim, isl_dim_param); + + return isl_qpolynomial_var_pow_on_domain(dim, pos, 1); +error: + isl_space_free(dim); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_subs(__isl_take struct isl_upoly *up, + unsigned first, unsigned n, __isl_keep struct isl_upoly **subs) +{ + int i; + struct isl_upoly_rec *rec; + struct isl_upoly *base, *res; + + if (!up) + return NULL; + + if (isl_upoly_is_cst(up)) + return up; + + if (up->var < first) + return up; + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + isl_assert(up->ctx, rec->n >= 1, goto error); + + if (up->var >= first + n) + base = isl_upoly_var_pow(up->ctx, up->var, 1); + else + base = isl_upoly_copy(subs[up->var - first]); + + res = isl_upoly_subs(isl_upoly_copy(rec->p[rec->n - 1]), first, n, subs); + for (i = rec->n - 2; i >= 0; --i) { + struct isl_upoly *t; + t = isl_upoly_subs(isl_upoly_copy(rec->p[i]), first, n, subs); + res = isl_upoly_mul(res, isl_upoly_copy(base)); + res = isl_upoly_sum(res, t); + } + + isl_upoly_free(base); + isl_upoly_free(up); + + return res; +error: + isl_upoly_free(up); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_from_affine(isl_ctx *ctx, isl_int *f, + isl_int denom, unsigned len) +{ + int i; + struct isl_upoly *up; + + isl_assert(ctx, len >= 1, return NULL); + + up = isl_upoly_rat_cst(ctx, f[0], denom); + for (i = 0; i < len - 1; ++i) { + struct isl_upoly *t; + struct isl_upoly *c; + + if (isl_int_is_zero(f[1 + i])) + continue; + + c = isl_upoly_rat_cst(ctx, f[1 + i], denom); + t = isl_upoly_var_pow(ctx, i, 1); + t = isl_upoly_mul(c, t); + up = isl_upoly_sum(up, t); + } + + return up; +} + +/* Remove common factor of non-constant terms and denominator. + */ +static void normalize_div(__isl_keep isl_qpolynomial *qp, int div) +{ + isl_ctx *ctx = qp->div->ctx; + unsigned total = qp->div->n_col - 2; + + isl_seq_gcd(qp->div->row[div] + 2, total, &ctx->normalize_gcd); + isl_int_gcd(ctx->normalize_gcd, + ctx->normalize_gcd, qp->div->row[div][0]); + if (isl_int_is_one(ctx->normalize_gcd)) + return; + + isl_seq_scale_down(qp->div->row[div] + 2, qp->div->row[div] + 2, + ctx->normalize_gcd, total); + isl_int_divexact(qp->div->row[div][0], qp->div->row[div][0], + ctx->normalize_gcd); + isl_int_fdiv_q(qp->div->row[div][1], qp->div->row[div][1], + ctx->normalize_gcd); +} + +/* Replace the integer division identified by "div" by the polynomial "s". + * The integer division is assumed not to appear in the definition + * of any other integer divisions. + */ +static __isl_give isl_qpolynomial *substitute_div( + __isl_take isl_qpolynomial *qp, + int div, __isl_take struct isl_upoly *s) +{ + int i; + int total; + int *reordering; + + if (!qp || !s) + goto error; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + goto error; + + total = isl_space_dim(qp->dim, isl_dim_all); + qp->upoly = isl_upoly_subs(qp->upoly, total + div, 1, &s); + if (!qp->upoly) + goto error; + + reordering = isl_alloc_array(qp->dim->ctx, int, total + qp->div->n_row); + if (!reordering) + goto error; + for (i = 0; i < total + div; ++i) + reordering[i] = i; + for (i = total + div + 1; i < total + qp->div->n_row; ++i) + reordering[i] = i - 1; + qp->div = isl_mat_drop_rows(qp->div, div, 1); + qp->div = isl_mat_drop_cols(qp->div, 2 + total + div, 1); + qp->upoly = reorder(qp->upoly, reordering); + free(reordering); + + if (!qp->upoly || !qp->div) + goto error; + + isl_upoly_free(s); + return qp; +error: + isl_qpolynomial_free(qp); + isl_upoly_free(s); + return NULL; +} + +/* Replace all integer divisions [e/d] that turn out to not actually be integer + * divisions because d is equal to 1 by their definition, i.e., e. + */ +static __isl_give isl_qpolynomial *substitute_non_divs( + __isl_take isl_qpolynomial *qp) +{ + int i, j; + int total; + struct isl_upoly *s; + + if (!qp) + return NULL; + + total = isl_space_dim(qp->dim, isl_dim_all); + for (i = 0; qp && i < qp->div->n_row; ++i) { + if (!isl_int_is_one(qp->div->row[i][0])) + continue; + for (j = i + 1; j < qp->div->n_row; ++j) { + if (isl_int_is_zero(qp->div->row[j][2 + total + i])) + continue; + isl_seq_combine(qp->div->row[j] + 1, + qp->div->ctx->one, qp->div->row[j] + 1, + qp->div->row[j][2 + total + i], + qp->div->row[i] + 1, 1 + total + i); + isl_int_set_si(qp->div->row[j][2 + total + i], 0); + normalize_div(qp, j); + } + s = isl_upoly_from_affine(qp->dim->ctx, qp->div->row[i] + 1, + qp->div->row[i][0], qp->div->n_col - 1); + qp = substitute_div(qp, i, s); + --i; + } + + return qp; +} + +/* Reduce the coefficients of div "div" to lie in the interval [0, d-1], + * with d the denominator. When replacing the coefficient e of x by + * d * frac(e/d) = e - d * floor(e/d), we are subtracting d * floor(e/d) * x + * inside the division, so we need to add floor(e/d) * x outside. + * That is, we replace q by q' + floor(e/d) * x and we therefore need + * to adjust the coefficient of x in each later div that depends on the + * current div "div" and also in the affine expression "aff" + * (if it too depends on "div"). + */ +static void reduce_div(__isl_keep isl_qpolynomial *qp, int div, + __isl_keep isl_vec *aff) +{ + int i, j; + isl_int v; + unsigned total = qp->div->n_col - qp->div->n_row - 2; + + isl_int_init(v); + for (i = 0; i < 1 + total + div; ++i) { + if (isl_int_is_nonneg(qp->div->row[div][1 + i]) && + isl_int_lt(qp->div->row[div][1 + i], qp->div->row[div][0])) + continue; + isl_int_fdiv_q(v, qp->div->row[div][1 + i], qp->div->row[div][0]); + isl_int_fdiv_r(qp->div->row[div][1 + i], + qp->div->row[div][1 + i], qp->div->row[div][0]); + if (!isl_int_is_zero(aff->el[1 + total + div])) + isl_int_addmul(aff->el[i], v, aff->el[1 + total + div]); + for (j = div + 1; j < qp->div->n_row; ++j) { + if (isl_int_is_zero(qp->div->row[j][2 + total + div])) + continue; + isl_int_addmul(qp->div->row[j][1 + i], + v, qp->div->row[j][2 + total + div]); + } + } + isl_int_clear(v); +} + +/* Check if the last non-zero coefficient is bigger that half of the + * denominator. If so, we will invert the div to further reduce the number + * of distinct divs that may appear. + * If the last non-zero coefficient is exactly half the denominator, + * then we continue looking for earlier coefficients that are bigger + * than half the denominator. + */ +static int needs_invert(__isl_keep isl_mat *div, int row) +{ + int i; + int cmp; + + for (i = div->n_col - 1; i >= 1; --i) { + if (isl_int_is_zero(div->row[row][i])) + continue; + isl_int_mul_ui(div->row[row][i], div->row[row][i], 2); + cmp = isl_int_cmp(div->row[row][i], div->row[row][0]); + isl_int_divexact_ui(div->row[row][i], div->row[row][i], 2); + if (cmp) + return cmp > 0; + if (i == 1) + return 1; + } + + return 0; +} + +/* Replace div "div" q = [e/d] by -[(-e+(d-1))/d]. + * We only invert the coefficients of e (and the coefficient of q in + * later divs and in "aff"). After calling this function, the + * coefficients of e should be reduced again. + */ +static void invert_div(__isl_keep isl_qpolynomial *qp, int div, + __isl_keep isl_vec *aff) +{ + unsigned total = qp->div->n_col - qp->div->n_row - 2; + + isl_seq_neg(qp->div->row[div] + 1, + qp->div->row[div] + 1, qp->div->n_col - 1); + isl_int_sub_ui(qp->div->row[div][1], qp->div->row[div][1], 1); + isl_int_add(qp->div->row[div][1], + qp->div->row[div][1], qp->div->row[div][0]); + if (!isl_int_is_zero(aff->el[1 + total + div])) + isl_int_neg(aff->el[1 + total + div], aff->el[1 + total + div]); + isl_mat_col_mul(qp->div, 2 + total + div, + qp->div->ctx->negone, 2 + total + div); +} + +/* Assuming "qp" is a monomial, reduce all its divs to have coefficients + * in the interval [0, d-1], with d the denominator and such that the + * last non-zero coefficient that is not equal to d/2 is smaller than d/2. + * + * After the reduction, some divs may have become redundant or identical, + * so we call substitute_non_divs and sort_divs. If these functions + * eliminate divs or merge two or more divs into one, the coefficients + * of the enclosing divs may have to be reduced again, so we call + * ourselves recursively if the number of divs decreases. + */ +static __isl_give isl_qpolynomial *reduce_divs(__isl_take isl_qpolynomial *qp) +{ + int i; + isl_vec *aff = NULL; + struct isl_upoly *s; + unsigned n_div; + + if (!qp) + return NULL; + + aff = isl_vec_alloc(qp->div->ctx, qp->div->n_col - 1); + aff = isl_vec_clr(aff); + if (!aff) + goto error; + + isl_int_set_si(aff->el[1 + qp->upoly->var], 1); + + for (i = 0; i < qp->div->n_row; ++i) { + normalize_div(qp, i); + reduce_div(qp, i, aff); + if (needs_invert(qp->div, i)) { + invert_div(qp, i, aff); + reduce_div(qp, i, aff); + } + } + + s = isl_upoly_from_affine(qp->div->ctx, aff->el, + qp->div->ctx->one, aff->size); + qp->upoly = isl_upoly_subs(qp->upoly, qp->upoly->var, 1, &s); + isl_upoly_free(s); + if (!qp->upoly) + goto error; + + isl_vec_free(aff); + + n_div = qp->div->n_row; + qp = substitute_non_divs(qp); + qp = sort_divs(qp); + if (qp && qp->div->n_row < n_div) + return reduce_divs(qp); + + return qp; +error: + isl_qpolynomial_free(qp); + isl_vec_free(aff); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_rat_cst_on_domain( + __isl_take isl_space *dim, const isl_int n, const isl_int d) +{ + struct isl_qpolynomial *qp; + struct isl_upoly_cst *cst; + + if (!dim) + return NULL; + + qp = isl_qpolynomial_alloc(dim, 0, isl_upoly_zero(dim->ctx)); + if (!qp) + return NULL; + + cst = isl_upoly_as_cst(qp->upoly); + isl_int_set(cst->n, n); + isl_int_set(cst->d, d); + + return qp; +} + +/* Return an isl_qpolynomial that is equal to "val" on domain space "domain". + */ +__isl_give isl_qpolynomial *isl_qpolynomial_val_on_domain( + __isl_take isl_space *domain, __isl_take isl_val *val) +{ + isl_qpolynomial *qp; + struct isl_upoly_cst *cst; + + if (!domain || !val) + goto error; + + qp = isl_qpolynomial_alloc(isl_space_copy(domain), 0, + isl_upoly_zero(domain->ctx)); + if (!qp) + goto error; + + cst = isl_upoly_as_cst(qp->upoly); + isl_int_set(cst->n, val->n); + isl_int_set(cst->d, val->d); + + isl_space_free(domain); + isl_val_free(val); + return qp; +error: + isl_space_free(domain); + isl_val_free(val); + return NULL; +} + +static int up_set_active(__isl_keep struct isl_upoly *up, int *active, int d) +{ + struct isl_upoly_rec *rec; + int i; + + if (!up) + return -1; + + if (isl_upoly_is_cst(up)) + return 0; + + if (up->var < d) + active[up->var] = 1; + + rec = isl_upoly_as_rec(up); + for (i = 0; i < rec->n; ++i) + if (up_set_active(rec->p[i], active, d) < 0) + return -1; + + return 0; +} + +static int set_active(__isl_keep isl_qpolynomial *qp, int *active) +{ + int i, j; + int d = isl_space_dim(qp->dim, isl_dim_all); + + if (!qp || !active) + return -1; + + for (i = 0; i < d; ++i) + for (j = 0; j < qp->div->n_row; ++j) { + if (isl_int_is_zero(qp->div->row[j][2 + i])) + continue; + active[i] = 1; + break; + } + + return up_set_active(qp->upoly, active, d); +} + +isl_bool isl_qpolynomial_involves_dims(__isl_keep isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + int *active = NULL; + isl_bool involves = isl_bool_false; + + if (!qp) + return isl_bool_error; + if (n == 0) + return isl_bool_false; + + isl_assert(qp->dim->ctx, + first + n <= isl_qpolynomial_dim(qp, type), + return isl_bool_error); + isl_assert(qp->dim->ctx, type == isl_dim_param || + type == isl_dim_in, return isl_bool_error); + + active = isl_calloc_array(qp->dim->ctx, int, + isl_space_dim(qp->dim, isl_dim_all)); + if (set_active(qp, active) < 0) + goto error; + + if (type == isl_dim_in) + first += isl_space_dim(qp->dim, isl_dim_param); + for (i = 0; i < n; ++i) + if (active[first + i]) { + involves = isl_bool_true; + break; + } + + free(active); + + return involves; +error: + free(active); + return isl_bool_error; +} + +/* Remove divs that do not appear in the quasi-polynomial, nor in any + * of the divs that do appear in the quasi-polynomial. + */ +static __isl_give isl_qpolynomial *remove_redundant_divs( + __isl_take isl_qpolynomial *qp) +{ + int i, j; + int d; + int len; + int skip; + int *active = NULL; + int *reordering = NULL; + int redundant = 0; + int n_div; + isl_ctx *ctx; + + if (!qp) + return NULL; + if (qp->div->n_row == 0) + return qp; + + d = isl_space_dim(qp->dim, isl_dim_all); + len = qp->div->n_col - 2; + ctx = isl_qpolynomial_get_ctx(qp); + active = isl_calloc_array(ctx, int, len); + if (!active) + goto error; + + if (up_set_active(qp->upoly, active, len) < 0) + goto error; + + for (i = qp->div->n_row - 1; i >= 0; --i) { + if (!active[d + i]) { + redundant = 1; + continue; + } + for (j = 0; j < i; ++j) { + if (isl_int_is_zero(qp->div->row[i][2 + d + j])) + continue; + active[d + j] = 1; + break; + } + } + + if (!redundant) { + free(active); + return qp; + } + + reordering = isl_alloc_array(qp->div->ctx, int, len); + if (!reordering) + goto error; + + for (i = 0; i < d; ++i) + reordering[i] = i; + + skip = 0; + n_div = qp->div->n_row; + for (i = 0; i < n_div; ++i) { + if (!active[d + i]) { + qp->div = isl_mat_drop_rows(qp->div, i - skip, 1); + qp->div = isl_mat_drop_cols(qp->div, + 2 + d + i - skip, 1); + skip++; + } + reordering[d + i] = d + i - skip; + } + + qp->upoly = reorder(qp->upoly, reordering); + + if (!qp->upoly || !qp->div) + goto error; + + free(active); + free(reordering); + + return qp; +error: + free(active); + free(reordering); + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give struct isl_upoly *isl_upoly_drop(__isl_take struct isl_upoly *up, + unsigned first, unsigned n) +{ + int i; + struct isl_upoly_rec *rec; + + if (!up) + return NULL; + if (n == 0 || up->var < 0 || up->var < first) + return up; + if (up->var < first + n) { + up = replace_by_constant_term(up); + return isl_upoly_drop(up, first, n); + } + up = isl_upoly_cow(up); + if (!up) + return NULL; + up->var -= n; + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + rec->p[i] = isl_upoly_drop(rec->p[i], first, n); + if (!rec->p[i]) + goto error; + } + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_set_dim_name( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned pos, const char *s) +{ + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + qp->dim = isl_space_set_dim_name(qp->dim, type, pos, s); + if (!qp->dim) + goto error; + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_drop_dims( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n) +{ + if (!qp) + return NULL; + if (type == isl_dim_out) + isl_die(qp->dim->ctx, isl_error_invalid, + "cannot drop output/set dimension", + goto error); + if (type == isl_dim_in) + type = isl_dim_set; + if (n == 0 && !isl_space_is_named_or_nested(qp->dim, type)) + return qp; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + isl_assert(qp->dim->ctx, first + n <= isl_space_dim(qp->dim, type), + goto error); + isl_assert(qp->dim->ctx, type == isl_dim_param || + type == isl_dim_set, goto error); + + qp->dim = isl_space_drop_dims(qp->dim, type, first, n); + if (!qp->dim) + goto error; + + if (type == isl_dim_set) + first += isl_space_dim(qp->dim, isl_dim_param); + + qp->div = isl_mat_drop_cols(qp->div, 2 + first, n); + if (!qp->div) + goto error; + + qp->upoly = isl_upoly_drop(qp->upoly, first, n); + if (!qp->upoly) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +/* Project the domain of the quasi-polynomial onto its parameter space. + * The quasi-polynomial may not involve any of the domain dimensions. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_project_domain_on_params( + __isl_take isl_qpolynomial *qp) +{ + isl_space *space; + unsigned n; + int involves; + + n = isl_qpolynomial_dim(qp, isl_dim_in); + involves = isl_qpolynomial_involves_dims(qp, isl_dim_in, 0, n); + if (involves < 0) + return isl_qpolynomial_free(qp); + if (involves) + isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid, + "polynomial involves some of the domain dimensions", + return isl_qpolynomial_free(qp)); + qp = isl_qpolynomial_drop_dims(qp, isl_dim_in, 0, n); + space = isl_qpolynomial_get_domain_space(qp); + space = isl_space_params(space); + qp = isl_qpolynomial_reset_domain_space(qp, space); + return qp; +} + +static __isl_give isl_qpolynomial *isl_qpolynomial_substitute_equalities_lifted( + __isl_take isl_qpolynomial *qp, __isl_take isl_basic_set *eq) +{ + int i, j, k; + isl_int denom; + unsigned total; + unsigned n_div; + struct isl_upoly *up; + + if (!eq) + goto error; + if (eq->n_eq == 0) { + isl_basic_set_free(eq); + return qp; + } + + qp = isl_qpolynomial_cow(qp); + if (!qp) + goto error; + qp->div = isl_mat_cow(qp->div); + if (!qp->div) + goto error; + + total = 1 + isl_space_dim(eq->dim, isl_dim_all); + n_div = eq->n_div; + isl_int_init(denom); + for (i = 0; i < eq->n_eq; ++i) { + j = isl_seq_last_non_zero(eq->eq[i], total + n_div); + if (j < 0 || j == 0 || j >= total) + continue; + + for (k = 0; k < qp->div->n_row; ++k) { + if (isl_int_is_zero(qp->div->row[k][1 + j])) + continue; + isl_seq_elim(qp->div->row[k] + 1, eq->eq[i], j, total, + &qp->div->row[k][0]); + normalize_div(qp, k); + } + + if (isl_int_is_pos(eq->eq[i][j])) + isl_seq_neg(eq->eq[i], eq->eq[i], total); + isl_int_abs(denom, eq->eq[i][j]); + isl_int_set_si(eq->eq[i][j], 0); + + up = isl_upoly_from_affine(qp->dim->ctx, + eq->eq[i], denom, total); + qp->upoly = isl_upoly_subs(qp->upoly, j - 1, 1, &up); + isl_upoly_free(up); + } + isl_int_clear(denom); + + if (!qp->upoly) + goto error; + + isl_basic_set_free(eq); + + qp = substitute_non_divs(qp); + qp = sort_divs(qp); + + return qp; +error: + isl_basic_set_free(eq); + isl_qpolynomial_free(qp); + return NULL; +} + +/* Exploit the equalities in "eq" to simplify the quasi-polynomial. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_substitute_equalities( + __isl_take isl_qpolynomial *qp, __isl_take isl_basic_set *eq) +{ + if (!qp || !eq) + goto error; + if (qp->div->n_row > 0) + eq = isl_basic_set_add_dims(eq, isl_dim_set, qp->div->n_row); + return isl_qpolynomial_substitute_equalities_lifted(qp, eq); +error: + isl_basic_set_free(eq); + isl_qpolynomial_free(qp); + return NULL; +} + +static __isl_give isl_basic_set *add_div_constraints( + __isl_take isl_basic_set *bset, __isl_take isl_mat *div) +{ + int i; + unsigned total; + + if (!bset || !div) + goto error; + + bset = isl_basic_set_extend_constraints(bset, 0, 2 * div->n_row); + if (!bset) + goto error; + total = isl_basic_set_total_dim(bset); + for (i = 0; i < div->n_row; ++i) + if (isl_basic_set_add_div_constraints_var(bset, + total - div->n_row + i, div->row[i]) < 0) + goto error; + + isl_mat_free(div); + return bset; +error: + isl_mat_free(div); + isl_basic_set_free(bset); + return NULL; +} + +/* Look for equalities among the variables shared by context and qp + * and the integer divisions of qp, if any. + * The equalities are then used to eliminate variables and/or integer + * divisions from qp. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_gist( + __isl_take isl_qpolynomial *qp, __isl_take isl_set *context) +{ + isl_basic_set *aff; + + if (!qp) + goto error; + if (qp->div->n_row > 0) { + isl_basic_set *bset; + context = isl_set_add_dims(context, isl_dim_set, + qp->div->n_row); + bset = isl_basic_set_universe(isl_set_get_space(context)); + bset = add_div_constraints(bset, isl_mat_copy(qp->div)); + context = isl_set_intersect(context, + isl_set_from_basic_set(bset)); + } + + aff = isl_set_affine_hull(context); + return isl_qpolynomial_substitute_equalities_lifted(qp, aff); +error: + isl_qpolynomial_free(qp); + isl_set_free(context); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_gist_params( + __isl_take isl_qpolynomial *qp, __isl_take isl_set *context) +{ + isl_space *space = isl_qpolynomial_get_domain_space(qp); + isl_set *dom_context = isl_set_universe(space); + dom_context = isl_set_intersect_params(dom_context, context); + return isl_qpolynomial_gist(qp, dom_context); +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_qpolynomial( + __isl_take isl_qpolynomial *qp) +{ + isl_set *dom; + + if (!qp) + return NULL; + if (isl_qpolynomial_is_zero(qp)) { + isl_space *dim = isl_qpolynomial_get_space(qp); + isl_qpolynomial_free(qp); + return isl_pw_qpolynomial_zero(dim); + } + + dom = isl_set_universe(isl_qpolynomial_get_domain_space(qp)); + return isl_pw_qpolynomial_alloc(dom, qp); +} + +#undef PW +#define PW isl_pw_qpolynomial +#undef EL +#define EL isl_qpolynomial +#undef EL_IS_ZERO +#define EL_IS_ZERO is_zero +#undef ZERO +#define ZERO zero +#undef IS_ZERO +#define IS_ZERO is_zero +#undef FIELD +#define FIELD qp +#undef DEFAULT_IS_ZERO +#define DEFAULT_IS_ZERO 1 + +#define NO_PULLBACK + +#include + +#undef UNION +#define UNION isl_union_pw_qpolynomial +#undef PART +#define PART isl_pw_qpolynomial +#undef PARTS +#define PARTS pw_qpolynomial + +#include +#include +#include + +int isl_pw_qpolynomial_is_one(__isl_keep isl_pw_qpolynomial *pwqp) +{ + if (!pwqp) + return -1; + + if (pwqp->n != -1) + return 0; + + if (!isl_set_plain_is_universe(pwqp->p[0].set)) + return 0; + + return isl_qpolynomial_is_one(pwqp->p[0].qp); +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2) +{ + return isl_pw_qpolynomial_union_add_(pwqp1, pwqp2); +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul( + __isl_take isl_pw_qpolynomial *pwqp1, + __isl_take isl_pw_qpolynomial *pwqp2) +{ + int i, j, n; + struct isl_pw_qpolynomial *res; + + if (!pwqp1 || !pwqp2) + goto error; + + isl_assert(pwqp1->dim->ctx, isl_space_is_equal(pwqp1->dim, pwqp2->dim), + goto error); + + if (isl_pw_qpolynomial_is_zero(pwqp1)) { + isl_pw_qpolynomial_free(pwqp2); + return pwqp1; + } + + if (isl_pw_qpolynomial_is_zero(pwqp2)) { + isl_pw_qpolynomial_free(pwqp1); + return pwqp2; + } + + if (isl_pw_qpolynomial_is_one(pwqp1)) { + isl_pw_qpolynomial_free(pwqp1); + return pwqp2; + } + + if (isl_pw_qpolynomial_is_one(pwqp2)) { + isl_pw_qpolynomial_free(pwqp2); + return pwqp1; + } + + n = pwqp1->n * pwqp2->n; + res = isl_pw_qpolynomial_alloc_size(isl_space_copy(pwqp1->dim), n); + + for (i = 0; i < pwqp1->n; ++i) { + for (j = 0; j < pwqp2->n; ++j) { + struct isl_set *common; + struct isl_qpolynomial *prod; + common = isl_set_intersect(isl_set_copy(pwqp1->p[i].set), + isl_set_copy(pwqp2->p[j].set)); + if (isl_set_plain_is_empty(common)) { + isl_set_free(common); + continue; + } + + prod = isl_qpolynomial_mul( + isl_qpolynomial_copy(pwqp1->p[i].qp), + isl_qpolynomial_copy(pwqp2->p[j].qp)); + + res = isl_pw_qpolynomial_add_piece(res, common, prod); + } + } + + isl_pw_qpolynomial_free(pwqp1); + isl_pw_qpolynomial_free(pwqp2); + + return res; +error: + isl_pw_qpolynomial_free(pwqp1); + isl_pw_qpolynomial_free(pwqp2); + return NULL; +} + +__isl_give isl_val *isl_upoly_eval(__isl_take struct isl_upoly *up, + __isl_take isl_vec *vec) +{ + int i; + struct isl_upoly_rec *rec; + isl_val *res; + isl_val *base; + + if (isl_upoly_is_cst(up)) { + isl_vec_free(vec); + res = isl_upoly_get_constant_val(up); + isl_upoly_free(up); + return res; + } + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + isl_assert(up->ctx, rec->n >= 1, goto error); + + base = isl_val_rat_from_isl_int(up->ctx, + vec->el[1 + up->var], vec->el[0]); + + res = isl_upoly_eval(isl_upoly_copy(rec->p[rec->n - 1]), + isl_vec_copy(vec)); + + for (i = rec->n - 2; i >= 0; --i) { + res = isl_val_mul(res, isl_val_copy(base)); + res = isl_val_add(res, + isl_upoly_eval(isl_upoly_copy(rec->p[i]), + isl_vec_copy(vec))); + } + + isl_val_free(base); + isl_upoly_free(up); + isl_vec_free(vec); + return res; +error: + isl_upoly_free(up); + isl_vec_free(vec); + return NULL; +} + +__isl_give isl_val *isl_qpolynomial_eval(__isl_take isl_qpolynomial *qp, + __isl_take isl_point *pnt) +{ + isl_vec *ext; + isl_val *v; + + if (!qp || !pnt) + goto error; + isl_assert(pnt->dim->ctx, isl_space_is_equal(pnt->dim, qp->dim), goto error); + + if (qp->div->n_row == 0) + ext = isl_vec_copy(pnt->vec); + else { + int i; + unsigned dim = isl_space_dim(qp->dim, isl_dim_all); + ext = isl_vec_alloc(qp->dim->ctx, 1 + dim + qp->div->n_row); + if (!ext) + goto error; + + isl_seq_cpy(ext->el, pnt->vec->el, pnt->vec->size); + for (i = 0; i < qp->div->n_row; ++i) { + isl_seq_inner_product(qp->div->row[i] + 1, ext->el, + 1 + dim + i, &ext->el[1+dim+i]); + isl_int_fdiv_q(ext->el[1+dim+i], ext->el[1+dim+i], + qp->div->row[i][0]); + } + } + + v = isl_upoly_eval(isl_upoly_copy(qp->upoly), ext); + + isl_qpolynomial_free(qp); + isl_point_free(pnt); + + return v; +error: + isl_qpolynomial_free(qp); + isl_point_free(pnt); + return NULL; +} + +int isl_upoly_cmp(__isl_keep struct isl_upoly_cst *cst1, + __isl_keep struct isl_upoly_cst *cst2) +{ + int cmp; + isl_int t; + isl_int_init(t); + isl_int_mul(t, cst1->n, cst2->d); + isl_int_submul(t, cst2->n, cst1->d); + cmp = isl_int_sgn(t); + isl_int_clear(t); + return cmp; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_insert_dims( + __isl_take isl_qpolynomial *qp, enum isl_dim_type type, + unsigned first, unsigned n) +{ + unsigned total; + unsigned g_pos; + int *exp; + + if (!qp) + return NULL; + if (type == isl_dim_out) + isl_die(qp->div->ctx, isl_error_invalid, + "cannot insert output/set dimensions", + goto error); + if (type == isl_dim_in) + type = isl_dim_set; + if (n == 0 && !isl_space_is_named_or_nested(qp->dim, type)) + return qp; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + isl_assert(qp->div->ctx, first <= isl_space_dim(qp->dim, type), + goto error); + + g_pos = pos(qp->dim, type) + first; + + qp->div = isl_mat_insert_zero_cols(qp->div, 2 + g_pos, n); + if (!qp->div) + goto error; + + total = qp->div->n_col - 2; + if (total > g_pos) { + int i; + exp = isl_alloc_array(qp->div->ctx, int, total - g_pos); + if (!exp) + goto error; + for (i = 0; i < total - g_pos; ++i) + exp[i] = i + n; + qp->upoly = expand(qp->upoly, exp, g_pos); + free(exp); + if (!qp->upoly) + goto error; + } + + qp->dim = isl_space_insert_dims(qp->dim, type, first, n); + if (!qp->dim) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_add_dims( + __isl_take isl_qpolynomial *qp, enum isl_dim_type type, unsigned n) +{ + unsigned pos; + + pos = isl_qpolynomial_dim(qp, type); + + return isl_qpolynomial_insert_dims(qp, type, pos, n); +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_dims( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned n) +{ + unsigned pos; + + pos = isl_pw_qpolynomial_dim(pwqp, type); + + return isl_pw_qpolynomial_insert_dims(pwqp, type, pos, n); +} + +static int *reordering_move(isl_ctx *ctx, + unsigned len, unsigned dst, unsigned src, unsigned n) +{ + int i; + int *reordering; + + reordering = isl_alloc_array(ctx, int, len); + if (!reordering) + return NULL; + + if (dst <= src) { + for (i = 0; i < dst; ++i) + reordering[i] = i; + for (i = 0; i < n; ++i) + reordering[src + i] = dst + i; + for (i = 0; i < src - dst; ++i) + reordering[dst + i] = dst + n + i; + for (i = 0; i < len - src - n; ++i) + reordering[src + n + i] = src + n + i; + } else { + for (i = 0; i < src; ++i) + reordering[i] = i; + for (i = 0; i < n; ++i) + reordering[src + i] = dst + i; + for (i = 0; i < dst - src; ++i) + reordering[src + n + i] = src + i; + for (i = 0; i < len - dst - n; ++i) + reordering[dst + n + i] = dst + n + i; + } + + return reordering; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_move_dims( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + unsigned g_dst_pos; + unsigned g_src_pos; + int *reordering; + + if (n == 0) + return qp; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + if (dst_type == isl_dim_out || src_type == isl_dim_out) + isl_die(qp->dim->ctx, isl_error_invalid, + "cannot move output/set dimension", + goto error); + if (dst_type == isl_dim_in) + dst_type = isl_dim_set; + if (src_type == isl_dim_in) + src_type = isl_dim_set; + + isl_assert(qp->dim->ctx, src_pos + n <= isl_space_dim(qp->dim, src_type), + goto error); + + g_dst_pos = pos(qp->dim, dst_type) + dst_pos; + g_src_pos = pos(qp->dim, src_type) + src_pos; + if (dst_type > src_type) + g_dst_pos -= n; + + qp->div = isl_mat_move_cols(qp->div, 2 + g_dst_pos, 2 + g_src_pos, n); + if (!qp->div) + goto error; + qp = sort_divs(qp); + if (!qp) + goto error; + + reordering = reordering_move(qp->dim->ctx, + qp->div->n_col - 2, g_dst_pos, g_src_pos, n); + if (!reordering) + goto error; + + qp->upoly = reorder(qp->upoly, reordering); + free(reordering); + if (!qp->upoly) + goto error; + + qp->dim = isl_space_move_dims(qp->dim, dst_type, dst_pos, src_type, src_pos, n); + if (!qp->dim) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_from_affine(__isl_take isl_space *dim, + isl_int *f, isl_int denom) +{ + struct isl_upoly *up; + + dim = isl_space_domain(dim); + if (!dim) + return NULL; + + up = isl_upoly_from_affine(dim->ctx, f, denom, + 1 + isl_space_dim(dim, isl_dim_all)); + + return isl_qpolynomial_alloc(dim, 0, up); +} + +__isl_give isl_qpolynomial *isl_qpolynomial_from_aff(__isl_take isl_aff *aff) +{ + isl_ctx *ctx; + struct isl_upoly *up; + isl_qpolynomial *qp; + + if (!aff) + return NULL; + + ctx = isl_aff_get_ctx(aff); + up = isl_upoly_from_affine(ctx, aff->v->el + 1, aff->v->el[0], + aff->v->size - 1); + + qp = isl_qpolynomial_alloc(isl_aff_get_domain_space(aff), + aff->ls->div->n_row, up); + if (!qp) + goto error; + + isl_mat_free(qp->div); + qp->div = isl_mat_copy(aff->ls->div); + qp->div = isl_mat_cow(qp->div); + if (!qp->div) + goto error; + + isl_aff_free(aff); + qp = reduce_divs(qp); + qp = remove_redundant_divs(qp); + return qp; +error: + isl_aff_free(aff); + return isl_qpolynomial_free(qp); +} + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_pw_aff( + __isl_take isl_pw_aff *pwaff) +{ + int i; + isl_pw_qpolynomial *pwqp; + + if (!pwaff) + return NULL; + + pwqp = isl_pw_qpolynomial_alloc_size(isl_pw_aff_get_space(pwaff), + pwaff->n); + + for (i = 0; i < pwaff->n; ++i) { + isl_set *dom; + isl_qpolynomial *qp; + + dom = isl_set_copy(pwaff->p[i].set); + qp = isl_qpolynomial_from_aff(isl_aff_copy(pwaff->p[i].aff)); + pwqp = isl_pw_qpolynomial_add_piece(pwqp, dom, qp); + } + + isl_pw_aff_free(pwaff); + return pwqp; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_from_constraint( + __isl_take isl_constraint *c, enum isl_dim_type type, unsigned pos) +{ + isl_aff *aff; + + aff = isl_constraint_get_bound(c, type, pos); + isl_constraint_free(c); + return isl_qpolynomial_from_aff(aff); +} + +/* For each 0 <= i < "n", replace variable "first" + i of type "type" + * in "qp" by subs[i]. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_substitute( + __isl_take isl_qpolynomial *qp, + enum isl_dim_type type, unsigned first, unsigned n, + __isl_keep isl_qpolynomial **subs) +{ + int i; + struct isl_upoly **ups; + + if (n == 0) + return qp; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + if (type == isl_dim_out) + isl_die(qp->dim->ctx, isl_error_invalid, + "cannot substitute output/set dimension", + goto error); + if (type == isl_dim_in) + type = isl_dim_set; + + for (i = 0; i < n; ++i) + if (!subs[i]) + goto error; + + isl_assert(qp->dim->ctx, first + n <= isl_space_dim(qp->dim, type), + goto error); + + for (i = 0; i < n; ++i) + isl_assert(qp->dim->ctx, isl_space_is_equal(qp->dim, subs[i]->dim), + goto error); + + isl_assert(qp->dim->ctx, qp->div->n_row == 0, goto error); + for (i = 0; i < n; ++i) + isl_assert(qp->dim->ctx, subs[i]->div->n_row == 0, goto error); + + first += pos(qp->dim, type); + + ups = isl_alloc_array(qp->dim->ctx, struct isl_upoly *, n); + if (!ups) + goto error; + for (i = 0; i < n; ++i) + ups[i] = subs[i]->upoly; + + qp->upoly = isl_upoly_subs(qp->upoly, first, n, ups); + + free(ups); + + if (!qp->upoly) + goto error; + + return qp; +error: + isl_qpolynomial_free(qp); + return NULL; +} + +/* Extend "bset" with extra set dimensions for each integer division + * in "qp" and then call "fn" with the extended bset and the polynomial + * that results from replacing each of the integer divisions by the + * corresponding extra set dimension. + */ +int isl_qpolynomial_as_polynomial_on_domain(__isl_keep isl_qpolynomial *qp, + __isl_keep isl_basic_set *bset, + int (*fn)(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, void *user), void *user) +{ + isl_space *dim; + isl_mat *div; + isl_qpolynomial *poly; + + if (!qp || !bset) + goto error; + if (qp->div->n_row == 0) + return fn(isl_basic_set_copy(bset), isl_qpolynomial_copy(qp), + user); + + div = isl_mat_copy(qp->div); + dim = isl_space_copy(qp->dim); + dim = isl_space_add_dims(dim, isl_dim_set, qp->div->n_row); + poly = isl_qpolynomial_alloc(dim, 0, isl_upoly_copy(qp->upoly)); + bset = isl_basic_set_copy(bset); + bset = isl_basic_set_add_dims(bset, isl_dim_set, qp->div->n_row); + bset = add_div_constraints(bset, div); + + return fn(bset, poly, user); +error: + return -1; +} + +/* Return total degree in variables first (inclusive) up to last (exclusive). + */ +int isl_upoly_degree(__isl_keep struct isl_upoly *up, int first, int last) +{ + int deg = -1; + int i; + struct isl_upoly_rec *rec; + + if (!up) + return -2; + if (isl_upoly_is_zero(up)) + return -1; + if (isl_upoly_is_cst(up) || up->var < first) + return 0; + + rec = isl_upoly_as_rec(up); + if (!rec) + return -2; + + for (i = 0; i < rec->n; ++i) { + int d; + + if (isl_upoly_is_zero(rec->p[i])) + continue; + d = isl_upoly_degree(rec->p[i], first, last); + if (up->var < last) + d += i; + if (d > deg) + deg = d; + } + + return deg; +} + +/* Return total degree in set variables. + */ +int isl_qpolynomial_degree(__isl_keep isl_qpolynomial *poly) +{ + unsigned ovar; + unsigned nvar; + + if (!poly) + return -2; + + ovar = isl_space_offset(poly->dim, isl_dim_set); + nvar = isl_space_dim(poly->dim, isl_dim_set); + return isl_upoly_degree(poly->upoly, ovar, ovar + nvar); +} + +__isl_give struct isl_upoly *isl_upoly_coeff(__isl_keep struct isl_upoly *up, + unsigned pos, int deg) +{ + int i; + struct isl_upoly_rec *rec; + + if (!up) + return NULL; + + if (isl_upoly_is_cst(up) || up->var < pos) { + if (deg == 0) + return isl_upoly_copy(up); + else + return isl_upoly_zero(up->ctx); + } + + rec = isl_upoly_as_rec(up); + if (!rec) + return NULL; + + if (up->var == pos) { + if (deg < rec->n) + return isl_upoly_copy(rec->p[deg]); + else + return isl_upoly_zero(up->ctx); + } + + up = isl_upoly_copy(up); + up = isl_upoly_cow(up); + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + struct isl_upoly *t; + t = isl_upoly_coeff(rec->p[i], pos, deg); + if (!t) + goto error; + isl_upoly_free(rec->p[i]); + rec->p[i] = t; + } + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +/* Return coefficient of power "deg" of variable "t_pos" of type "type". + */ +__isl_give isl_qpolynomial *isl_qpolynomial_coeff( + __isl_keep isl_qpolynomial *qp, + enum isl_dim_type type, unsigned t_pos, int deg) +{ + unsigned g_pos; + struct isl_upoly *up; + isl_qpolynomial *c; + + if (!qp) + return NULL; + + if (type == isl_dim_out) + isl_die(qp->div->ctx, isl_error_invalid, + "output/set dimension does not have a coefficient", + return NULL); + if (type == isl_dim_in) + type = isl_dim_set; + + isl_assert(qp->div->ctx, t_pos < isl_space_dim(qp->dim, type), + return NULL); + + g_pos = pos(qp->dim, type) + t_pos; + up = isl_upoly_coeff(qp->upoly, g_pos, deg); + + c = isl_qpolynomial_alloc(isl_space_copy(qp->dim), qp->div->n_row, up); + if (!c) + return NULL; + isl_mat_free(c->div); + c->div = isl_mat_copy(qp->div); + if (!c->div) + goto error; + return c; +error: + isl_qpolynomial_free(c); + return NULL; +} + +/* Homogenize the polynomial in the variables first (inclusive) up to + * last (exclusive) by inserting powers of variable first. + * Variable first is assumed not to appear in the input. + */ +__isl_give struct isl_upoly *isl_upoly_homogenize( + __isl_take struct isl_upoly *up, int deg, int target, + int first, int last) +{ + int i; + struct isl_upoly_rec *rec; + + if (!up) + return NULL; + if (isl_upoly_is_zero(up)) + return up; + if (deg == target) + return up; + if (isl_upoly_is_cst(up) || up->var < first) { + struct isl_upoly *hom; + + hom = isl_upoly_var_pow(up->ctx, first, target - deg); + if (!hom) + goto error; + rec = isl_upoly_as_rec(hom); + rec->p[target - deg] = isl_upoly_mul(rec->p[target - deg], up); + + return hom; + } + + up = isl_upoly_cow(up); + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + if (isl_upoly_is_zero(rec->p[i])) + continue; + rec->p[i] = isl_upoly_homogenize(rec->p[i], + up->var < last ? deg + i : i, target, + first, last); + if (!rec->p[i]) + goto error; + } + + return up; +error: + isl_upoly_free(up); + return NULL; +} + +/* Homogenize the polynomial in the set variables by introducing + * powers of an extra set variable at position 0. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_homogenize( + __isl_take isl_qpolynomial *poly) +{ + unsigned ovar; + unsigned nvar; + int deg = isl_qpolynomial_degree(poly); + + if (deg < -1) + goto error; + + poly = isl_qpolynomial_insert_dims(poly, isl_dim_in, 0, 1); + poly = isl_qpolynomial_cow(poly); + if (!poly) + goto error; + + ovar = isl_space_offset(poly->dim, isl_dim_set); + nvar = isl_space_dim(poly->dim, isl_dim_set); + poly->upoly = isl_upoly_homogenize(poly->upoly, 0, deg, + ovar, ovar + nvar); + if (!poly->upoly) + goto error; + + return poly; +error: + isl_qpolynomial_free(poly); + return NULL; +} + +__isl_give isl_term *isl_term_alloc(__isl_take isl_space *dim, + __isl_take isl_mat *div) +{ + isl_term *term; + int n; + + if (!dim || !div) + goto error; + + n = isl_space_dim(dim, isl_dim_all) + div->n_row; + + term = isl_calloc(dim->ctx, struct isl_term, + sizeof(struct isl_term) + (n - 1) * sizeof(int)); + if (!term) + goto error; + + term->ref = 1; + term->dim = dim; + term->div = div; + isl_int_init(term->n); + isl_int_init(term->d); + + return term; +error: + isl_space_free(dim); + isl_mat_free(div); + return NULL; +} + +__isl_give isl_term *isl_term_copy(__isl_keep isl_term *term) +{ + if (!term) + return NULL; + + term->ref++; + return term; +} + +__isl_give isl_term *isl_term_dup(__isl_keep isl_term *term) +{ + int i; + isl_term *dup; + unsigned total; + + if (!term) + return NULL; + + total = isl_space_dim(term->dim, isl_dim_all) + term->div->n_row; + + dup = isl_term_alloc(isl_space_copy(term->dim), isl_mat_copy(term->div)); + if (!dup) + return NULL; + + isl_int_set(dup->n, term->n); + isl_int_set(dup->d, term->d); + + for (i = 0; i < total; ++i) + dup->pow[i] = term->pow[i]; + + return dup; +} + +__isl_give isl_term *isl_term_cow(__isl_take isl_term *term) +{ + if (!term) + return NULL; + + if (term->ref == 1) + return term; + term->ref--; + return isl_term_dup(term); +} + +void isl_term_free(__isl_take isl_term *term) +{ + if (!term) + return; + + if (--term->ref > 0) + return; + + isl_space_free(term->dim); + isl_mat_free(term->div); + isl_int_clear(term->n); + isl_int_clear(term->d); + free(term); +} + +unsigned isl_term_dim(__isl_keep isl_term *term, enum isl_dim_type type) +{ + if (!term) + return 0; + + switch (type) { + case isl_dim_param: + case isl_dim_in: + case isl_dim_out: return isl_space_dim(term->dim, type); + case isl_dim_div: return term->div->n_row; + case isl_dim_all: return isl_space_dim(term->dim, isl_dim_all) + + term->div->n_row; + default: return 0; + } +} + +isl_ctx *isl_term_get_ctx(__isl_keep isl_term *term) +{ + return term ? term->dim->ctx : NULL; +} + +void isl_term_get_num(__isl_keep isl_term *term, isl_int *n) +{ + if (!term) + return; + isl_int_set(*n, term->n); +} + +void isl_term_get_den(__isl_keep isl_term *term, isl_int *d) +{ + if (!term) + return; + isl_int_set(*d, term->d); +} + +/* Return the coefficient of the term "term". + */ +__isl_give isl_val *isl_term_get_coefficient_val(__isl_keep isl_term *term) +{ + if (!term) + return NULL; + + return isl_val_rat_from_isl_int(isl_term_get_ctx(term), + term->n, term->d); +} + +int isl_term_get_exp(__isl_keep isl_term *term, + enum isl_dim_type type, unsigned pos) +{ + if (!term) + return -1; + + isl_assert(term->dim->ctx, pos < isl_term_dim(term, type), return -1); + + if (type >= isl_dim_set) + pos += isl_space_dim(term->dim, isl_dim_param); + if (type >= isl_dim_div) + pos += isl_space_dim(term->dim, isl_dim_set); + + return term->pow[pos]; +} + +__isl_give isl_aff *isl_term_get_div(__isl_keep isl_term *term, unsigned pos) +{ + isl_local_space *ls; + isl_aff *aff; + + if (!term) + return NULL; + + isl_assert(term->dim->ctx, pos < isl_term_dim(term, isl_dim_div), + return NULL); + + ls = isl_local_space_alloc_div(isl_space_copy(term->dim), + isl_mat_copy(term->div)); + aff = isl_aff_alloc(ls); + if (!aff) + return NULL; + + isl_seq_cpy(aff->v->el, term->div->row[pos], aff->v->size); + + aff = isl_aff_normalize(aff); + + return aff; +} + +__isl_give isl_term *isl_upoly_foreach_term(__isl_keep struct isl_upoly *up, + isl_stat (*fn)(__isl_take isl_term *term, void *user), + __isl_take isl_term *term, void *user) +{ + int i; + struct isl_upoly_rec *rec; + + if (!up || !term) + goto error; + + if (isl_upoly_is_zero(up)) + return term; + + isl_assert(up->ctx, !isl_upoly_is_nan(up), goto error); + isl_assert(up->ctx, !isl_upoly_is_infty(up), goto error); + isl_assert(up->ctx, !isl_upoly_is_neginfty(up), goto error); + + if (isl_upoly_is_cst(up)) { + struct isl_upoly_cst *cst; + cst = isl_upoly_as_cst(up); + if (!cst) + goto error; + term = isl_term_cow(term); + if (!term) + goto error; + isl_int_set(term->n, cst->n); + isl_int_set(term->d, cst->d); + if (fn(isl_term_copy(term), user) < 0) + goto error; + return term; + } + + rec = isl_upoly_as_rec(up); + if (!rec) + goto error; + + for (i = 0; i < rec->n; ++i) { + term = isl_term_cow(term); + if (!term) + goto error; + term->pow[up->var] = i; + term = isl_upoly_foreach_term(rec->p[i], fn, term, user); + if (!term) + goto error; + } + term->pow[up->var] = 0; + + return term; +error: + isl_term_free(term); + return NULL; +} + +isl_stat isl_qpolynomial_foreach_term(__isl_keep isl_qpolynomial *qp, + isl_stat (*fn)(__isl_take isl_term *term, void *user), void *user) +{ + isl_term *term; + + if (!qp) + return isl_stat_error; + + term = isl_term_alloc(isl_space_copy(qp->dim), isl_mat_copy(qp->div)); + if (!term) + return isl_stat_error; + + term = isl_upoly_foreach_term(qp->upoly, fn, term, user); + + isl_term_free(term); + + return term ? isl_stat_ok : isl_stat_error; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_from_term(__isl_take isl_term *term) +{ + struct isl_upoly *up; + isl_qpolynomial *qp; + int i, n; + + if (!term) + return NULL; + + n = isl_space_dim(term->dim, isl_dim_all) + term->div->n_row; + + up = isl_upoly_rat_cst(term->dim->ctx, term->n, term->d); + for (i = 0; i < n; ++i) { + if (!term->pow[i]) + continue; + up = isl_upoly_mul(up, + isl_upoly_var_pow(term->dim->ctx, i, term->pow[i])); + } + + qp = isl_qpolynomial_alloc(isl_space_copy(term->dim), term->div->n_row, up); + if (!qp) + goto error; + isl_mat_free(qp->div); + qp->div = isl_mat_copy(term->div); + if (!qp->div) + goto error; + + isl_term_free(term); + return qp; +error: + isl_qpolynomial_free(qp); + isl_term_free(term); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_lift(__isl_take isl_qpolynomial *qp, + __isl_take isl_space *dim) +{ + int i; + int extra; + unsigned total; + + if (!qp || !dim) + goto error; + + if (isl_space_is_equal(qp->dim, dim)) { + isl_space_free(dim); + return qp; + } + + qp = isl_qpolynomial_cow(qp); + if (!qp) + goto error; + + extra = isl_space_dim(dim, isl_dim_set) - + isl_space_dim(qp->dim, isl_dim_set); + total = isl_space_dim(qp->dim, isl_dim_all); + if (qp->div->n_row) { + int *exp; + + exp = isl_alloc_array(qp->div->ctx, int, qp->div->n_row); + if (!exp) + goto error; + for (i = 0; i < qp->div->n_row; ++i) + exp[i] = extra + i; + qp->upoly = expand(qp->upoly, exp, total); + free(exp); + if (!qp->upoly) + goto error; + } + qp->div = isl_mat_insert_cols(qp->div, 2 + total, extra); + if (!qp->div) + goto error; + for (i = 0; i < qp->div->n_row; ++i) + isl_seq_clr(qp->div->row[i] + 2 + total, extra); + + isl_space_free(qp->dim); + qp->dim = dim; + + return qp; +error: + isl_space_free(dim); + isl_qpolynomial_free(qp); + return NULL; +} + +/* For each parameter or variable that does not appear in qp, + * first eliminate the variable from all constraints and then set it to zero. + */ +static __isl_give isl_set *fix_inactive(__isl_take isl_set *set, + __isl_keep isl_qpolynomial *qp) +{ + int *active = NULL; + int i; + int d; + unsigned nparam; + unsigned nvar; + + if (!set || !qp) + goto error; + + d = isl_space_dim(set->dim, isl_dim_all); + active = isl_calloc_array(set->ctx, int, d); + if (set_active(qp, active) < 0) + goto error; + + for (i = 0; i < d; ++i) + if (!active[i]) + break; + + if (i == d) { + free(active); + return set; + } + + nparam = isl_space_dim(set->dim, isl_dim_param); + nvar = isl_space_dim(set->dim, isl_dim_set); + for (i = 0; i < nparam; ++i) { + if (active[i]) + continue; + set = isl_set_eliminate(set, isl_dim_param, i, 1); + set = isl_set_fix_si(set, isl_dim_param, i, 0); + } + for (i = 0; i < nvar; ++i) { + if (active[nparam + i]) + continue; + set = isl_set_eliminate(set, isl_dim_set, i, 1); + set = isl_set_fix_si(set, isl_dim_set, i, 0); + } + + free(active); + + return set; +error: + free(active); + isl_set_free(set); + return NULL; +} + +struct isl_opt_data { + isl_qpolynomial *qp; + int first; + isl_val *opt; + int max; +}; + +static isl_stat opt_fn(__isl_take isl_point *pnt, void *user) +{ + struct isl_opt_data *data = (struct isl_opt_data *)user; + isl_val *val; + + val = isl_qpolynomial_eval(isl_qpolynomial_copy(data->qp), pnt); + if (data->first) { + data->first = 0; + data->opt = val; + } else if (data->max) { + data->opt = isl_val_max(data->opt, val); + } else { + data->opt = isl_val_min(data->opt, val); + } + + return isl_stat_ok; +} + +__isl_give isl_val *isl_qpolynomial_opt_on_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_set *set, int max) +{ + struct isl_opt_data data = { NULL, 1, NULL, max }; + + if (!set || !qp) + goto error; + + if (isl_upoly_is_cst(qp->upoly)) { + isl_set_free(set); + data.opt = isl_qpolynomial_get_constant_val(qp); + isl_qpolynomial_free(qp); + return data.opt; + } + + set = fix_inactive(set, qp); + + data.qp = qp; + if (isl_set_foreach_point(set, opt_fn, &data) < 0) + goto error; + + if (data.first) + data.opt = isl_val_zero(isl_set_get_ctx(set)); + + isl_set_free(set); + isl_qpolynomial_free(qp); + return data.opt; +error: + isl_set_free(set); + isl_qpolynomial_free(qp); + isl_val_free(data.opt); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_morph_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_morph *morph) +{ + int i; + int n_sub; + isl_ctx *ctx; + struct isl_upoly **subs; + isl_mat *mat, *diag; + + qp = isl_qpolynomial_cow(qp); + if (!qp || !morph) + goto error; + + ctx = qp->dim->ctx; + isl_assert(ctx, isl_space_is_equal(qp->dim, morph->dom->dim), goto error); + + n_sub = morph->inv->n_row - 1; + if (morph->inv->n_row != morph->inv->n_col) + n_sub += qp->div->n_row; + subs = isl_calloc_array(ctx, struct isl_upoly *, n_sub); + if (n_sub && !subs) + goto error; + + for (i = 0; 1 + i < morph->inv->n_row; ++i) + subs[i] = isl_upoly_from_affine(ctx, morph->inv->row[1 + i], + morph->inv->row[0][0], morph->inv->n_col); + if (morph->inv->n_row != morph->inv->n_col) + for (i = 0; i < qp->div->n_row; ++i) + subs[morph->inv->n_row - 1 + i] = + isl_upoly_var_pow(ctx, morph->inv->n_col - 1 + i, 1); + + qp->upoly = isl_upoly_subs(qp->upoly, 0, n_sub, subs); + + for (i = 0; i < n_sub; ++i) + isl_upoly_free(subs[i]); + free(subs); + + diag = isl_mat_diag(ctx, 1, morph->inv->row[0][0]); + mat = isl_mat_diagonal(diag, isl_mat_copy(morph->inv)); + diag = isl_mat_diag(ctx, qp->div->n_row, morph->inv->row[0][0]); + mat = isl_mat_diagonal(mat, diag); + qp->div = isl_mat_product(qp->div, mat); + isl_space_free(qp->dim); + qp->dim = isl_space_copy(morph->ran->dim); + + if (!qp->upoly || !qp->div || !qp->dim) + goto error; + + isl_morph_free(morph); + + return qp; +error: + isl_qpolynomial_free(qp); + isl_morph_free(morph); + return NULL; +} + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul( + __isl_take isl_union_pw_qpolynomial *upwqp1, + __isl_take isl_union_pw_qpolynomial *upwqp2) +{ + return isl_union_pw_qpolynomial_match_bin_op(upwqp1, upwqp2, + &isl_pw_qpolynomial_mul); +} + +/* Reorder the columns of the given div definitions according to the + * given reordering. + */ +static __isl_give isl_mat *reorder_divs(__isl_take isl_mat *div, + __isl_take isl_reordering *r) +{ + int i, j; + isl_mat *mat; + int extra; + + if (!div || !r) + goto error; + + extra = isl_space_dim(r->dim, isl_dim_all) + div->n_row - r->len; + mat = isl_mat_alloc(div->ctx, div->n_row, div->n_col + extra); + if (!mat) + goto error; + + for (i = 0; i < div->n_row; ++i) { + isl_seq_cpy(mat->row[i], div->row[i], 2); + isl_seq_clr(mat->row[i] + 2, mat->n_col - 2); + for (j = 0; j < r->len; ++j) + isl_int_set(mat->row[i][2 + r->pos[j]], + div->row[i][2 + j]); + } + + isl_reordering_free(r); + isl_mat_free(div); + return mat; +error: + isl_reordering_free(r); + isl_mat_free(div); + return NULL; +} + +/* Reorder the dimension of "qp" according to the given reordering. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_realign_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_reordering *r) +{ + qp = isl_qpolynomial_cow(qp); + if (!qp) + goto error; + + r = isl_reordering_extend(r, qp->div->n_row); + if (!r) + goto error; + + qp->div = reorder_divs(qp->div, isl_reordering_copy(r)); + if (!qp->div) + goto error; + + qp->upoly = reorder(qp->upoly, r->pos); + if (!qp->upoly) + goto error; + + qp = isl_qpolynomial_reset_domain_space(qp, isl_space_copy(r->dim)); + + isl_reordering_free(r); + return qp; +error: + isl_qpolynomial_free(qp); + isl_reordering_free(r); + return NULL; +} + +__isl_give isl_qpolynomial *isl_qpolynomial_align_params( + __isl_take isl_qpolynomial *qp, __isl_take isl_space *model) +{ + if (!qp || !model) + goto error; + + if (!isl_space_match(qp->dim, isl_dim_param, model, isl_dim_param)) { + isl_reordering *exp; + + model = isl_space_drop_dims(model, isl_dim_in, + 0, isl_space_dim(model, isl_dim_in)); + model = isl_space_drop_dims(model, isl_dim_out, + 0, isl_space_dim(model, isl_dim_out)); + exp = isl_parameter_alignment_reordering(qp->dim, model); + exp = isl_reordering_extend_space(exp, + isl_qpolynomial_get_domain_space(qp)); + qp = isl_qpolynomial_realign_domain(qp, exp); + } + + isl_space_free(model); + return qp; +error: + isl_space_free(model); + isl_qpolynomial_free(qp); + return NULL; +} + +struct isl_split_periods_data { + int max_periods; + isl_pw_qpolynomial *res; +}; + +/* Create a slice where the integer division "div" has the fixed value "v". + * In particular, if "div" refers to floor(f/m), then create a slice + * + * m v <= f <= m v + (m - 1) + * + * or + * + * f - m v >= 0 + * -f + m v + (m - 1) >= 0 + */ +static __isl_give isl_set *set_div_slice(__isl_take isl_space *dim, + __isl_keep isl_qpolynomial *qp, int div, isl_int v) +{ + int total; + isl_basic_set *bset = NULL; + int k; + + if (!dim || !qp) + goto error; + + total = isl_space_dim(dim, isl_dim_all); + bset = isl_basic_set_alloc_space(isl_space_copy(dim), 0, 0, 2); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_cpy(bset->ineq[k], qp->div->row[div] + 1, 1 + total); + isl_int_submul(bset->ineq[k][0], v, qp->div->row[div][0]); + + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_neg(bset->ineq[k], qp->div->row[div] + 1, 1 + total); + isl_int_addmul(bset->ineq[k][0], v, qp->div->row[div][0]); + isl_int_add(bset->ineq[k][0], bset->ineq[k][0], qp->div->row[div][0]); + isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1); + + isl_space_free(dim); + return isl_set_from_basic_set(bset); +error: + isl_basic_set_free(bset); + isl_space_free(dim); + return NULL; +} + +static isl_stat split_periods(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp, void *user); + +/* Create a slice of the domain "set" such that integer division "div" + * has the fixed value "v" and add the results to data->res, + * replacing the integer division by "v" in "qp". + */ +static isl_stat set_div(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp, int div, isl_int v, + struct isl_split_periods_data *data) +{ + int i; + int total; + isl_set *slice; + struct isl_upoly *cst; + + slice = set_div_slice(isl_set_get_space(set), qp, div, v); + set = isl_set_intersect(set, slice); + + if (!qp) + goto error; + + total = isl_space_dim(qp->dim, isl_dim_all); + + for (i = div + 1; i < qp->div->n_row; ++i) { + if (isl_int_is_zero(qp->div->row[i][2 + total + div])) + continue; + isl_int_addmul(qp->div->row[i][1], + qp->div->row[i][2 + total + div], v); + isl_int_set_si(qp->div->row[i][2 + total + div], 0); + } + + cst = isl_upoly_rat_cst(qp->dim->ctx, v, qp->dim->ctx->one); + qp = substitute_div(qp, div, cst); + + return split_periods(set, qp, data); +error: + isl_set_free(set); + isl_qpolynomial_free(qp); + return -1; +} + +/* Split the domain "set" such that integer division "div" + * has a fixed value (ranging from "min" to "max") on each slice + * and add the results to data->res. + */ +static isl_stat split_div(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp, int div, isl_int min, isl_int max, + struct isl_split_periods_data *data) +{ + for (; isl_int_le(min, max); isl_int_add_ui(min, min, 1)) { + isl_set *set_i = isl_set_copy(set); + isl_qpolynomial *qp_i = isl_qpolynomial_copy(qp); + + if (set_div(set_i, qp_i, div, min, data) < 0) + goto error; + } + isl_set_free(set); + isl_qpolynomial_free(qp); + return isl_stat_ok; +error: + isl_set_free(set); + isl_qpolynomial_free(qp); + return isl_stat_error; +} + +/* If "qp" refers to any integer division + * that can only attain "max_periods" distinct values on "set" + * then split the domain along those distinct values. + * Add the results (or the original if no splitting occurs) + * to data->res. + */ +static isl_stat split_periods(__isl_take isl_set *set, + __isl_take isl_qpolynomial *qp, void *user) +{ + int i; + isl_pw_qpolynomial *pwqp; + struct isl_split_periods_data *data; + isl_int min, max; + int total; + isl_stat r = isl_stat_ok; + + data = (struct isl_split_periods_data *)user; + + if (!set || !qp) + goto error; + + if (qp->div->n_row == 0) { + pwqp = isl_pw_qpolynomial_alloc(set, qp); + data->res = isl_pw_qpolynomial_add_disjoint(data->res, pwqp); + return isl_stat_ok; + } + + isl_int_init(min); + isl_int_init(max); + total = isl_space_dim(qp->dim, isl_dim_all); + for (i = 0; i < qp->div->n_row; ++i) { + enum isl_lp_result lp_res; + + if (isl_seq_first_non_zero(qp->div->row[i] + 2 + total, + qp->div->n_row) != -1) + continue; + + lp_res = isl_set_solve_lp(set, 0, qp->div->row[i] + 1, + set->ctx->one, &min, NULL, NULL); + if (lp_res == isl_lp_error) + goto error2; + if (lp_res == isl_lp_unbounded || lp_res == isl_lp_empty) + continue; + isl_int_fdiv_q(min, min, qp->div->row[i][0]); + + lp_res = isl_set_solve_lp(set, 1, qp->div->row[i] + 1, + set->ctx->one, &max, NULL, NULL); + if (lp_res == isl_lp_error) + goto error2; + if (lp_res == isl_lp_unbounded || lp_res == isl_lp_empty) + continue; + isl_int_fdiv_q(max, max, qp->div->row[i][0]); + + isl_int_sub(max, max, min); + if (isl_int_cmp_si(max, data->max_periods) < 0) { + isl_int_add(max, max, min); + break; + } + } + + if (i < qp->div->n_row) { + r = split_div(set, qp, i, min, max, data); + } else { + pwqp = isl_pw_qpolynomial_alloc(set, qp); + data->res = isl_pw_qpolynomial_add_disjoint(data->res, pwqp); + } + + isl_int_clear(max); + isl_int_clear(min); + + return r; +error2: + isl_int_clear(max); + isl_int_clear(min); +error: + isl_set_free(set); + isl_qpolynomial_free(qp); + return isl_stat_error; +} + +/* If any quasi-polynomial in pwqp refers to any integer division + * that can only attain "max_periods" distinct values on its domain + * then split the domain along those distinct values. + */ +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_split_periods( + __isl_take isl_pw_qpolynomial *pwqp, int max_periods) +{ + struct isl_split_periods_data data; + + data.max_periods = max_periods; + data.res = isl_pw_qpolynomial_zero(isl_pw_qpolynomial_get_space(pwqp)); + + if (isl_pw_qpolynomial_foreach_piece(pwqp, &split_periods, &data) < 0) + goto error; + + isl_pw_qpolynomial_free(pwqp); + + return data.res; +error: + isl_pw_qpolynomial_free(data.res); + isl_pw_qpolynomial_free(pwqp); + return NULL; +} + +/* Construct a piecewise quasipolynomial that is constant on the given + * domain. In particular, it is + * 0 if cst == 0 + * 1 if cst == 1 + * infinity if cst == -1 + */ +static __isl_give isl_pw_qpolynomial *constant_on_domain( + __isl_take isl_basic_set *bset, int cst) +{ + isl_space *dim; + isl_qpolynomial *qp; + + if (!bset) + return NULL; + + bset = isl_basic_set_params(bset); + dim = isl_basic_set_get_space(bset); + if (cst < 0) + qp = isl_qpolynomial_infty_on_domain(dim); + else if (cst == 0) + qp = isl_qpolynomial_zero_on_domain(dim); + else + qp = isl_qpolynomial_one_on_domain(dim); + return isl_pw_qpolynomial_alloc(isl_set_from_basic_set(bset), qp); +} + +/* Factor bset, call fn on each of the factors and return the product. + * + * If no factors can be found, simply call fn on the input. + * Otherwise, construct the factors based on the factorizer, + * call fn on each factor and compute the product. + */ +static __isl_give isl_pw_qpolynomial *compressed_multiplicative_call( + __isl_take isl_basic_set *bset, + __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset)) +{ + int i, n; + isl_space *dim; + isl_set *set; + isl_factorizer *f; + isl_qpolynomial *qp; + isl_pw_qpolynomial *pwqp; + unsigned nparam; + unsigned nvar; + + f = isl_basic_set_factorizer(bset); + if (!f) + goto error; + if (f->n_group == 0) { + isl_factorizer_free(f); + return fn(bset); + } + + nparam = isl_basic_set_dim(bset, isl_dim_param); + nvar = isl_basic_set_dim(bset, isl_dim_set); + + dim = isl_basic_set_get_space(bset); + dim = isl_space_domain(dim); + set = isl_set_universe(isl_space_copy(dim)); + qp = isl_qpolynomial_one_on_domain(dim); + pwqp = isl_pw_qpolynomial_alloc(set, qp); + + bset = isl_morph_basic_set(isl_morph_copy(f->morph), bset); + + for (i = 0, n = 0; i < f->n_group; ++i) { + isl_basic_set *bset_i; + isl_pw_qpolynomial *pwqp_i; + + bset_i = isl_basic_set_copy(bset); + bset_i = isl_basic_set_drop_constraints_involving(bset_i, + nparam + n + f->len[i], nvar - n - f->len[i]); + bset_i = isl_basic_set_drop_constraints_involving(bset_i, + nparam, n); + bset_i = isl_basic_set_drop(bset_i, isl_dim_set, + n + f->len[i], nvar - n - f->len[i]); + bset_i = isl_basic_set_drop(bset_i, isl_dim_set, 0, n); + + pwqp_i = fn(bset_i); + pwqp = isl_pw_qpolynomial_mul(pwqp, pwqp_i); + + n += f->len[i]; + } + + isl_basic_set_free(bset); + isl_factorizer_free(f); + + return pwqp; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Factor bset, call fn on each of the factors and return the product. + * The function is assumed to evaluate to zero on empty domains, + * to one on zero-dimensional domains and to infinity on unbounded domains + * and will not be called explicitly on zero-dimensional or unbounded domains. + * + * We first check for some special cases and remove all equalities. + * Then we hand over control to compressed_multiplicative_call. + */ +__isl_give isl_pw_qpolynomial *isl_basic_set_multiplicative_call( + __isl_take isl_basic_set *bset, + __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset)) +{ + int bounded; + isl_morph *morph; + isl_pw_qpolynomial *pwqp; + + if (!bset) + return NULL; + + if (isl_basic_set_plain_is_empty(bset)) + return constant_on_domain(bset, 0); + + if (isl_basic_set_dim(bset, isl_dim_set) == 0) + return constant_on_domain(bset, 1); + + bounded = isl_basic_set_is_bounded(bset); + if (bounded < 0) + goto error; + if (!bounded) + return constant_on_domain(bset, -1); + + if (bset->n_eq == 0) + return compressed_multiplicative_call(bset, fn); + + morph = isl_basic_set_full_compression(bset); + bset = isl_morph_basic_set(isl_morph_copy(morph), bset); + + pwqp = compressed_multiplicative_call(bset, fn); + + morph = isl_morph_dom_params(morph); + morph = isl_morph_ran_params(morph); + morph = isl_morph_inverse(morph); + + pwqp = isl_pw_qpolynomial_morph_domain(pwqp, morph); + + return pwqp; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Drop all floors in "qp", turning each integer division [a/m] into + * a rational division a/m. If "down" is set, then the integer division + * is replaced by (a-(m-1))/m instead. + */ +static __isl_give isl_qpolynomial *qp_drop_floors( + __isl_take isl_qpolynomial *qp, int down) +{ + int i; + struct isl_upoly *s; + + if (!qp) + return NULL; + if (qp->div->n_row == 0) + return qp; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + + for (i = qp->div->n_row - 1; i >= 0; --i) { + if (down) { + isl_int_sub(qp->div->row[i][1], + qp->div->row[i][1], qp->div->row[i][0]); + isl_int_add_ui(qp->div->row[i][1], + qp->div->row[i][1], 1); + } + s = isl_upoly_from_affine(qp->dim->ctx, qp->div->row[i] + 1, + qp->div->row[i][0], qp->div->n_col - 1); + qp = substitute_div(qp, i, s); + if (!qp) + return NULL; + } + + return qp; +} + +/* Drop all floors in "pwqp", turning each integer division [a/m] into + * a rational division a/m. + */ +static __isl_give isl_pw_qpolynomial *pwqp_drop_floors( + __isl_take isl_pw_qpolynomial *pwqp) +{ + int i; + + if (!pwqp) + return NULL; + + if (isl_pw_qpolynomial_is_zero(pwqp)) + return pwqp; + + pwqp = isl_pw_qpolynomial_cow(pwqp); + if (!pwqp) + return NULL; + + for (i = 0; i < pwqp->n; ++i) { + pwqp->p[i].qp = qp_drop_floors(pwqp->p[i].qp, 0); + if (!pwqp->p[i].qp) + goto error; + } + + return pwqp; +error: + isl_pw_qpolynomial_free(pwqp); + return NULL; +} + +/* Adjust all the integer divisions in "qp" such that they are at least + * one over the given orthant (identified by "signs"). This ensures + * that they will still be non-negative even after subtracting (m-1)/m. + * + * In particular, f is replaced by f' + v, changing f = [a/m] + * to f' = [(a - m v)/m]. + * If the constant term k in a is smaller than m, + * the constant term of v is set to floor(k/m) - 1. + * For any other term, if the coefficient c and the variable x have + * the same sign, then no changes are needed. + * Otherwise, if the variable is positive (and c is negative), + * then the coefficient of x in v is set to floor(c/m). + * If the variable is negative (and c is positive), + * then the coefficient of x in v is set to ceil(c/m). + */ +static __isl_give isl_qpolynomial *make_divs_pos(__isl_take isl_qpolynomial *qp, + int *signs) +{ + int i, j; + int total; + isl_vec *v = NULL; + struct isl_upoly *s; + + qp = isl_qpolynomial_cow(qp); + if (!qp) + return NULL; + qp->div = isl_mat_cow(qp->div); + if (!qp->div) + goto error; + + total = isl_space_dim(qp->dim, isl_dim_all); + v = isl_vec_alloc(qp->div->ctx, qp->div->n_col - 1); + + for (i = 0; i < qp->div->n_row; ++i) { + isl_int *row = qp->div->row[i]; + v = isl_vec_clr(v); + if (!v) + goto error; + if (isl_int_lt(row[1], row[0])) { + isl_int_fdiv_q(v->el[0], row[1], row[0]); + isl_int_sub_ui(v->el[0], v->el[0], 1); + isl_int_submul(row[1], row[0], v->el[0]); + } + for (j = 0; j < total; ++j) { + if (isl_int_sgn(row[2 + j]) * signs[j] >= 0) + continue; + if (signs[j] < 0) + isl_int_cdiv_q(v->el[1 + j], row[2 + j], row[0]); + else + isl_int_fdiv_q(v->el[1 + j], row[2 + j], row[0]); + isl_int_submul(row[2 + j], row[0], v->el[1 + j]); + } + for (j = 0; j < i; ++j) { + if (isl_int_sgn(row[2 + total + j]) >= 0) + continue; + isl_int_fdiv_q(v->el[1 + total + j], + row[2 + total + j], row[0]); + isl_int_submul(row[2 + total + j], + row[0], v->el[1 + total + j]); + } + for (j = i + 1; j < qp->div->n_row; ++j) { + if (isl_int_is_zero(qp->div->row[j][2 + total + i])) + continue; + isl_seq_combine(qp->div->row[j] + 1, + qp->div->ctx->one, qp->div->row[j] + 1, + qp->div->row[j][2 + total + i], v->el, v->size); + } + isl_int_set_si(v->el[1 + total + i], 1); + s = isl_upoly_from_affine(qp->dim->ctx, v->el, + qp->div->ctx->one, v->size); + qp->upoly = isl_upoly_subs(qp->upoly, total + i, 1, &s); + isl_upoly_free(s); + if (!qp->upoly) + goto error; + } + + isl_vec_free(v); + return qp; +error: + isl_vec_free(v); + isl_qpolynomial_free(qp); + return NULL; +} + +struct isl_to_poly_data { + int sign; + isl_pw_qpolynomial *res; + isl_qpolynomial *qp; +}; + +/* Appoximate data->qp by a polynomial on the orthant identified by "signs". + * We first make all integer divisions positive and then split the + * quasipolynomials into terms with sign data->sign (the direction + * of the requested approximation) and terms with the opposite sign. + * In the first set of terms, each integer division [a/m] is + * overapproximated by a/m, while in the second it is underapproximated + * by (a-(m-1))/m. + */ +static int to_polynomial_on_orthant(__isl_take isl_set *orthant, int *signs, + void *user) +{ + struct isl_to_poly_data *data = user; + isl_pw_qpolynomial *t; + isl_qpolynomial *qp, *up, *down; + + qp = isl_qpolynomial_copy(data->qp); + qp = make_divs_pos(qp, signs); + + up = isl_qpolynomial_terms_of_sign(qp, signs, data->sign); + up = qp_drop_floors(up, 0); + down = isl_qpolynomial_terms_of_sign(qp, signs, -data->sign); + down = qp_drop_floors(down, 1); + + isl_qpolynomial_free(qp); + qp = isl_qpolynomial_add(up, down); + + t = isl_pw_qpolynomial_alloc(orthant, qp); + data->res = isl_pw_qpolynomial_add_disjoint(data->res, t); + + return 0; +} + +/* Approximate each quasipolynomial by a polynomial. If "sign" is positive, + * the polynomial will be an overapproximation. If "sign" is negative, + * it will be an underapproximation. If "sign" is zero, the approximation + * will lie somewhere in between. + * + * In particular, is sign == 0, we simply drop the floors, turning + * the integer divisions into rational divisions. + * Otherwise, we split the domains into orthants, make all integer divisions + * positive and then approximate each [a/m] by either a/m or (a-(m-1))/m, + * depending on the requested sign and the sign of the term in which + * the integer division appears. + */ +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_to_polynomial( + __isl_take isl_pw_qpolynomial *pwqp, int sign) +{ + int i; + struct isl_to_poly_data data; + + if (sign == 0) + return pwqp_drop_floors(pwqp); + + if (!pwqp) + return NULL; + + data.sign = sign; + data.res = isl_pw_qpolynomial_zero(isl_pw_qpolynomial_get_space(pwqp)); + + for (i = 0; i < pwqp->n; ++i) { + if (pwqp->p[i].qp->div->n_row == 0) { + isl_pw_qpolynomial *t; + t = isl_pw_qpolynomial_alloc( + isl_set_copy(pwqp->p[i].set), + isl_qpolynomial_copy(pwqp->p[i].qp)); + data.res = isl_pw_qpolynomial_add_disjoint(data.res, t); + continue; + } + data.qp = pwqp->p[i].qp; + if (isl_set_foreach_orthant(pwqp->p[i].set, + &to_polynomial_on_orthant, &data) < 0) + goto error; + } + + isl_pw_qpolynomial_free(pwqp); + + return data.res; +error: + isl_pw_qpolynomial_free(pwqp); + isl_pw_qpolynomial_free(data.res); + return NULL; +} + +static __isl_give isl_pw_qpolynomial *poly_entry( + __isl_take isl_pw_qpolynomial *pwqp, void *user) +{ + int *sign = user; + + return isl_pw_qpolynomial_to_polynomial(pwqp, *sign); +} + +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_to_polynomial( + __isl_take isl_union_pw_qpolynomial *upwqp, int sign) +{ + return isl_union_pw_qpolynomial_transform_inplace(upwqp, + &poly_entry, &sign); +} + +__isl_give isl_basic_map *isl_basic_map_from_qpolynomial( + __isl_take isl_qpolynomial *qp) +{ + int i, k; + isl_space *dim; + isl_vec *aff = NULL; + isl_basic_map *bmap = NULL; + unsigned pos; + unsigned n_div; + + if (!qp) + return NULL; + if (!isl_upoly_is_affine(qp->upoly)) + isl_die(qp->dim->ctx, isl_error_invalid, + "input quasi-polynomial not affine", goto error); + aff = isl_qpolynomial_extract_affine(qp); + if (!aff) + goto error; + dim = isl_qpolynomial_get_space(qp); + pos = 1 + isl_space_offset(dim, isl_dim_out); + n_div = qp->div->n_row; + bmap = isl_basic_map_alloc_space(dim, n_div, 1, 2 * n_div); + + for (i = 0; i < n_div; ++i) { + k = isl_basic_map_alloc_div(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->div[k], qp->div->row[i], qp->div->n_col); + isl_int_set_si(bmap->div[k][qp->div->n_col], 0); + if (isl_basic_map_add_div_constraints(bmap, k) < 0) + goto error; + } + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_int_neg(bmap->eq[k][pos], aff->el[0]); + isl_seq_cpy(bmap->eq[k], aff->el + 1, pos); + isl_seq_cpy(bmap->eq[k] + pos + 1, aff->el + 1 + pos, n_div); + + isl_vec_free(aff); + isl_qpolynomial_free(qp); + bmap = isl_basic_map_finalize(bmap); + return bmap; +error: + isl_vec_free(aff); + isl_qpolynomial_free(qp); + isl_basic_map_free(bmap); + return NULL; +} Index: lib/Analysis/isl/isl_polynomial_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_polynomial_private.h @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include +#include + +struct isl_upoly { + int ref; + struct isl_ctx *ctx; + + int var; +}; + +struct isl_upoly_cst { + struct isl_upoly up; + isl_int n; + isl_int d; +}; + +struct isl_upoly_rec { + struct isl_upoly up; + int n; + + size_t size; + struct isl_upoly *p[]; +}; + +/* dim represents the domain space. + */ +struct isl_qpolynomial { + int ref; + + isl_space *dim; + struct isl_mat *div; + struct isl_upoly *upoly; +}; + +struct isl_term { + int ref; + + isl_int n; + isl_int d; + + isl_space *dim; + struct isl_mat *div; + + int pow[1]; +}; + +struct isl_pw_qpolynomial_piece { + struct isl_set *set; + struct isl_qpolynomial *qp; +}; + +struct isl_pw_qpolynomial { + int ref; + + isl_space *dim; + + int n; + + size_t size; + struct isl_pw_qpolynomial_piece p[1]; +}; + +/* dim represents the domain space. + */ +struct isl_qpolynomial_fold { + int ref; + + enum isl_fold type; + isl_space *dim; + + int n; + + size_t size; + struct isl_qpolynomial *qp[1]; +}; + +struct isl_pw_qpolynomial_fold_piece { + struct isl_set *set; + struct isl_qpolynomial_fold *fold; +}; + +struct isl_pw_qpolynomial_fold { + int ref; + + enum isl_fold type; + isl_space *dim; + + int n; + + size_t size; + struct isl_pw_qpolynomial_fold_piece p[1]; +}; + +void isl_term_get_num(__isl_keep isl_term *term, isl_int *n); + +__isl_give struct isl_upoly *isl_upoly_zero(struct isl_ctx *ctx); +__isl_give struct isl_upoly *isl_upoly_copy(__isl_keep struct isl_upoly *up); +__isl_give struct isl_upoly *isl_upoly_cow(__isl_take struct isl_upoly *up); +__isl_give struct isl_upoly *isl_upoly_dup(__isl_keep struct isl_upoly *up); +void isl_upoly_free(__isl_take struct isl_upoly *up); +__isl_give struct isl_upoly *isl_upoly_mul(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2); + +int isl_upoly_is_cst(__isl_keep struct isl_upoly *up); +int isl_upoly_is_zero(__isl_keep struct isl_upoly *up); +int isl_upoly_is_one(__isl_keep struct isl_upoly *up); +int isl_upoly_is_negone(__isl_keep struct isl_upoly *up); +__isl_keep struct isl_upoly_cst *isl_upoly_as_cst(__isl_keep struct isl_upoly *up); +__isl_keep struct isl_upoly_rec *isl_upoly_as_rec(__isl_keep struct isl_upoly *up); + +__isl_give struct isl_upoly *isl_upoly_sum(__isl_take struct isl_upoly *up1, + __isl_take struct isl_upoly *up2); +__isl_give struct isl_upoly *isl_upoly_mul_isl_int( + __isl_take struct isl_upoly *up, isl_int v); + +__isl_give isl_qpolynomial *isl_qpolynomial_alloc(__isl_take isl_space *dim, + unsigned n_div, __isl_take struct isl_upoly *up); +__isl_give isl_qpolynomial *isl_qpolynomial_cow(__isl_take isl_qpolynomial *qp); +__isl_give isl_qpolynomial *isl_qpolynomial_dup(__isl_keep isl_qpolynomial *qp); + +__isl_give isl_qpolynomial *isl_qpolynomial_cst_on_domain(__isl_take isl_space *dim, + isl_int v); +__isl_give isl_qpolynomial *isl_qpolynomial_rat_cst_on_domain( + __isl_take isl_space *space, const isl_int n, const isl_int d); +__isl_give isl_qpolynomial *isl_qpolynomial_var_pow_on_domain(__isl_take isl_space *dim, + int pos, int power); +isl_bool isl_qpolynomial_is_one(__isl_keep isl_qpolynomial *qp); +int isl_qpolynomial_is_affine(__isl_keep isl_qpolynomial *qp); +int isl_qpolynomial_is_cst(__isl_keep isl_qpolynomial *qp, + isl_int *n, isl_int *d); + +__isl_give isl_qpolynomial *isl_qpolynomial_add_on_domain( + __isl_keep isl_set *dom, + __isl_take isl_qpolynomial *qp1, + __isl_take isl_qpolynomial *qp2); + +int isl_qpolynomial_plain_cmp(__isl_keep isl_qpolynomial *qp1, + __isl_keep isl_qpolynomial *qp2); + +int isl_qpolynomial_degree(__isl_keep isl_qpolynomial *poly); +__isl_give isl_qpolynomial *isl_qpolynomial_coeff( + __isl_keep isl_qpolynomial *poly, + enum isl_dim_type type, unsigned pos, int deg); + +__isl_give isl_vec *isl_qpolynomial_extract_affine( + __isl_keep isl_qpolynomial *qp); +__isl_give isl_qpolynomial *isl_qpolynomial_from_affine(__isl_take isl_space *dim, + isl_int *f, isl_int denom); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_cow( + __isl_take isl_pw_qpolynomial *pwqp); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_piece( + __isl_take isl_pw_qpolynomial *pwqp, + __isl_take isl_set *set, __isl_take isl_qpolynomial *qp); +int isl_pw_qpolynomial_is_one(__isl_keep isl_pw_qpolynomial *pwqp); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_project_out( + __isl_take isl_pw_qpolynomial *pwqp, + enum isl_dim_type type, unsigned first, unsigned n); + +__isl_give isl_val *isl_qpolynomial_opt_on_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_set *set, int max); + +enum isl_fold isl_fold_type_negate(enum isl_fold type); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_cow( + __isl_take isl_qpolynomial_fold *fold); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_dup( + __isl_keep isl_qpolynomial_fold *fold); + +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_cow( + __isl_take isl_pw_qpolynomial_fold *pwf); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_add_on_domain( + __isl_keep isl_set *set, + __isl_take isl_qpolynomial_fold *fold1, + __isl_take isl_qpolynomial_fold *fold2); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold_on_domain( + __isl_keep isl_set *set, + __isl_take isl_qpolynomial_fold *fold1, + __isl_take isl_qpolynomial_fold *fold2); + +int isl_qpolynomial_fold_plain_cmp(__isl_keep isl_qpolynomial_fold *fold1, + __isl_keep isl_qpolynomial_fold *fold2); + +__isl_give isl_val *isl_qpolynomial_fold_opt_on_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *set, int max); + +int isl_pw_qpolynomial_fold_covers(__isl_keep isl_pw_qpolynomial_fold *pwf1, + __isl_keep isl_pw_qpolynomial_fold *pwf2); + +__isl_give isl_qpolynomial *isl_qpolynomial_morph_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_morph *morph); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_morph_domain( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_morph *morph); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_morph_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_morph *morph); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_morph_domain( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_morph *morph); + +__isl_give isl_qpolynomial *isl_qpolynomial_lift(__isl_take isl_qpolynomial *qp, + __isl_take isl_space *dim); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_lift( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *dim); + +__isl_give isl_qpolynomial *isl_qpolynomial_substitute_equalities( + __isl_take isl_qpolynomial *qp, __isl_take isl_basic_set *eq); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute_equalities( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_basic_set *eq); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context); + +__isl_give isl_qpolynomial *isl_qpolynomial_realign_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_reordering *r); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_realign_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_reordering *r); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_realign_domain( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_reordering *r); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_realign_domain( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_reordering *r); + +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_reset_space( + __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_space *space); +__isl_give isl_qpolynomial *isl_qpolynomial_reset_domain_space( + __isl_take isl_qpolynomial *qp, __isl_take isl_space *dim); +__isl_give isl_qpolynomial *isl_qpolynomial_reset_space_and_domain( + __isl_take isl_qpolynomial *qp, __isl_take isl_space *space, + __isl_take isl_space *domain); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_domain_space( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *dim); +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_space_and_domain( + __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space, + __isl_take isl_space *domain); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_reset_domain_space( + __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_space *dim); + +void isl_qpolynomial_get_den(__isl_keep isl_qpolynomial *qp, isl_int *d); +__isl_give isl_qpolynomial *isl_qpolynomial_add_isl_int( + __isl_take isl_qpolynomial *qp, isl_int v); +__isl_give isl_qpolynomial *isl_qpolynomial_mul_isl_int( + __isl_take isl_qpolynomial *qp, isl_int v); +__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul_isl_int( + __isl_take isl_pw_qpolynomial *pwqp, isl_int v); + +__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_mul_isl_int( + __isl_take isl_qpolynomial_fold *fold, isl_int v); +__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_mul_isl_int( + __isl_take isl_pw_qpolynomial_fold *pwf, isl_int v); +__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul_isl_int( + __isl_take isl_union_pw_qpolynomial *upwqp, isl_int v); +__isl_give isl_union_pw_qpolynomial_fold * +isl_union_pw_qpolynomial_fold_mul_isl_int( + __isl_take isl_union_pw_qpolynomial_fold *upwf, isl_int v); Index: lib/Analysis/isl/isl_power_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_power_templ.c @@ -0,0 +1,81 @@ +#include + +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) + +/* Compute the given non-zero power of "map" and return the result. + * If the exponent "exp" is negative, then the -exp th power of the inverse + * relation is computed. + */ +__isl_give TYPE *FN(TYPE,fixed_power)(__isl_take TYPE *map, isl_int exp) +{ + isl_ctx *ctx; + TYPE *res = NULL; + isl_int r; + + if (!map) + return NULL; + + ctx = FN(TYPE,get_ctx)(map); + if (isl_int_is_zero(exp)) + isl_die(ctx, isl_error_invalid, + "expecting non-zero exponent", goto error); + + if (isl_int_is_neg(exp)) { + isl_int_neg(exp, exp); + map = FN(TYPE,reverse)(map); + return FN(TYPE,fixed_power)(map, exp); + } + + isl_int_init(r); + for (;;) { + isl_int_fdiv_r(r, exp, ctx->two); + + if (!isl_int_is_zero(r)) { + if (!res) + res = FN(TYPE,copy)(map); + else { + res = FN(TYPE,apply_range)(res, + FN(TYPE,copy)(map)); + res = FN(TYPE,coalesce)(res); + } + if (!res) + break; + } + + isl_int_fdiv_q(exp, exp, ctx->two); + if (isl_int_is_zero(exp)) + break; + + map = FN(TYPE,apply_range)(map, FN(TYPE,copy)(map)); + map = FN(TYPE,coalesce)(map); + } + isl_int_clear(r); + + FN(TYPE,free)(map); + return res; +error: + FN(TYPE,free)(map); + return NULL; +} + +/* Compute the given non-zero power of "map" and return the result. + * If the exponent "exp" is negative, then the -exp th power of the inverse + * relation is computed. + */ +__isl_give TYPE *FN(TYPE,fixed_power_val)(__isl_take TYPE *map, + __isl_take isl_val *exp) +{ + if (!map || !exp) + goto error; + if (!isl_val_is_int(exp)) + isl_die(FN(TYPE,get_ctx)(map), isl_error_invalid, + "expecting integer exponent", goto error); + map = FN(TYPE,fixed_power)(map, exp->n); + isl_val_free(exp); + return map; +error: + FN(TYPE,free)(map); + isl_val_free(exp); + return NULL; +} Index: lib/Analysis/isl/isl_printer.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_printer.c @@ -0,0 +1,840 @@ +#include +#include +#include + +static __isl_give isl_printer *file_start_line(__isl_take isl_printer *p) +{ + fprintf(p->file, "%s%*s%s", p->indent_prefix ? p->indent_prefix : "", + p->indent, "", p->prefix ? p->prefix : ""); + return p; +} + +static __isl_give isl_printer *file_end_line(__isl_take isl_printer *p) +{ + fprintf(p->file, "%s\n", p->suffix ? p->suffix : ""); + return p; +} + +static __isl_give isl_printer *file_flush(__isl_take isl_printer *p) +{ + fflush(p->file); + return p; +} + +static __isl_give isl_printer *file_print_str(__isl_take isl_printer *p, + const char *s) +{ + fprintf(p->file, "%s", s); + return p; +} + +static __isl_give isl_printer *file_print_double(__isl_take isl_printer *p, + double d) +{ + fprintf(p->file, "%g", d); + return p; +} + +static __isl_give isl_printer *file_print_int(__isl_take isl_printer *p, int i) +{ + fprintf(p->file, "%d", i); + return p; +} + +static __isl_give isl_printer *file_print_isl_int(__isl_take isl_printer *p, isl_int i) +{ + isl_int_print(p->file, i, p->width); + return p; +} + +static int grow_buf(__isl_keep isl_printer *p, int extra) +{ + int new_size; + char *new_buf; + + if (p->buf_size == 0) + return -1; + + new_size = ((p->buf_n + extra + 1) * 3) / 2; + new_buf = isl_realloc_array(p->ctx, p->buf, char, new_size); + if (!new_buf) { + p->buf_size = 0; + return -1; + } + p->buf = new_buf; + p->buf_size = new_size; + + return 0; +} + +static __isl_give isl_printer *str_print(__isl_take isl_printer *p, + const char *s, int len) +{ + if (p->buf_n + len + 1 >= p->buf_size && grow_buf(p, len)) + goto error; + memcpy(p->buf + p->buf_n, s, len); + p->buf_n += len; + + p->buf[p->buf_n] = '\0'; + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *str_print_indent(__isl_take isl_printer *p, + int indent) +{ + int i; + + if (p->buf_n + indent + 1 >= p->buf_size && grow_buf(p, indent)) + goto error; + for (i = 0; i < indent; ++i) + p->buf[p->buf_n++] = ' '; + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *str_start_line(__isl_take isl_printer *p) +{ + if (p->indent_prefix) + p = str_print(p, p->indent_prefix, strlen(p->indent_prefix)); + p = str_print_indent(p, p->indent); + if (p->prefix) + p = str_print(p, p->prefix, strlen(p->prefix)); + return p; +} + +static __isl_give isl_printer *str_end_line(__isl_take isl_printer *p) +{ + if (p->suffix) + p = str_print(p, p->suffix, strlen(p->suffix)); + p = str_print(p, "\n", strlen("\n")); + return p; +} + +static __isl_give isl_printer *str_flush(__isl_take isl_printer *p) +{ + p->buf_n = 0; + p->buf[p->buf_n] = '\0'; + return p; +} + +static __isl_give isl_printer *str_print_str(__isl_take isl_printer *p, + const char *s) +{ + return str_print(p, s, strlen(s)); +} + +static __isl_give isl_printer *str_print_double(__isl_take isl_printer *p, + double d) +{ + int left = p->buf_size - p->buf_n; + int need = snprintf(p->buf + p->buf_n, left, "%g", d); + if (need >= left) { + if (grow_buf(p, need)) + goto error; + left = p->buf_size - p->buf_n; + need = snprintf(p->buf + p->buf_n, left, "%g", d); + } + p->buf_n += need; + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *str_print_int(__isl_take isl_printer *p, int i) +{ + int left = p->buf_size - p->buf_n; + int need = snprintf(p->buf + p->buf_n, left, "%d", i); + if (need >= left) { + if (grow_buf(p, need)) + goto error; + left = p->buf_size - p->buf_n; + need = snprintf(p->buf + p->buf_n, left, "%d", i); + } + p->buf_n += need; + return p; +error: + isl_printer_free(p); + return NULL; +} + +static __isl_give isl_printer *str_print_isl_int(__isl_take isl_printer *p, + isl_int i) +{ + char *s; + int len; + + s = isl_int_get_str(i); + len = strlen(s); + if (len < p->width) + p = str_print_indent(p, p->width - len); + p = str_print(p, s, len); + isl_int_free_str(s); + return p; +} + +struct isl_printer_ops { + __isl_give isl_printer *(*start_line)(__isl_take isl_printer *p); + __isl_give isl_printer *(*end_line)(__isl_take isl_printer *p); + __isl_give isl_printer *(*print_double)(__isl_take isl_printer *p, + double d); + __isl_give isl_printer *(*print_int)(__isl_take isl_printer *p, int i); + __isl_give isl_printer *(*print_isl_int)(__isl_take isl_printer *p, + isl_int i); + __isl_give isl_printer *(*print_str)(__isl_take isl_printer *p, + const char *s); + __isl_give isl_printer *(*flush)(__isl_take isl_printer *p); +}; + +static struct isl_printer_ops file_ops = { + file_start_line, + file_end_line, + file_print_double, + file_print_int, + file_print_isl_int, + file_print_str, + file_flush +}; + +static struct isl_printer_ops str_ops = { + str_start_line, + str_end_line, + str_print_double, + str_print_int, + str_print_isl_int, + str_print_str, + str_flush +}; + +__isl_give isl_printer *isl_printer_to_file(isl_ctx *ctx, FILE *file) +{ + struct isl_printer *p = isl_calloc_type(ctx, struct isl_printer); + if (!p) + return NULL; + p->ctx = ctx; + isl_ctx_ref(p->ctx); + p->ops = &file_ops; + p->file = file; + p->buf = NULL; + p->buf_n = 0; + p->buf_size = 0; + p->indent = 0; + p->output_format = ISL_FORMAT_ISL; + p->indent_prefix = NULL; + p->prefix = NULL; + p->suffix = NULL; + p->width = 0; + p->yaml_style = ISL_YAML_STYLE_FLOW; + + return p; +} + +__isl_give isl_printer *isl_printer_to_str(isl_ctx *ctx) +{ + struct isl_printer *p = isl_calloc_type(ctx, struct isl_printer); + if (!p) + return NULL; + p->ctx = ctx; + isl_ctx_ref(p->ctx); + p->ops = &str_ops; + p->file = NULL; + p->buf = isl_alloc_array(ctx, char, 256); + if (!p->buf) + goto error; + p->buf_n = 0; + p->buf[0] = '\0'; + p->buf_size = 256; + p->indent = 0; + p->output_format = ISL_FORMAT_ISL; + p->indent_prefix = NULL; + p->prefix = NULL; + p->suffix = NULL; + p->width = 0; + p->yaml_style = ISL_YAML_STYLE_FLOW; + + return p; +error: + isl_printer_free(p); + return NULL; +} + +__isl_null isl_printer *isl_printer_free(__isl_take isl_printer *p) +{ + if (!p) + return NULL; + free(p->buf); + free(p->indent_prefix); + free(p->prefix); + free(p->suffix); + free(p->yaml_state); + isl_id_to_id_free(p->notes); + isl_ctx_deref(p->ctx); + free(p); + + return NULL; +} + +isl_ctx *isl_printer_get_ctx(__isl_keep isl_printer *printer) +{ + return printer ? printer->ctx : NULL; +} + +FILE *isl_printer_get_file(__isl_keep isl_printer *printer) +{ + if (!printer) + return NULL; + if (!printer->file) + isl_die(isl_printer_get_ctx(printer), isl_error_invalid, + "not a file printer", return NULL); + return printer->file; +} + +__isl_give isl_printer *isl_printer_set_isl_int_width(__isl_take isl_printer *p, + int width) +{ + if (!p) + return NULL; + + p->width = width; + + return p; +} + +__isl_give isl_printer *isl_printer_set_indent(__isl_take isl_printer *p, + int indent) +{ + if (!p) + return NULL; + + p->indent = indent; + + return p; +} + +__isl_give isl_printer *isl_printer_indent(__isl_take isl_printer *p, + int indent) +{ + if (!p) + return NULL; + + p->indent += indent; + if (p->indent < 0) + p->indent = 0; + + return p; +} + +/* Replace the indent prefix of "p" by "prefix". + */ +__isl_give isl_printer *isl_printer_set_indent_prefix(__isl_take isl_printer *p, + const char *prefix) +{ + if (!p) + return NULL; + + free(p->indent_prefix); + p->indent_prefix = prefix ? strdup(prefix) : NULL; + + return p; +} + +__isl_give isl_printer *isl_printer_set_prefix(__isl_take isl_printer *p, + const char *prefix) +{ + if (!p) + return NULL; + + free(p->prefix); + p->prefix = prefix ? strdup(prefix) : NULL; + + return p; +} + +__isl_give isl_printer *isl_printer_set_suffix(__isl_take isl_printer *p, + const char *suffix) +{ + if (!p) + return NULL; + + free(p->suffix); + p->suffix = suffix ? strdup(suffix) : NULL; + + return p; +} + +__isl_give isl_printer *isl_printer_set_output_format(__isl_take isl_printer *p, + int output_format) +{ + if (!p) + return NULL; + + p->output_format = output_format; + + return p; +} + +int isl_printer_get_output_format(__isl_keep isl_printer *p) +{ + if (!p) + return -1; + return p->output_format; +} + +/* Does "p" have a note with identifier "id"? + */ +isl_bool isl_printer_has_note(__isl_keep isl_printer *p, + __isl_keep isl_id *id) +{ + if (!p || !id) + return isl_bool_error; + if (!p->notes) + return isl_bool_false; + return isl_id_to_id_has(p->notes, id); +} + +/* Retrieve the note identified by "id" from "p". + * The note is assumed to exist. + */ +__isl_give isl_id *isl_printer_get_note(__isl_keep isl_printer *p, + __isl_take isl_id *id) +{ + isl_bool has_note; + + has_note = isl_printer_has_note(p, id); + if (has_note < 0) + goto error; + if (!has_note) + isl_die(isl_printer_get_ctx(p), isl_error_invalid, + "no such note", goto error); + + return isl_id_to_id_get(p->notes, id); +error: + isl_id_free(id); + return NULL; +} + +/* Associate "note" to the identifier "id" in "p", + * replacing the previous note associated to the identifier, if any. + */ +__isl_give isl_printer *isl_printer_set_note(__isl_take isl_printer *p, + __isl_take isl_id *id, __isl_take isl_id *note) +{ + if (!p || !id || !note) + goto error; + if (!p->notes) { + p->notes = isl_id_to_id_alloc(isl_printer_get_ctx(p), 1); + if (!p->notes) + goto error; + } + p->notes = isl_id_to_id_set(p->notes, id, note); + if (!p->notes) + return isl_printer_free(p); + return p; +error: + isl_printer_free(p); + isl_id_free(id); + isl_id_free(note); + return NULL; +} + +/* Keep track of whether the printing to "p" is being performed from + * an isl_*_dump function as specified by "dump". + */ +__isl_give isl_printer *isl_printer_set_dump(__isl_take isl_printer *p, + int dump) +{ + if (!p) + return NULL; + + p->dump = dump; + + return p; +} + +/* Set the YAML style of "p" to "yaml_style" and return the updated printer. + */ +__isl_give isl_printer *isl_printer_set_yaml_style(__isl_take isl_printer *p, + int yaml_style) +{ + if (!p) + return NULL; + + p->yaml_style = yaml_style; + + return p; +} + +/* Return the YAML style of "p" or -1 on error. + */ +int isl_printer_get_yaml_style(__isl_keep isl_printer *p) +{ + if (!p) + return -1; + return p->yaml_style; +} + +/* Push "state" onto the stack of currently active YAML elements and + * return the updated printer. + */ +static __isl_give isl_printer *push_state(__isl_take isl_printer *p, + enum isl_yaml_state state) +{ + if (!p) + return NULL; + + if (p->yaml_size < p->yaml_depth + 1) { + enum isl_yaml_state *state; + state = isl_realloc_array(p->ctx, p->yaml_state, + enum isl_yaml_state, p->yaml_depth + 1); + if (!state) + return isl_printer_free(p); + p->yaml_state = state; + p->yaml_size = p->yaml_depth + 1; + } + + p->yaml_state[p->yaml_depth] = state; + p->yaml_depth++; + + return p; +} + +/* Remove the innermost active YAML element from the stack and + * return the updated printer. + */ +static __isl_give isl_printer *pop_state(__isl_take isl_printer *p) +{ + if (!p) + return NULL; + p->yaml_depth--; + return p; +} + +/* Set the state of the innermost active YAML element to "state" and + * return the updated printer. + */ +static __isl_give isl_printer *update_state(__isl_take isl_printer *p, + enum isl_yaml_state state) +{ + if (!p) + return NULL; + if (p->yaml_depth < 1) + isl_die(isl_printer_get_ctx(p), isl_error_invalid, + "not in YAML construct", return isl_printer_free(p)); + + p->yaml_state[p->yaml_depth - 1] = state; + + return p; +} + +/* Return the state of the innermost active YAML element. + * Return isl_yaml_none if we are not inside any YAML element. + */ +static enum isl_yaml_state current_state(__isl_keep isl_printer *p) +{ + if (!p) + return isl_yaml_none; + if (p->yaml_depth < 1) + return isl_yaml_none; + return p->yaml_state[p->yaml_depth - 1]; +} + +/* If we are printing a YAML document and we are at the start of an element, + * print whatever is needed before we can print the actual element and + * keep track of the fact that we are now printing the element. + * If "eol" is set, then whatever we print is going to be the last + * thing that gets printed on this line. + * + * If we are about the print the first key of a mapping, then nothing + * extra needs to be printed. For any other key, however, we need + * to either move to the next line (in block format) or print a comma + * (in flow format). + * Before printing a value in a mapping, we need to print a colon. + * + * For sequences, in flow format, we only need to print a comma + * for each element except the first. + * In block format, before the first element in the sequence, + * we move to a new line, print a dash and increase the indentation. + * Before any other element, we print a dash on a new line, + * temporarily moving the indentation back. + */ +static __isl_give isl_printer *enter_state(__isl_take isl_printer *p, + int eol) +{ + enum isl_yaml_state state; + + if (!p) + return NULL; + + state = current_state(p); + if (state == isl_yaml_mapping_val_start) { + if (eol) + p = p->ops->print_str(p, ":"); + else + p = p->ops->print_str(p, ": "); + p = update_state(p, isl_yaml_mapping_val); + } else if (state == isl_yaml_mapping_first_key_start) { + p = update_state(p, isl_yaml_mapping_key); + } else if (state == isl_yaml_mapping_key_start) { + if (p->yaml_style == ISL_YAML_STYLE_FLOW) + p = p->ops->print_str(p, ", "); + else { + p = p->ops->end_line(p); + p = p->ops->start_line(p); + } + p = update_state(p, isl_yaml_mapping_key); + } else if (state == isl_yaml_sequence_first_start) { + if (p->yaml_style != ISL_YAML_STYLE_FLOW) { + p = p->ops->end_line(p); + p = p->ops->start_line(p); + p = p->ops->print_str(p, "- "); + p = isl_printer_indent(p, 2); + } + p = update_state(p, isl_yaml_sequence); + } else if (state == isl_yaml_sequence_start) { + if (p->yaml_style == ISL_YAML_STYLE_FLOW) + p = p->ops->print_str(p, ", "); + else { + p = p->ops->end_line(p); + p = isl_printer_indent(p, -2); + p = p->ops->start_line(p); + p = p->ops->print_str(p, "- "); + p = isl_printer_indent(p, 2); + } + p = update_state(p, isl_yaml_sequence); + } + + return p; +} + +__isl_give isl_printer *isl_printer_print_str(__isl_take isl_printer *p, + const char *s) +{ + if (!p) + return NULL; + if (!s) + return isl_printer_free(p); + p = enter_state(p, 0); + if (!p) + return NULL; + return p->ops->print_str(p, s); +} + +__isl_give isl_printer *isl_printer_print_double(__isl_take isl_printer *p, + double d) +{ + p = enter_state(p, 0); + if (!p) + return NULL; + + return p->ops->print_double(p, d); +} + +__isl_give isl_printer *isl_printer_print_int(__isl_take isl_printer *p, int i) +{ + p = enter_state(p, 0); + if (!p) + return NULL; + + return p->ops->print_int(p, i); +} + +__isl_give isl_printer *isl_printer_print_isl_int(__isl_take isl_printer *p, + isl_int i) +{ + p = enter_state(p, 0); + if (!p) + return NULL; + + return p->ops->print_isl_int(p, i); +} + +__isl_give isl_printer *isl_printer_start_line(__isl_take isl_printer *p) +{ + if (!p) + return NULL; + + return p->ops->start_line(p); +} + +__isl_give isl_printer *isl_printer_end_line(__isl_take isl_printer *p) +{ + if (!p) + return NULL; + + return p->ops->end_line(p); +} + +char *isl_printer_get_str(__isl_keep isl_printer *printer) +{ + if (!printer || !printer->buf) + return NULL; + return strdup(printer->buf); +} + +__isl_give isl_printer *isl_printer_flush(__isl_take isl_printer *p) +{ + if (!p) + return NULL; + + return p->ops->flush(p); +} + +/* Start a YAML mapping and push a new state to reflect that we + * are about to print the first key in a mapping. + * + * In flow style, print the opening brace. + * In block style, move to the next line with an increased indentation, + * except if this is the outer mapping or if we are inside a sequence + * (in which case we have already increased the indentation and we want + * to print the first key on the same line as the dash). + */ +__isl_give isl_printer *isl_printer_yaml_start_mapping( + __isl_take isl_printer *p) +{ + enum isl_yaml_state state; + + if (!p) + return NULL; + p = enter_state(p, p->yaml_style == ISL_YAML_STYLE_BLOCK); + if (!p) + return NULL; + state = current_state(p); + if (p->yaml_style == ISL_YAML_STYLE_FLOW) + p = p->ops->print_str(p, "{ "); + else if (state != isl_yaml_none && state != isl_yaml_sequence) { + p = p->ops->end_line(p); + p = isl_printer_indent(p, 2); + p = p->ops->start_line(p); + } + p = push_state(p, isl_yaml_mapping_first_key_start); + return p; +} + +/* Finish a YAML mapping and pop it from the state stack. + * + * In flow style, print the closing brace. + * + * In block style, first check if we are still in the + * isl_yaml_mapping_first_key_start state. If so, we have not printed + * anything yet, so print "{}" to indicate an empty mapping. + * If we increased the indentation in isl_printer_yaml_start_mapping, + * then decrease it again. + * If this is the outer mapping then print a newline. + */ +__isl_give isl_printer *isl_printer_yaml_end_mapping( + __isl_take isl_printer *p) +{ + enum isl_yaml_state state; + + state = current_state(p); + p = pop_state(p); + if (!p) + return NULL; + if (p->yaml_style == ISL_YAML_STYLE_FLOW) + return p->ops->print_str(p, " }"); + if (state == isl_yaml_mapping_first_key_start) + p = p->ops->print_str(p, "{}"); + if (!p) + return NULL; + state = current_state(p); + if (state != isl_yaml_none && state != isl_yaml_sequence) + p = isl_printer_indent(p, -2); + if (state == isl_yaml_none) + p = p->ops->end_line(p); + return p; +} + +/* Start a YAML sequence and push a new state to reflect that we + * are about to print the first element in a sequence. + * + * In flow style, print the opening bracket. + */ +__isl_give isl_printer *isl_printer_yaml_start_sequence( + __isl_take isl_printer *p) +{ + if (!p) + return NULL; + p = enter_state(p, p->yaml_style == ISL_YAML_STYLE_BLOCK); + p = push_state(p, isl_yaml_sequence_first_start); + if (!p) + return NULL; + if (p->yaml_style == ISL_YAML_STYLE_FLOW) + p = p->ops->print_str(p, "[ "); + return p; +} + +/* Finish a YAML sequence and pop it from the state stack. + * + * In flow style, print the closing bracket. + * + * In block style, check if we are still in the + * isl_yaml_sequence_first_start state. If so, we have not printed + * anything yet, so print "[]" or " []" to indicate an empty sequence. + * We print the extra space when we instructed enter_state not + * to print a space at the end of the line. + * Otherwise, undo the increase in indentation performed by + * enter_state when moving away from the isl_yaml_sequence_first_start + * state. + * If this is the outer sequence then print a newline. + */ +__isl_give isl_printer *isl_printer_yaml_end_sequence( + __isl_take isl_printer *p) +{ + enum isl_yaml_state state, up; + + state = current_state(p); + p = pop_state(p); + if (!p) + return NULL; + if (p->yaml_style == ISL_YAML_STYLE_FLOW) + return p->ops->print_str(p, " ]"); + up = current_state(p); + if (state == isl_yaml_sequence_first_start) { + if (up == isl_yaml_mapping_val) + p = p->ops->print_str(p, " []"); + else + p = p->ops->print_str(p, "[]"); + } else { + p = isl_printer_indent(p, -2); + } + if (!p) + return NULL; + state = current_state(p); + if (state == isl_yaml_none) + p = p->ops->end_line(p); + return p; +} + +/* Mark the fact that the current element is finished and that + * the next output belongs to the next element. + * In particular, if we are printing a key, then prepare for + * printing the subsequent value. If we are printing a value, + * prepare for printing the next key. If we are printing an + * element in a sequence, prepare for printing the next element. + */ +__isl_give isl_printer *isl_printer_yaml_next(__isl_take isl_printer *p) +{ + enum isl_yaml_state state; + + if (!p) + return NULL; + if (p->yaml_depth < 1) + isl_die(isl_printer_get_ctx(p), isl_error_invalid, + "not in YAML construct", return isl_printer_free(p)); + + state = current_state(p); + if (state == isl_yaml_mapping_key) + state = isl_yaml_mapping_val_start; + else if (state == isl_yaml_mapping_val) + state = isl_yaml_mapping_key_start; + else if (state == isl_yaml_sequence) + state = isl_yaml_sequence_start; + p = update_state(p, state); + + return p; +} Index: lib/Analysis/isl/isl_printer_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_printer_private.h @@ -0,0 +1,52 @@ +#ifndef ISL_PRINTER_PRIVATE_H +#define ISL_PRINTER_PRIVATE_H + +#include +#include +#include + +struct isl_printer_ops; + +/* A printer to a file or a string. + * + * "dump" is set if the printing is performed from an isl_*_dump function. + * + * yaml_style is the YAML style in which the next elements should + * be printed and may be either ISL_YAML_STYLE_BLOCK or ISL_YAML_STYLE_FLOW, + * with ISL_YAML_STYLE_FLOW being the default. + * yaml_state keeps track of the currently active YAML elements. + * yaml_size is the size of this arrays, while yaml_depth + * is the number of elements currently in use. + * yaml_state may be NULL if no YAML printing is being performed. + * + * notes keeps track of arbitrary notes as a mapping between + * name identifiers and note identifiers. It may be NULL + * if there are no notes yet. + */ +struct isl_printer { + struct isl_ctx *ctx; + struct isl_printer_ops *ops; + FILE *file; + int buf_n; + int buf_size; + char *buf; + int indent; + int output_format; + int dump; + char *indent_prefix; + char *prefix; + char *suffix; + int width; + + int yaml_style; + int yaml_depth; + int yaml_size; + enum isl_yaml_state *yaml_state; + + isl_id_to_id *notes; +}; + +__isl_give isl_printer *isl_printer_set_dump(__isl_take isl_printer *p, + int dump); + +#endif Index: lib/Analysis/isl/isl_pw_hash.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_pw_hash.c @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege + */ + +#include +#include + +/* Return a hash value that digests "pw". + */ +uint32_t FN(PW,get_hash)(__isl_keep PW *pw) +{ + int i; + uint32_t hash; + + if (!pw) + return 0; + + hash = isl_hash_init(); + for (i = 0; i < pw->n; ++i) { + uint32_t set_hash, el_hash; + + set_hash = isl_set_get_hash(pw->p[i].set); + isl_hash_hash(hash, set_hash); + el_hash = FN(EL,get_hash)(pw->p[i].FIELD); + isl_hash_hash(hash, el_hash); + } + + return hash; +} Index: lib/Analysis/isl/isl_pw_macro.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_pw_macro.h @@ -0,0 +1,4 @@ +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) +#define xS(TYPE,NAME) struct TYPE ## _ ## NAME +#define S(TYPE,NAME) xS(TYPE,NAME) Index: lib/Analysis/isl/isl_pw_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_pw_templ.c @@ -0,0 +1,2160 @@ +/* + * Copyright 2010-2011 INRIA Saclay + * Copyright 2011 Sven Verdoolaege + * Copyright 2012-2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include + +#include + +#ifdef HAS_TYPE +__isl_give PW *FN(PW,alloc_size)(__isl_take isl_space *dim, + enum isl_fold type, int n) +#else +__isl_give PW *FN(PW,alloc_size)(__isl_take isl_space *dim, int n) +#endif +{ + isl_ctx *ctx; + struct PW *pw; + + if (!dim) + return NULL; + ctx = isl_space_get_ctx(dim); + isl_assert(ctx, n >= 0, goto error); + pw = isl_alloc(ctx, struct PW, + sizeof(struct PW) + (n - 1) * sizeof(S(PW,piece))); + if (!pw) + goto error; + + pw->ref = 1; +#ifdef HAS_TYPE + pw->type = type; +#endif + pw->size = n; + pw->n = 0; + pw->dim = dim; + return pw; +error: + isl_space_free(dim); + return NULL; +} + +#ifdef HAS_TYPE +__isl_give PW *FN(PW,ZERO)(__isl_take isl_space *dim, enum isl_fold type) +{ + return FN(PW,alloc_size)(dim, type, 0); +} +#else +__isl_give PW *FN(PW,ZERO)(__isl_take isl_space *dim) +{ + return FN(PW,alloc_size)(dim, 0); +} +#endif + +__isl_give PW *FN(PW,add_piece)(__isl_take PW *pw, + __isl_take isl_set *set, __isl_take EL *el) +{ + isl_ctx *ctx; + isl_space *el_dim = NULL; + + if (!pw || !set || !el) + goto error; + + if (isl_set_plain_is_empty(set) || FN(EL,EL_IS_ZERO)(el)) { + isl_set_free(set); + FN(EL,free)(el); + return pw; + } + + ctx = isl_set_get_ctx(set); +#ifdef HAS_TYPE + if (pw->type != el->type) + isl_die(ctx, isl_error_invalid, "fold types don't match", + goto error); +#endif + el_dim = FN(EL,get_space(el)); + isl_assert(ctx, isl_space_is_equal(pw->dim, el_dim), goto error); + isl_assert(ctx, pw->n < pw->size, goto error); + + pw->p[pw->n].set = set; + pw->p[pw->n].FIELD = el; + pw->n++; + + isl_space_free(el_dim); + return pw; +error: + isl_space_free(el_dim); + FN(PW,free)(pw); + isl_set_free(set); + FN(EL,free)(el); + return NULL; +} + +#ifdef HAS_TYPE +__isl_give PW *FN(PW,alloc)(enum isl_fold type, + __isl_take isl_set *set, __isl_take EL *el) +#else +__isl_give PW *FN(PW,alloc)(__isl_take isl_set *set, __isl_take EL *el) +#endif +{ + PW *pw; + + if (!set || !el) + goto error; + +#ifdef HAS_TYPE + pw = FN(PW,alloc_size)(FN(EL,get_space)(el), type, 1); +#else + pw = FN(PW,alloc_size)(FN(EL,get_space)(el), 1); +#endif + + return FN(PW,add_piece)(pw, set, el); +error: + isl_set_free(set); + FN(EL,free)(el); + return NULL; +} + +__isl_give PW *FN(PW,dup)(__isl_keep PW *pw) +{ + int i; + PW *dup; + + if (!pw) + return NULL; + +#ifdef HAS_TYPE + dup = FN(PW,alloc_size)(isl_space_copy(pw->dim), pw->type, pw->n); +#else + dup = FN(PW,alloc_size)(isl_space_copy(pw->dim), pw->n); +#endif + if (!dup) + return NULL; + + for (i = 0; i < pw->n; ++i) + dup = FN(PW,add_piece)(dup, isl_set_copy(pw->p[i].set), + FN(EL,copy)(pw->p[i].FIELD)); + + return dup; +} + +__isl_give PW *FN(PW,cow)(__isl_take PW *pw) +{ + if (!pw) + return NULL; + + if (pw->ref == 1) + return pw; + pw->ref--; + return FN(PW,dup)(pw); +} + +__isl_give PW *FN(PW,copy)(__isl_keep PW *pw) +{ + if (!pw) + return NULL; + + pw->ref++; + return pw; +} + +__isl_null PW *FN(PW,free)(__isl_take PW *pw) +{ + int i; + + if (!pw) + return NULL; + if (--pw->ref > 0) + return NULL; + + for (i = 0; i < pw->n; ++i) { + isl_set_free(pw->p[i].set); + FN(EL,free)(pw->p[i].FIELD); + } + isl_space_free(pw->dim); + free(pw); + + return NULL; +} + +const char *FN(PW,get_dim_name)(__isl_keep PW *pw, enum isl_dim_type type, + unsigned pos) +{ + return pw ? isl_space_get_dim_name(pw->dim, type, pos) : NULL; +} + +isl_bool FN(PW,has_dim_id)(__isl_keep PW *pw, enum isl_dim_type type, + unsigned pos) +{ + return pw ? isl_space_has_dim_id(pw->dim, type, pos) : isl_bool_error; +} + +__isl_give isl_id *FN(PW,get_dim_id)(__isl_keep PW *pw, enum isl_dim_type type, + unsigned pos) +{ + return pw ? isl_space_get_dim_id(pw->dim, type, pos) : NULL; +} + +isl_bool FN(PW,has_tuple_name)(__isl_keep PW *pw, enum isl_dim_type type) +{ + return pw ? isl_space_has_tuple_name(pw->dim, type) : isl_bool_error; +} + +const char *FN(PW,get_tuple_name)(__isl_keep PW *pw, enum isl_dim_type type) +{ + return pw ? isl_space_get_tuple_name(pw->dim, type) : NULL; +} + +isl_bool FN(PW,has_tuple_id)(__isl_keep PW *pw, enum isl_dim_type type) +{ + return pw ? isl_space_has_tuple_id(pw->dim, type) : isl_bool_error; +} + +__isl_give isl_id *FN(PW,get_tuple_id)(__isl_keep PW *pw, enum isl_dim_type type) +{ + return pw ? isl_space_get_tuple_id(pw->dim, type) : NULL; +} + +isl_bool FN(PW,IS_ZERO)(__isl_keep PW *pw) +{ + if (!pw) + return isl_bool_error; + + return pw->n == 0; +} + +#ifndef NO_REALIGN +__isl_give PW *FN(PW,realign_domain)(__isl_take PW *pw, + __isl_take isl_reordering *exp) +{ + int i; + + pw = FN(PW,cow)(pw); + if (!pw || !exp) + goto error; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_realign(pw->p[i].set, + isl_reordering_copy(exp)); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,realign_domain)(pw->p[i].FIELD, + isl_reordering_copy(exp)); + if (!pw->p[i].FIELD) + goto error; + } + + pw = FN(PW,reset_domain_space)(pw, isl_space_copy(exp->dim)); + + isl_reordering_free(exp); + return pw; +error: + isl_reordering_free(exp); + FN(PW,free)(pw); + return NULL; +} + +/* Align the parameters of "pw" to those of "model". + */ +__isl_give PW *FN(PW,align_params)(__isl_take PW *pw, __isl_take isl_space *model) +{ + isl_ctx *ctx; + + if (!pw || !model) + goto error; + + ctx = isl_space_get_ctx(model); + if (!isl_space_has_named_params(model)) + isl_die(ctx, isl_error_invalid, + "model has unnamed parameters", goto error); + if (!isl_space_has_named_params(pw->dim)) + isl_die(ctx, isl_error_invalid, + "input has unnamed parameters", goto error); + if (!isl_space_match(pw->dim, isl_dim_param, model, isl_dim_param)) { + isl_reordering *exp; + + model = isl_space_drop_dims(model, isl_dim_in, + 0, isl_space_dim(model, isl_dim_in)); + model = isl_space_drop_dims(model, isl_dim_out, + 0, isl_space_dim(model, isl_dim_out)); + exp = isl_parameter_alignment_reordering(pw->dim, model); + exp = isl_reordering_extend_space(exp, + FN(PW,get_domain_space)(pw)); + pw = FN(PW,realign_domain)(pw, exp); + } + + isl_space_free(model); + return pw; +error: + isl_space_free(model); + FN(PW,free)(pw); + return NULL; +} + +static __isl_give PW *FN(PW,align_params_pw_pw_and)(__isl_take PW *pw1, + __isl_take PW *pw2, + __isl_give PW *(*fn)(__isl_take PW *pw1, __isl_take PW *pw2)) +{ + isl_ctx *ctx; + + if (!pw1 || !pw2) + goto error; + if (isl_space_match(pw1->dim, isl_dim_param, pw2->dim, isl_dim_param)) + return fn(pw1, pw2); + ctx = FN(PW,get_ctx)(pw1); + if (!isl_space_has_named_params(pw1->dim) || + !isl_space_has_named_params(pw2->dim)) + isl_die(ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + pw1 = FN(PW,align_params)(pw1, FN(PW,get_space)(pw2)); + pw2 = FN(PW,align_params)(pw2, FN(PW,get_space)(pw1)); + return fn(pw1, pw2); +error: + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return NULL; +} + +static __isl_give PW *FN(PW,align_params_pw_set_and)(__isl_take PW *pw, + __isl_take isl_set *set, + __isl_give PW *(*fn)(__isl_take PW *pw, __isl_take isl_set *set)) +{ + isl_ctx *ctx; + + if (!pw || !set) + goto error; + if (isl_space_match(pw->dim, isl_dim_param, set->dim, isl_dim_param)) + return fn(pw, set); + ctx = FN(PW,get_ctx)(pw); + if (!isl_space_has_named_params(pw->dim) || + !isl_space_has_named_params(set->dim)) + isl_die(ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + pw = FN(PW,align_params)(pw, isl_set_get_space(set)); + set = isl_set_align_params(set, FN(PW,get_space)(pw)); + return fn(pw, set); +error: + FN(PW,free)(pw); + isl_set_free(set); + return NULL; +} +#endif + +static __isl_give PW *FN(PW,union_add_aligned)(__isl_take PW *pw1, + __isl_take PW *pw2) +{ + int i, j, n; + struct PW *res; + isl_ctx *ctx; + isl_set *set; + + if (!pw1 || !pw2) + goto error; + + ctx = isl_space_get_ctx(pw1->dim); +#ifdef HAS_TYPE + if (pw1->type != pw2->type) + isl_die(ctx, isl_error_invalid, + "fold types don't match", goto error); +#endif + isl_assert(ctx, isl_space_is_equal(pw1->dim, pw2->dim), goto error); + + if (FN(PW,IS_ZERO)(pw1)) { + FN(PW,free)(pw1); + return pw2; + } + + if (FN(PW,IS_ZERO)(pw2)) { + FN(PW,free)(pw2); + return pw1; + } + + n = (pw1->n + 1) * (pw2->n + 1); +#ifdef HAS_TYPE + res = FN(PW,alloc_size)(isl_space_copy(pw1->dim), pw1->type, n); +#else + res = FN(PW,alloc_size)(isl_space_copy(pw1->dim), n); +#endif + + for (i = 0; i < pw1->n; ++i) { + set = isl_set_copy(pw1->p[i].set); + for (j = 0; j < pw2->n; ++j) { + struct isl_set *common; + EL *sum; + common = isl_set_intersect(isl_set_copy(pw1->p[i].set), + isl_set_copy(pw2->p[j].set)); + if (isl_set_plain_is_empty(common)) { + isl_set_free(common); + continue; + } + set = isl_set_subtract(set, + isl_set_copy(pw2->p[j].set)); + + sum = FN(EL,add_on_domain)(common, + FN(EL,copy)(pw1->p[i].FIELD), + FN(EL,copy)(pw2->p[j].FIELD)); + + res = FN(PW,add_piece)(res, common, sum); + } + res = FN(PW,add_piece)(res, set, FN(EL,copy)(pw1->p[i].FIELD)); + } + + for (j = 0; j < pw2->n; ++j) { + set = isl_set_copy(pw2->p[j].set); + for (i = 0; i < pw1->n; ++i) + set = isl_set_subtract(set, + isl_set_copy(pw1->p[i].set)); + res = FN(PW,add_piece)(res, set, FN(EL,copy)(pw2->p[j].FIELD)); + } + + FN(PW,free)(pw1); + FN(PW,free)(pw2); + + return res; +error: + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return NULL; +} + +/* Private version of "union_add". For isl_pw_qpolynomial and + * isl_pw_qpolynomial_fold, we prefer to simply call it "add". + */ +static __isl_give PW *FN(PW,union_add_)(__isl_take PW *pw1, __isl_take PW *pw2) +{ + return FN(PW,align_params_pw_pw_and)(pw1, pw2, + &FN(PW,union_add_aligned)); +} + +/* Make sure "pw" has room for at least "n" more pieces. + * + * If there is only one reference to pw, we extend it in place. + * Otherwise, we create a new PW and copy the pieces. + */ +static __isl_give PW *FN(PW,grow)(__isl_take PW *pw, int n) +{ + int i; + isl_ctx *ctx; + PW *res; + + if (!pw) + return NULL; + if (pw->n + n <= pw->size) + return pw; + ctx = FN(PW,get_ctx)(pw); + n += pw->n; + if (pw->ref == 1) { + res = isl_realloc(ctx, pw, struct PW, + sizeof(struct PW) + (n - 1) * sizeof(S(PW,piece))); + if (!res) + return FN(PW,free)(pw); + res->size = n; + return res; + } +#ifdef HAS_TYPE + res = FN(PW,alloc_size)(isl_space_copy(pw->dim), pw->type, n); +#else + res = FN(PW,alloc_size)(isl_space_copy(pw->dim), n); +#endif + if (!res) + return FN(PW,free)(pw); + for (i = 0; i < pw->n; ++i) + res = FN(PW,add_piece)(res, isl_set_copy(pw->p[i].set), + FN(EL,copy)(pw->p[i].FIELD)); + FN(PW,free)(pw); + return res; +} + +static __isl_give PW *FN(PW,add_disjoint_aligned)(__isl_take PW *pw1, + __isl_take PW *pw2) +{ + int i; + isl_ctx *ctx; + + if (!pw1 || !pw2) + goto error; + + if (pw1->size < pw1->n + pw2->n && pw1->n < pw2->n) + return FN(PW,add_disjoint_aligned)(pw2, pw1); + + ctx = isl_space_get_ctx(pw1->dim); +#ifdef HAS_TYPE + if (pw1->type != pw2->type) + isl_die(ctx, isl_error_invalid, + "fold types don't match", goto error); +#endif + isl_assert(ctx, isl_space_is_equal(pw1->dim, pw2->dim), goto error); + + if (FN(PW,IS_ZERO)(pw1)) { + FN(PW,free)(pw1); + return pw2; + } + + if (FN(PW,IS_ZERO)(pw2)) { + FN(PW,free)(pw2); + return pw1; + } + + pw1 = FN(PW,grow)(pw1, pw2->n); + if (!pw1) + goto error; + + for (i = 0; i < pw2->n; ++i) + pw1 = FN(PW,add_piece)(pw1, + isl_set_copy(pw2->p[i].set), + FN(EL,copy)(pw2->p[i].FIELD)); + + FN(PW,free)(pw2); + + return pw1; +error: + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return NULL; +} + +__isl_give PW *FN(PW,add_disjoint)(__isl_take PW *pw1, __isl_take PW *pw2) +{ + return FN(PW,align_params_pw_pw_and)(pw1, pw2, + &FN(PW,add_disjoint_aligned)); +} + +/* This function is currently only used from isl_aff.c + */ +static __isl_give PW *FN(PW,on_shared_domain_in)(__isl_take PW *pw1, + __isl_take PW *pw2, __isl_take isl_space *space, + __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2)) + __attribute__ ((unused)); + +/* Apply "fn" to pairs of elements from pw1 and pw2 on shared domains. + * The result of "fn" (and therefore also of this function) lives in "space". + */ +static __isl_give PW *FN(PW,on_shared_domain_in)(__isl_take PW *pw1, + __isl_take PW *pw2, __isl_take isl_space *space, + __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2)) +{ + int i, j, n; + PW *res = NULL; + + if (!pw1 || !pw2) + goto error; + + n = pw1->n * pw2->n; +#ifdef HAS_TYPE + res = FN(PW,alloc_size)(isl_space_copy(space), pw1->type, n); +#else + res = FN(PW,alloc_size)(isl_space_copy(space), n); +#endif + + for (i = 0; i < pw1->n; ++i) { + for (j = 0; j < pw2->n; ++j) { + isl_set *common; + EL *res_ij; + int empty; + + common = isl_set_intersect( + isl_set_copy(pw1->p[i].set), + isl_set_copy(pw2->p[j].set)); + empty = isl_set_plain_is_empty(common); + if (empty < 0 || empty) { + isl_set_free(common); + if (empty < 0) + goto error; + continue; + } + + res_ij = fn(FN(EL,copy)(pw1->p[i].FIELD), + FN(EL,copy)(pw2->p[j].FIELD)); + res_ij = FN(EL,gist)(res_ij, isl_set_copy(common)); + + res = FN(PW,add_piece)(res, common, res_ij); + } + } + + isl_space_free(space); + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return res; +error: + isl_space_free(space); + FN(PW,free)(pw1); + FN(PW,free)(pw2); + FN(PW,free)(res); + return NULL; +} + +/* This function is currently only used from isl_aff.c + */ +static __isl_give PW *FN(PW,on_shared_domain)(__isl_take PW *pw1, + __isl_take PW *pw2, + __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2)) + __attribute__ ((unused)); + +/* Apply "fn" to pairs of elements from pw1 and pw2 on shared domains. + * The result of "fn" is assumed to live in the same space as "pw1" and "pw2". + */ +static __isl_give PW *FN(PW,on_shared_domain)(__isl_take PW *pw1, + __isl_take PW *pw2, + __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2)) +{ + isl_space *space; + + if (!pw1 || !pw2) + goto error; + + space = isl_space_copy(pw1->dim); + return FN(PW,on_shared_domain_in)(pw1, pw2, space, fn); +error: + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return NULL; +} + +#ifndef NO_NEG +__isl_give PW *FN(PW,neg)(__isl_take PW *pw) +{ + int i; + + if (!pw) + return NULL; + + if (FN(PW,IS_ZERO)(pw)) + return pw; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].FIELD = FN(EL,neg)(pw->p[i].FIELD); + if (!pw->p[i].FIELD) + return FN(PW,free)(pw); + } + + return pw; +} +#endif + +#ifndef NO_SUB +__isl_give PW *FN(PW,sub)(__isl_take PW *pw1, __isl_take PW *pw2) +{ + return FN(PW,add)(pw1, FN(PW,neg)(pw2)); +} +#endif + +#ifndef NO_EVAL +__isl_give isl_val *FN(PW,eval)(__isl_take PW *pw, __isl_take isl_point *pnt) +{ + int i; + int found = 0; + isl_ctx *ctx; + isl_space *pnt_dim = NULL; + isl_val *v; + + if (!pw || !pnt) + goto error; + ctx = isl_point_get_ctx(pnt); + pnt_dim = isl_point_get_space(pnt); + isl_assert(ctx, isl_space_is_domain_internal(pnt_dim, pw->dim), + goto error); + + for (i = 0; i < pw->n; ++i) { + found = isl_set_contains_point(pw->p[i].set, pnt); + if (found < 0) + goto error; + if (found) + break; + } + if (found) + v = FN(EL,eval)(FN(EL,copy)(pw->p[i].FIELD), + isl_point_copy(pnt)); + else + v = isl_val_zero(ctx); + FN(PW,free)(pw); + isl_space_free(pnt_dim); + isl_point_free(pnt); + return v; +error: + FN(PW,free)(pw); + isl_space_free(pnt_dim); + isl_point_free(pnt); + return NULL; +} +#endif + +/* Return the parameter domain of "pw". + */ +__isl_give isl_set *FN(PW,params)(__isl_take PW *pw) +{ + return isl_set_params(FN(PW,domain)(pw)); +} + +__isl_give isl_set *FN(PW,domain)(__isl_take PW *pw) +{ + int i; + isl_set *dom; + + if (!pw) + return NULL; + + dom = isl_set_empty(FN(PW,get_domain_space)(pw)); + for (i = 0; i < pw->n; ++i) + dom = isl_set_union_disjoint(dom, isl_set_copy(pw->p[i].set)); + + FN(PW,free)(pw); + + return dom; +} + +/* Exploit the equalities in the domain of piece "i" of "pw" + * to simplify the associated function. + * If the domain of piece "i" is empty, then remove it entirely, + * replacing it with the final piece. + */ +static int FN(PW,exploit_equalities_and_remove_if_empty)(__isl_keep PW *pw, + int i) +{ + isl_basic_set *aff; + int empty = isl_set_plain_is_empty(pw->p[i].set); + + if (empty < 0) + return -1; + if (empty) { + isl_set_free(pw->p[i].set); + FN(EL,free)(pw->p[i].FIELD); + if (i != pw->n - 1) + pw->p[i] = pw->p[pw->n - 1]; + pw->n--; + + return 0; + } + + aff = isl_set_affine_hull(isl_set_copy(pw->p[i].set)); + pw->p[i].FIELD = FN(EL,substitute_equalities)(pw->p[i].FIELD, aff); + if (!pw->p[i].FIELD) + return -1; + + return 0; +} + +/* Convert a piecewise expression defined over a parameter domain + * into one that is defined over a zero-dimensional set. + */ +__isl_give PW *FN(PW,from_range)(__isl_take PW *pw) +{ + isl_space *space; + + if (!pw) + return NULL; + if (!isl_space_is_set(pw->dim)) + isl_die(FN(PW,get_ctx)(pw), isl_error_invalid, + "not living in a set space", return FN(PW,free)(pw)); + + space = FN(PW,get_space)(pw); + space = isl_space_from_range(space); + pw = FN(PW,reset_space)(pw, space); + + return pw; +} + +/* Fix the value of the given parameter or domain dimension of "pw" + * to be equal to "value". + */ +__isl_give PW *FN(PW,fix_si)(__isl_take PW *pw, enum isl_dim_type type, + unsigned pos, int value) +{ + int i; + + if (!pw) + return NULL; + + if (type == isl_dim_out) + isl_die(FN(PW,get_ctx)(pw), isl_error_invalid, + "cannot fix output dimension", return FN(PW,free)(pw)); + + if (pw->n == 0) + return pw; + + if (type == isl_dim_in) + type = isl_dim_set; + + pw = FN(PW,cow)(pw); + if (!pw) + return FN(PW,free)(pw); + + for (i = pw->n - 1; i >= 0; --i) { + pw->p[i].set = isl_set_fix_si(pw->p[i].set, type, pos, value); + if (FN(PW,exploit_equalities_and_remove_if_empty)(pw, i) < 0) + return FN(PW,free)(pw); + } + + return pw; +} + +/* Restrict the domain of "pw" by combining each cell + * with "set" through a call to "fn", where "fn" may be + * isl_set_intersect, isl_set_intersect_params or isl_set_subtract. + */ +static __isl_give PW *FN(PW,restrict_domain_aligned)(__isl_take PW *pw, + __isl_take isl_set *set, + __isl_give isl_set *(*fn)(__isl_take isl_set *set1, + __isl_take isl_set *set2)) +{ + int i; + + if (!pw || !set) + goto error; + + if (pw->n == 0) { + isl_set_free(set); + return pw; + } + + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + + for (i = pw->n - 1; i >= 0; --i) { + pw->p[i].set = fn(pw->p[i].set, isl_set_copy(set)); + if (FN(PW,exploit_equalities_and_remove_if_empty)(pw, i) < 0) + goto error; + } + + isl_set_free(set); + return pw; +error: + isl_set_free(set); + FN(PW,free)(pw); + return NULL; +} + +static __isl_give PW *FN(PW,intersect_domain_aligned)(__isl_take PW *pw, + __isl_take isl_set *set) +{ + return FN(PW,restrict_domain_aligned)(pw, set, &isl_set_intersect); +} + +__isl_give PW *FN(PW,intersect_domain)(__isl_take PW *pw, + __isl_take isl_set *context) +{ + return FN(PW,align_params_pw_set_and)(pw, context, + &FN(PW,intersect_domain_aligned)); +} + +static __isl_give PW *FN(PW,intersect_params_aligned)(__isl_take PW *pw, + __isl_take isl_set *set) +{ + return FN(PW,restrict_domain_aligned)(pw, set, + &isl_set_intersect_params); +} + +/* Intersect the domain of "pw" with the parameter domain "context". + */ +__isl_give PW *FN(PW,intersect_params)(__isl_take PW *pw, + __isl_take isl_set *context) +{ + return FN(PW,align_params_pw_set_and)(pw, context, + &FN(PW,intersect_params_aligned)); +} + +/* Subtract "domain' from the domain of "pw", assuming their + * parameters have been aligned. + */ +static __isl_give PW *FN(PW,subtract_domain_aligned)(__isl_take PW *pw, + __isl_take isl_set *domain) +{ + return FN(PW,restrict_domain_aligned)(pw, domain, &isl_set_subtract); +} + +/* Subtract "domain' from the domain of "pw". + */ +__isl_give PW *FN(PW,subtract_domain)(__isl_take PW *pw, + __isl_take isl_set *domain) +{ + return FN(PW,align_params_pw_set_and)(pw, domain, + &FN(PW,subtract_domain_aligned)); +} + +/* Compute the gist of "pw" with respect to the domain constraints + * of "context" for the case where the domain of the last element + * of "pw" is equal to "context". + * Call "fn_el" to compute the gist of this element, replace + * its domain by the universe and drop all other elements + * as their domains are necessarily disjoint from "context". + */ +static __isl_give PW *FN(PW,gist_last)(__isl_take PW *pw, + __isl_take isl_set *context, + __isl_give EL *(*fn_el)(__isl_take EL *el, __isl_take isl_set *set)) +{ + int i; + isl_space *space; + + for (i = 0; i < pw->n - 1; ++i) { + isl_set_free(pw->p[i].set); + FN(EL,free)(pw->p[i].FIELD); + } + pw->p[0].FIELD = pw->p[pw->n - 1].FIELD; + pw->p[0].set = pw->p[pw->n - 1].set; + pw->n = 1; + + space = isl_set_get_space(context); + pw->p[0].FIELD = fn_el(pw->p[0].FIELD, context); + context = isl_set_universe(space); + isl_set_free(pw->p[0].set); + pw->p[0].set = context; + + if (!pw->p[0].FIELD || !pw->p[0].set) + return FN(PW,free)(pw); + + return pw; +} + +/* Compute the gist of "pw" with respect to the domain constraints + * of "context". Call "fn_el" to compute the gist of the elements + * and "fn_dom" to compute the gist of the domains. + * + * If the piecewise expression is empty or the context is the universe, + * then nothing can be simplified. + */ +static __isl_give PW *FN(PW,gist_aligned)(__isl_take PW *pw, + __isl_take isl_set *context, + __isl_give EL *(*fn_el)(__isl_take EL *el, + __isl_take isl_set *set), + __isl_give isl_set *(*fn_dom)(__isl_take isl_set *set, + __isl_take isl_basic_set *bset)) +{ + int i; + int is_universe; + isl_basic_set *hull = NULL; + + if (!pw || !context) + goto error; + + if (pw->n == 0) { + isl_set_free(context); + return pw; + } + + is_universe = isl_set_plain_is_universe(context); + if (is_universe < 0) + goto error; + if (is_universe) { + isl_set_free(context); + return pw; + } + + if (!isl_space_match(pw->dim, isl_dim_param, + context->dim, isl_dim_param)) { + pw = FN(PW,align_params)(pw, isl_set_get_space(context)); + context = isl_set_align_params(context, FN(PW,get_space)(pw)); + } + + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + + if (pw->n == 1) { + int equal; + + equal = isl_set_plain_is_equal(pw->p[0].set, context); + if (equal < 0) + goto error; + if (equal) + return FN(PW,gist_last)(pw, context, fn_el); + } + + context = isl_set_compute_divs(context); + hull = isl_set_simple_hull(isl_set_copy(context)); + + for (i = pw->n - 1; i >= 0; --i) { + isl_set *set_i; + int empty; + + if (i == pw->n - 1) { + int equal; + equal = isl_set_plain_is_equal(pw->p[i].set, context); + if (equal < 0) + goto error; + if (equal) { + isl_basic_set_free(hull); + return FN(PW,gist_last)(pw, context, fn_el); + } + } + set_i = isl_set_intersect(isl_set_copy(pw->p[i].set), + isl_set_copy(context)); + empty = isl_set_plain_is_empty(set_i); + pw->p[i].FIELD = fn_el(pw->p[i].FIELD, set_i); + pw->p[i].set = fn_dom(pw->p[i].set, isl_basic_set_copy(hull)); + if (empty < 0 || !pw->p[i].FIELD || !pw->p[i].set) + goto error; + if (empty) { + isl_set_free(pw->p[i].set); + FN(EL,free)(pw->p[i].FIELD); + if (i != pw->n - 1) + pw->p[i] = pw->p[pw->n - 1]; + pw->n--; + } + } + + isl_basic_set_free(hull); + isl_set_free(context); + + return pw; +error: + FN(PW,free)(pw); + isl_basic_set_free(hull); + isl_set_free(context); + return NULL; +} + +static __isl_give PW *FN(PW,gist_domain_aligned)(__isl_take PW *pw, + __isl_take isl_set *set) +{ + return FN(PW,gist_aligned)(pw, set, &FN(EL,gist), + &isl_set_gist_basic_set); +} + +__isl_give PW *FN(PW,gist)(__isl_take PW *pw, __isl_take isl_set *context) +{ + return FN(PW,align_params_pw_set_and)(pw, context, + &FN(PW,gist_domain_aligned)); +} + +static __isl_give PW *FN(PW,gist_params_aligned)(__isl_take PW *pw, + __isl_take isl_set *set) +{ + return FN(PW,gist_aligned)(pw, set, &FN(EL,gist_params), + &isl_set_gist_params_basic_set); +} + +__isl_give PW *FN(PW,gist_params)(__isl_take PW *pw, + __isl_take isl_set *context) +{ + return FN(PW,align_params_pw_set_and)(pw, context, + &FN(PW,gist_params_aligned)); +} + +/* Return -1 if the piece "p1" should be sorted before "p2" + * and 1 if it should be sorted after "p2". + * Return 0 if they do not need to be sorted in a specific order. + * + * The two pieces are compared on the basis of their function value expressions. + */ +static int FN(PW,sort_field_cmp)(const void *p1, const void *p2, void *arg) +{ + struct FN(PW,piece) const *pc1 = p1; + struct FN(PW,piece) const *pc2 = p2; + + return FN(EL,plain_cmp)(pc1->FIELD, pc2->FIELD); +} + +/* Sort the pieces of "pw" according to their function value + * expressions and then combine pairs of adjacent pieces with + * the same such expression. + * + * The sorting is performed in place because it does not + * change the meaning of "pw", but care needs to be + * taken not to change any possible other copies of "pw" + * in case anything goes wrong. + */ +__isl_give PW *FN(PW,sort)(__isl_take PW *pw) +{ + int i, j; + isl_set *set; + + if (!pw) + return NULL; + if (pw->n <= 1) + return pw; + if (isl_sort(pw->p, pw->n, sizeof(pw->p[0]), + &FN(PW,sort_field_cmp), NULL) < 0) + return FN(PW,free)(pw); + for (i = pw->n - 1; i >= 1; --i) { + if (!FN(EL,plain_is_equal)(pw->p[i - 1].FIELD, pw->p[i].FIELD)) + continue; + set = isl_set_union(isl_set_copy(pw->p[i - 1].set), + isl_set_copy(pw->p[i].set)); + if (!set) + return FN(PW,free)(pw); + isl_set_free(pw->p[i].set); + FN(EL,free)(pw->p[i].FIELD); + isl_set_free(pw->p[i - 1].set); + pw->p[i - 1].set = set; + for (j = i + 1; j < pw->n; ++j) + pw->p[j - 1] = pw->p[j]; + pw->n--; + } + + return pw; +} + +/* Coalesce the domains of "pw". + * + * Prior to the actual coalescing, first sort the pieces such that + * pieces with the same function value expression are combined + * into a single piece, the combined domain of which can then + * be coalesced. + */ +__isl_give PW *FN(PW,coalesce)(__isl_take PW *pw) +{ + int i; + + pw = FN(PW,sort)(pw); + if (!pw) + return NULL; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_coalesce(pw->p[i].set); + if (!pw->p[i].set) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} + +isl_ctx *FN(PW,get_ctx)(__isl_keep PW *pw) +{ + return pw ? isl_space_get_ctx(pw->dim) : NULL; +} + +#ifndef NO_INVOLVES_DIMS +isl_bool FN(PW,involves_dims)(__isl_keep PW *pw, enum isl_dim_type type, + unsigned first, unsigned n) +{ + int i; + enum isl_dim_type set_type; + + if (!pw) + return isl_bool_error; + if (pw->n == 0 || n == 0) + return isl_bool_false; + + set_type = type == isl_dim_in ? isl_dim_set : type; + + for (i = 0; i < pw->n; ++i) { + isl_bool involves = FN(EL,involves_dims)(pw->p[i].FIELD, + type, first, n); + if (involves < 0 || involves) + return involves; + involves = isl_set_involves_dims(pw->p[i].set, + set_type, first, n); + if (involves < 0 || involves) + return involves; + } + return isl_bool_false; +} +#endif + +__isl_give PW *FN(PW,set_dim_name)(__isl_take PW *pw, + enum isl_dim_type type, unsigned pos, const char *s) +{ + int i; + enum isl_dim_type set_type; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + + set_type = type == isl_dim_in ? isl_dim_set : type; + + pw->dim = isl_space_set_dim_name(pw->dim, type, pos, s); + if (!pw->dim) + goto error; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_set_dim_name(pw->p[i].set, + set_type, pos, s); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,set_dim_name)(pw->p[i].FIELD, type, pos, s); + if (!pw->p[i].FIELD) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} + +#ifndef NO_DROP_DIMS +__isl_give PW *FN(PW,drop_dims)(__isl_take PW *pw, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + enum isl_dim_type set_type; + + if (!pw) + return NULL; + if (n == 0 && !isl_space_get_tuple_name(pw->dim, type)) + return pw; + + set_type = type == isl_dim_in ? isl_dim_set : type; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + pw->dim = isl_space_drop_dims(pw->dim, type, first, n); + if (!pw->dim) + goto error; + for (i = 0; i < pw->n; ++i) { + pw->p[i].FIELD = FN(EL,drop_dims)(pw->p[i].FIELD, type, first, n); + if (!pw->p[i].FIELD) + goto error; + if (type == isl_dim_out) + continue; + pw->p[i].set = isl_set_drop(pw->p[i].set, set_type, first, n); + if (!pw->p[i].set) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} + +/* This function is very similar to drop_dims. + * The only difference is that the cells may still involve + * the specified dimensions. They are removed using + * isl_set_project_out instead of isl_set_drop. + */ +__isl_give PW *FN(PW,project_out)(__isl_take PW *pw, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + enum isl_dim_type set_type; + + if (!pw) + return NULL; + if (n == 0 && !isl_space_get_tuple_name(pw->dim, type)) + return pw; + + set_type = type == isl_dim_in ? isl_dim_set : type; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + pw->dim = isl_space_drop_dims(pw->dim, type, first, n); + if (!pw->dim) + goto error; + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_project_out(pw->p[i].set, + set_type, first, n); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,drop_dims)(pw->p[i].FIELD, type, first, n); + if (!pw->p[i].FIELD) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} + +/* Project the domain of pw onto its parameter space. + */ +__isl_give PW *FN(PW,project_domain_on_params)(__isl_take PW *pw) +{ + isl_space *space; + unsigned n; + + n = FN(PW,dim)(pw, isl_dim_in); + pw = FN(PW,project_out)(pw, isl_dim_in, 0, n); + space = FN(PW,get_domain_space)(pw); + space = isl_space_params(space); + pw = FN(PW,reset_domain_space)(pw, space); + return pw; +} +#endif + +#ifndef NO_INSERT_DIMS +__isl_give PW *FN(PW,insert_dims)(__isl_take PW *pw, enum isl_dim_type type, + unsigned first, unsigned n) +{ + int i; + enum isl_dim_type set_type; + + if (!pw) + return NULL; + if (n == 0 && !isl_space_is_named_or_nested(pw->dim, type)) + return pw; + + set_type = type == isl_dim_in ? isl_dim_set : type; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + + pw->dim = isl_space_insert_dims(pw->dim, type, first, n); + if (!pw->dim) + goto error; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_insert_dims(pw->p[i].set, + set_type, first, n); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,insert_dims)(pw->p[i].FIELD, + type, first, n); + if (!pw->p[i].FIELD) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} +#endif + +__isl_give PW *FN(PW,fix_dim)(__isl_take PW *pw, + enum isl_dim_type type, unsigned pos, isl_int v) +{ + int i; + + if (!pw) + return NULL; + + if (type == isl_dim_in) + type = isl_dim_set; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_fix(pw->p[i].set, type, pos, v); + if (FN(PW,exploit_equalities_and_remove_if_empty)(pw, i) < 0) + return FN(PW,free)(pw); + } + + return pw; +} + +/* Fix the value of the variable at position "pos" of type "type" of "pw" + * to be equal to "v". + */ +__isl_give PW *FN(PW,fix_val)(__isl_take PW *pw, + enum isl_dim_type type, unsigned pos, __isl_take isl_val *v) +{ + if (!v) + return FN(PW,free)(pw); + if (!isl_val_is_int(v)) + isl_die(FN(PW,get_ctx)(pw), isl_error_invalid, + "expecting integer value", goto error); + + pw = FN(PW,fix_dim)(pw, type, pos, v->n); + isl_val_free(v); + + return pw; +error: + isl_val_free(v); + return FN(PW,free)(pw); +} + +unsigned FN(PW,dim)(__isl_keep PW *pw, enum isl_dim_type type) +{ + return pw ? isl_space_dim(pw->dim, type) : 0; +} + +__isl_give PW *FN(PW,split_dims)(__isl_take PW *pw, + enum isl_dim_type type, unsigned first, unsigned n) +{ + int i; + + if (!pw) + return NULL; + if (n == 0) + return pw; + + if (type == isl_dim_in) + type = isl_dim_set; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + if (!pw->dim) + goto error; + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_split_dims(pw->p[i].set, type, first, n); + if (!pw->p[i].set) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} + +#ifndef NO_OPT +/* Compute the maximal value attained by the piecewise quasipolynomial + * on its domain or zero if the domain is empty. + * In the worst case, the domain is scanned completely, + * so the domain is assumed to be bounded. + */ +__isl_give isl_val *FN(PW,opt)(__isl_take PW *pw, int max) +{ + int i; + isl_val *opt; + + if (!pw) + return NULL; + + if (pw->n == 0) { + opt = isl_val_zero(FN(PW,get_ctx)(pw)); + FN(PW,free)(pw); + return opt; + } + + opt = FN(EL,opt_on_domain)(FN(EL,copy)(pw->p[0].FIELD), + isl_set_copy(pw->p[0].set), max); + for (i = 1; i < pw->n; ++i) { + isl_val *opt_i; + opt_i = FN(EL,opt_on_domain)(FN(EL,copy)(pw->p[i].FIELD), + isl_set_copy(pw->p[i].set), max); + if (max) + opt = isl_val_max(opt, opt_i); + else + opt = isl_val_min(opt, opt_i); + } + + FN(PW,free)(pw); + return opt; +} + +__isl_give isl_val *FN(PW,max)(__isl_take PW *pw) +{ + return FN(PW,opt)(pw, 1); +} + +__isl_give isl_val *FN(PW,min)(__isl_take PW *pw) +{ + return FN(PW,opt)(pw, 0); +} +#endif + +__isl_give isl_space *FN(PW,get_space)(__isl_keep PW *pw) +{ + return pw ? isl_space_copy(pw->dim) : NULL; +} + +__isl_give isl_space *FN(PW,get_domain_space)(__isl_keep PW *pw) +{ + return pw ? isl_space_domain(isl_space_copy(pw->dim)) : NULL; +} + +/* Return the position of the dimension of the given type and name + * in "pw". + * Return -1 if no such dimension can be found. + */ +int FN(PW,find_dim_by_name)(__isl_keep PW *pw, + enum isl_dim_type type, const char *name) +{ + if (!pw) + return -1; + return isl_space_find_dim_by_name(pw->dim, type, name); +} + +#ifndef NO_RESET_DIM +/* Reset the space of "pw". Since we don't know if the elements + * represent the spaces themselves or their domains, we pass along + * both when we call their reset_space_and_domain. + */ +static __isl_give PW *FN(PW,reset_space_and_domain)(__isl_take PW *pw, + __isl_take isl_space *space, __isl_take isl_space *domain) +{ + int i; + + pw = FN(PW,cow)(pw); + if (!pw || !space || !domain) + goto error; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_reset_space(pw->p[i].set, + isl_space_copy(domain)); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,reset_space_and_domain)(pw->p[i].FIELD, + isl_space_copy(space), isl_space_copy(domain)); + if (!pw->p[i].FIELD) + goto error; + } + + isl_space_free(domain); + + isl_space_free(pw->dim); + pw->dim = space; + + return pw; +error: + isl_space_free(domain); + isl_space_free(space); + FN(PW,free)(pw); + return NULL; +} + +__isl_give PW *FN(PW,reset_domain_space)(__isl_take PW *pw, + __isl_take isl_space *domain) +{ + isl_space *space; + + space = isl_space_extend_domain_with_range(isl_space_copy(domain), + FN(PW,get_space)(pw)); + return FN(PW,reset_space_and_domain)(pw, space, domain); +} + +__isl_give PW *FN(PW,reset_space)(__isl_take PW *pw, __isl_take isl_space *dim) +{ + isl_space *domain; + + domain = isl_space_domain(isl_space_copy(dim)); + return FN(PW,reset_space_and_domain)(pw, dim, domain); +} + +__isl_give PW *FN(PW,set_tuple_id)(__isl_take PW *pw, enum isl_dim_type type, + __isl_take isl_id *id) +{ + isl_space *space; + + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + + space = FN(PW,get_space)(pw); + space = isl_space_set_tuple_id(space, type, id); + + return FN(PW,reset_space)(pw, space); +error: + isl_id_free(id); + return FN(PW,free)(pw); +} + +/* Drop the id on the specified tuple. + */ +__isl_give PW *FN(PW,reset_tuple_id)(__isl_take PW *pw, enum isl_dim_type type) +{ + isl_space *space; + + if (!pw) + return NULL; + if (!FN(PW,has_tuple_id)(pw, type)) + return pw; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + + space = FN(PW,get_space)(pw); + space = isl_space_reset_tuple_id(space, type); + + return FN(PW,reset_space)(pw, space); +} + +__isl_give PW *FN(PW,set_dim_id)(__isl_take PW *pw, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + pw->dim = isl_space_set_dim_id(pw->dim, type, pos, id); + return FN(PW,reset_space)(pw, isl_space_copy(pw->dim)); +error: + isl_id_free(id); + return FN(PW,free)(pw); +} +#endif + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the space of "pw". + */ +__isl_give PW *FN(PW,reset_user)(__isl_take PW *pw) +{ + isl_space *space; + + space = FN(PW,get_space)(pw); + space = isl_space_reset_user(space); + + return FN(PW,reset_space)(pw, space); +} + +int FN(PW,has_equal_space)(__isl_keep PW *pw1, __isl_keep PW *pw2) +{ + if (!pw1 || !pw2) + return -1; + + return isl_space_is_equal(pw1->dim, pw2->dim); +} + +#ifndef NO_MORPH +__isl_give PW *FN(PW,morph_domain)(__isl_take PW *pw, + __isl_take isl_morph *morph) +{ + int i; + isl_ctx *ctx; + + if (!pw || !morph) + goto error; + + ctx = isl_space_get_ctx(pw->dim); + isl_assert(ctx, isl_space_is_domain_internal(morph->dom->dim, pw->dim), + goto error); + + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + pw->dim = isl_space_extend_domain_with_range( + isl_space_copy(morph->ran->dim), pw->dim); + if (!pw->dim) + goto error; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_morph_set(isl_morph_copy(morph), pw->p[i].set); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,morph_domain)(pw->p[i].FIELD, + isl_morph_copy(morph)); + if (!pw->p[i].FIELD) + goto error; + } + + isl_morph_free(morph); + + return pw; +error: + FN(PW,free)(pw); + isl_morph_free(morph); + return NULL; +} +#endif + +int FN(PW,n_piece)(__isl_keep PW *pw) +{ + return pw ? pw->n : 0; +} + +isl_stat FN(PW,foreach_piece)(__isl_keep PW *pw, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take EL *el, void *user), + void *user) +{ + int i; + + if (!pw) + return isl_stat_error; + + for (i = 0; i < pw->n; ++i) + if (fn(isl_set_copy(pw->p[i].set), + FN(EL,copy)(pw->p[i].FIELD), user) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +#ifndef NO_LIFT +static int any_divs(__isl_keep isl_set *set) +{ + int i; + + if (!set) + return -1; + + for (i = 0; i < set->n; ++i) + if (set->p[i]->n_div > 0) + return 1; + + return 0; +} + +static isl_stat foreach_lifted_subset(__isl_take isl_set *set, + __isl_take EL *el, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take EL *el, + void *user), void *user) +{ + int i; + + if (!set || !el) + goto error; + + for (i = 0; i < set->n; ++i) { + isl_set *lift; + EL *copy; + + lift = isl_set_from_basic_set(isl_basic_set_copy(set->p[i])); + lift = isl_set_lift(lift); + + copy = FN(EL,copy)(el); + copy = FN(EL,lift)(copy, isl_set_get_space(lift)); + + if (fn(lift, copy, user) < 0) + goto error; + } + + isl_set_free(set); + FN(EL,free)(el); + + return isl_stat_ok; +error: + isl_set_free(set); + FN(EL,free)(el); + return isl_stat_error; +} + +isl_stat FN(PW,foreach_lifted_piece)(__isl_keep PW *pw, + isl_stat (*fn)(__isl_take isl_set *set, __isl_take EL *el, + void *user), void *user) +{ + int i; + + if (!pw) + return isl_stat_error; + + for (i = 0; i < pw->n; ++i) { + isl_set *set; + EL *el; + + set = isl_set_copy(pw->p[i].set); + el = FN(EL,copy)(pw->p[i].FIELD); + if (!any_divs(set)) { + if (fn(set, el, user) < 0) + return isl_stat_error; + continue; + } + if (foreach_lifted_subset(set, el, fn, user) < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} +#endif + +#ifndef NO_MOVE_DIMS +__isl_give PW *FN(PW,move_dims)(__isl_take PW *pw, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + int i; + + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + + pw->dim = isl_space_move_dims(pw->dim, dst_type, dst_pos, src_type, src_pos, n); + if (!pw->dim) + goto error; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].FIELD = FN(EL,move_dims)(pw->p[i].FIELD, + dst_type, dst_pos, src_type, src_pos, n); + if (!pw->p[i].FIELD) + goto error; + } + + if (dst_type == isl_dim_in) + dst_type = isl_dim_set; + if (src_type == isl_dim_in) + src_type = isl_dim_set; + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_move_dims(pw->p[i].set, + dst_type, dst_pos, + src_type, src_pos, n); + if (!pw->p[i].set) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} +#endif + +__isl_give PW *FN(PW,mul_isl_int)(__isl_take PW *pw, isl_int v) +{ + int i; + + if (isl_int_is_one(v)) + return pw; + if (pw && DEFAULT_IS_ZERO && isl_int_is_zero(v)) { + PW *zero; + isl_space *dim = FN(PW,get_space)(pw); +#ifdef HAS_TYPE + zero = FN(PW,ZERO)(dim, pw->type); +#else + zero = FN(PW,ZERO)(dim); +#endif + FN(PW,free)(pw); + return zero; + } + pw = FN(PW,cow)(pw); + if (!pw) + return NULL; + if (pw->n == 0) + return pw; + +#ifdef HAS_TYPE + if (isl_int_is_neg(v)) + pw->type = isl_fold_type_negate(pw->type); +#endif + for (i = 0; i < pw->n; ++i) { + pw->p[i].FIELD = FN(EL,scale)(pw->p[i].FIELD, v); + if (!pw->p[i].FIELD) + goto error; + } + + return pw; +error: + FN(PW,free)(pw); + return NULL; +} + +/* Multiply the pieces of "pw" by "v" and return the result. + */ +__isl_give PW *FN(PW,scale_val)(__isl_take PW *pw, __isl_take isl_val *v) +{ + int i; + + if (!pw || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return pw; + } + if (pw && DEFAULT_IS_ZERO && isl_val_is_zero(v)) { + PW *zero; + isl_space *space = FN(PW,get_space)(pw); +#ifdef HAS_TYPE + zero = FN(PW,ZERO)(space, pw->type); +#else + zero = FN(PW,ZERO)(space); +#endif + FN(PW,free)(pw); + isl_val_free(v); + return zero; + } + if (pw->n == 0) { + isl_val_free(v); + return pw; + } + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + +#ifdef HAS_TYPE + if (isl_val_is_neg(v)) + pw->type = isl_fold_type_negate(pw->type); +#endif + for (i = 0; i < pw->n; ++i) { + pw->p[i].FIELD = FN(EL,scale_val)(pw->p[i].FIELD, + isl_val_copy(v)); + if (!pw->p[i].FIELD) + goto error; + } + + isl_val_free(v); + return pw; +error: + isl_val_free(v); + FN(PW,free)(pw); + return NULL; +} + +/* Divide the pieces of "pw" by "v" and return the result. + */ +__isl_give PW *FN(PW,scale_down_val)(__isl_take PW *pw, __isl_take isl_val *v) +{ + int i; + + if (!pw || !v) + goto error; + + if (isl_val_is_one(v)) { + isl_val_free(v); + return pw; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational factor", goto error); + if (isl_val_is_zero(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "cannot scale down by zero", goto error); + + if (pw->n == 0) { + isl_val_free(v); + return pw; + } + pw = FN(PW,cow)(pw); + if (!pw) + goto error; + +#ifdef HAS_TYPE + if (isl_val_is_neg(v)) + pw->type = isl_fold_type_negate(pw->type); +#endif + for (i = 0; i < pw->n; ++i) { + pw->p[i].FIELD = FN(EL,scale_down_val)(pw->p[i].FIELD, + isl_val_copy(v)); + if (!pw->p[i].FIELD) + goto error; + } + + isl_val_free(v); + return pw; +error: + isl_val_free(v); + FN(PW,free)(pw); + return NULL; +} + +__isl_give PW *FN(PW,scale)(__isl_take PW *pw, isl_int v) +{ + return FN(PW,mul_isl_int)(pw, v); +} + +/* Apply some normalization to "pw". + * In particular, sort the pieces according to their function value + * expressions, combining pairs of adjacent pieces with + * the same such expression, and then normalize the domains of the pieces. + * + * We normalize in place, but if anything goes wrong we need + * to return NULL, so we need to make sure we don't change the + * meaning of any possible other copies of "pw". + */ +__isl_give PW *FN(PW,normalize)(__isl_take PW *pw) +{ + int i; + isl_set *set; + + pw = FN(PW,sort)(pw); + if (!pw) + return NULL; + for (i = 0; i < pw->n; ++i) { + set = isl_set_normalize(isl_set_copy(pw->p[i].set)); + if (!set) + return FN(PW,free)(pw); + isl_set_free(pw->p[i].set); + pw->p[i].set = set; + } + + return pw; +} + +/* Is pw1 obviously equal to pw2? + * That is, do they have obviously identical cells and obviously identical + * elements on each cell? + */ +isl_bool FN(PW,plain_is_equal)(__isl_keep PW *pw1, __isl_keep PW *pw2) +{ + int i; + isl_bool equal; + + if (!pw1 || !pw2) + return isl_bool_error; + + if (pw1 == pw2) + return isl_bool_true; + if (!isl_space_is_equal(pw1->dim, pw2->dim)) + return isl_bool_false; + + pw1 = FN(PW,copy)(pw1); + pw2 = FN(PW,copy)(pw2); + pw1 = FN(PW,normalize)(pw1); + pw2 = FN(PW,normalize)(pw2); + if (!pw1 || !pw2) + goto error; + + equal = pw1->n == pw2->n; + for (i = 0; equal && i < pw1->n; ++i) { + equal = isl_set_plain_is_equal(pw1->p[i].set, pw2->p[i].set); + if (equal < 0) + goto error; + if (!equal) + break; + equal = FN(EL,plain_is_equal)(pw1->p[i].FIELD, pw2->p[i].FIELD); + if (equal < 0) + goto error; + } + + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return equal; +error: + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return isl_bool_error; +} + +#ifndef NO_PULLBACK +static __isl_give PW *FN(PW,align_params_pw_multi_aff_and)(__isl_take PW *pw, + __isl_take isl_multi_aff *ma, + __isl_give PW *(*fn)(__isl_take PW *pw, __isl_take isl_multi_aff *ma)) +{ + isl_ctx *ctx; + isl_space *ma_space; + + ma_space = isl_multi_aff_get_space(ma); + if (!pw || !ma || !ma_space) + goto error; + if (isl_space_match(pw->dim, isl_dim_param, ma_space, isl_dim_param)) { + isl_space_free(ma_space); + return fn(pw, ma); + } + ctx = FN(PW,get_ctx)(pw); + if (!isl_space_has_named_params(pw->dim) || + !isl_space_has_named_params(ma_space)) + isl_die(ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + pw = FN(PW,align_params)(pw, ma_space); + ma = isl_multi_aff_align_params(ma, FN(PW,get_space)(pw)); + return fn(pw, ma); +error: + isl_space_free(ma_space); + FN(PW,free)(pw); + isl_multi_aff_free(ma); + return NULL; +} + +static __isl_give PW *FN(PW,align_params_pw_pw_multi_aff_and)(__isl_take PW *pw, + __isl_take isl_pw_multi_aff *pma, + __isl_give PW *(*fn)(__isl_take PW *pw, + __isl_take isl_pw_multi_aff *ma)) +{ + isl_ctx *ctx; + isl_space *pma_space; + + pma_space = isl_pw_multi_aff_get_space(pma); + if (!pw || !pma || !pma_space) + goto error; + if (isl_space_match(pw->dim, isl_dim_param, pma_space, isl_dim_param)) { + isl_space_free(pma_space); + return fn(pw, pma); + } + ctx = FN(PW,get_ctx)(pw); + if (!isl_space_has_named_params(pw->dim) || + !isl_space_has_named_params(pma_space)) + isl_die(ctx, isl_error_invalid, + "unaligned unnamed parameters", goto error); + pw = FN(PW,align_params)(pw, pma_space); + pma = isl_pw_multi_aff_align_params(pma, FN(PW,get_space)(pw)); + return fn(pw, pma); +error: + isl_space_free(pma_space); + FN(PW,free)(pw); + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Compute the pullback of "pw" by the function represented by "ma". + * In other words, plug in "ma" in "pw". + */ +static __isl_give PW *FN(PW,pullback_multi_aff_aligned)(__isl_take PW *pw, + __isl_take isl_multi_aff *ma) +{ + int i; + isl_space *space = NULL; + + ma = isl_multi_aff_align_divs(ma); + pw = FN(PW,cow)(pw); + if (!pw || !ma) + goto error; + + space = isl_space_join(isl_multi_aff_get_space(ma), + FN(PW,get_space)(pw)); + + for (i = 0; i < pw->n; ++i) { + pw->p[i].set = isl_set_preimage_multi_aff(pw->p[i].set, + isl_multi_aff_copy(ma)); + if (!pw->p[i].set) + goto error; + pw->p[i].FIELD = FN(EL,pullback_multi_aff)(pw->p[i].FIELD, + isl_multi_aff_copy(ma)); + if (!pw->p[i].FIELD) + goto error; + } + + pw = FN(PW,reset_space)(pw, space); + isl_multi_aff_free(ma); + return pw; +error: + isl_space_free(space); + isl_multi_aff_free(ma); + FN(PW,free)(pw); + return NULL; +} + +__isl_give PW *FN(PW,pullback_multi_aff)(__isl_take PW *pw, + __isl_take isl_multi_aff *ma) +{ + return FN(PW,align_params_pw_multi_aff_and)(pw, ma, + &FN(PW,pullback_multi_aff_aligned)); +} + +/* Compute the pullback of "pw" by the function represented by "pma". + * In other words, plug in "pma" in "pw". + */ +static __isl_give PW *FN(PW,pullback_pw_multi_aff_aligned)(__isl_take PW *pw, + __isl_take isl_pw_multi_aff *pma) +{ + int i; + PW *res; + + if (!pma) + goto error; + + if (pma->n == 0) { + isl_space *space; + space = isl_space_join(isl_pw_multi_aff_get_space(pma), + FN(PW,get_space)(pw)); + isl_pw_multi_aff_free(pma); + res = FN(PW,empty)(space); + FN(PW,free)(pw); + return res; + } + + res = FN(PW,pullback_multi_aff)(FN(PW,copy)(pw), + isl_multi_aff_copy(pma->p[0].maff)); + res = FN(PW,intersect_domain)(res, isl_set_copy(pma->p[0].set)); + + for (i = 1; i < pma->n; ++i) { + PW *res_i; + + res_i = FN(PW,pullback_multi_aff)(FN(PW,copy)(pw), + isl_multi_aff_copy(pma->p[i].maff)); + res_i = FN(PW,intersect_domain)(res_i, + isl_set_copy(pma->p[i].set)); + res = FN(PW,add_disjoint)(res, res_i); + } + + isl_pw_multi_aff_free(pma); + FN(PW,free)(pw); + return res; +error: + isl_pw_multi_aff_free(pma); + FN(PW,free)(pw); + return NULL; +} + +__isl_give PW *FN(PW,pullback_pw_multi_aff)(__isl_take PW *pw, + __isl_take isl_pw_multi_aff *pma) +{ + return FN(PW,align_params_pw_pw_multi_aff_and)(pw, pma, + &FN(PW,pullback_pw_multi_aff_aligned)); +} +#endif Index: lib/Analysis/isl/isl_pw_union_opt.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_pw_union_opt.c @@ -0,0 +1,253 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2012 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include + +/* Given a function "cmp" that returns the set of elements where + * "el1" is "better" than "el2", return this set. + */ +static __isl_give isl_set *FN(PW,better)(__isl_keep EL *el1, __isl_keep EL *el2, + __isl_give isl_set *(*cmp)(__isl_take EL *el1, __isl_take EL *el2)) +{ + return cmp(FN(EL,copy)(el1), FN(EL,copy)(el2)); +} + +/* Return a list containing the domains of the pieces of "pw". + */ +static __isl_give isl_set_list *FN(PW,extract_domains)(__isl_keep PW *pw) +{ + int i; + isl_ctx *ctx; + isl_set_list *list; + + if (!pw) + return NULL; + ctx = FN(PW,get_ctx)(pw); + list = isl_set_list_alloc(ctx, pw->n); + for (i = 0; i < pw->n; ++i) + list = isl_set_list_add(list, isl_set_copy(pw->p[i].set)); + + return list; +} + +/* Given sets B ("set"), C ("better") and A' ("out"), return + * + * (B \cap C) \cup ((B \setminus C) \setminus A') + */ +static __isl_give isl_set *FN(PW,better_or_out)(__isl_take isl_set *set, + __isl_take isl_set *better, __isl_take isl_set *out) +{ + isl_set *set_better, *set_out; + + set_better = isl_set_intersect(isl_set_copy(set), isl_set_copy(better)); + set_out = isl_set_subtract(isl_set_subtract(set, better), out); + + return isl_set_union(set_better, set_out); +} + +/* Given sets A ("set"), C ("better") and B' ("out"), return + * + * (A \setminus C) \cup ((A \cap C) \setminus B') + */ +static __isl_give isl_set *FN(PW,worse_or_out)(__isl_take isl_set *set, + __isl_take isl_set *better, __isl_take isl_set *out) +{ + isl_set *set_worse, *set_out; + + set_worse = isl_set_subtract(isl_set_copy(set), isl_set_copy(better)); + set_out = isl_set_subtract(isl_set_intersect(set, better), out); + + return isl_set_union(set_worse, set_out); +} + +/* Given two piecewise expressions "pw1" and "pw2", replace their domains + * by the sets in "list1" and "list2" and combine the results into + * a single piecewise expression. + * The pieces of "pw1" and "pw2" are assumed to have been sorted + * according to the function value expressions. + * The pieces of the result are also sorted in this way. + * + * Run through the pieces of "pw1" and "pw2" in order until they + * have both been exhausted, picking the piece from "pw1" or "pw2" + * depending on which should come first, together with the corresponding + * domain from "list1" or "list2". In cases where the next pieces + * in both "pw1" and "pw2" have the same function value expression, + * construct only a single piece in the result with as domain + * the union of the domains in "list1" and "list2". + */ +static __isl_give PW *FN(PW,merge)(__isl_take PW *pw1, __isl_take PW *pw2, + __isl_take isl_set_list *list1, __isl_take isl_set_list *list2) +{ + int i, j; + PW *res; + + if (!pw1 || !pw2) + goto error; + + res = FN(PW,alloc_size)(isl_space_copy(pw1->dim), pw1->n + pw2->n); + + i = 0; j = 0; + while (i < pw1->n || j < pw2->n) { + int cmp; + isl_set *set; + EL *el; + + if (i < pw1->n && j < pw2->n) + cmp = FN(EL,plain_cmp)(pw1->p[i].FIELD, + pw2->p[j].FIELD); + else + cmp = i < pw1->n ? -1 : 1; + + if (cmp < 0) { + set = isl_set_list_get_set(list1, i); + el = FN(EL,copy)(pw1->p[i].FIELD); + ++i; + } else if (cmp > 0) { + set = isl_set_list_get_set(list2, j); + el = FN(EL,copy)(pw2->p[j].FIELD); + ++j; + } else { + set = isl_set_union(isl_set_list_get_set(list1, i), + isl_set_list_get_set(list2, j)); + el = FN(EL,copy)(pw1->p[i].FIELD); + ++i; + ++j; + } + res = FN(PW,add_piece)(res, set, el); + } + + isl_set_list_free(list1); + isl_set_list_free(list2); + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return res; +error: + isl_set_list_free(list1); + isl_set_list_free(list2); + FN(PW,free)(pw1); + FN(PW,free)(pw2); + return NULL; +} + +/* Given a function "cmp" that returns the set of elements where + * "el1" is "better" than "el2", return a piecewise + * expression defined on the union of the definition domains + * of "pw1" and "pw2" that maps to the "best" of "pw1" and + * "pw2" on each cell. If only one of the two input functions + * is defined on a given cell, then it is considered the best. + * + * Run through all pairs of pieces in "pw1" and "pw2". + * If the domains of these pieces intersect, then the intersection + * needs to be distributed over the two pieces based on "cmp". + * Let C be the set where the piece from "pw2" is better (according to "cmp") + * than the piece from "pw1". Let A be the domain of the piece from "pw1" and + * B the domain of the piece from "pw2". + * + * The elements in C need to be removed from A, except for those parts + * that lie outside of B. That is, + * + * A <- (A \setminus C) \cup ((A \cap C) \setminus B') + * + * Conversely, the elements in B need to be restricted to C, except + * for those parts that lie outside of A. That is + * + * B <- (B \cap C) \cup ((B \setminus C) \setminus A') + * + * Since all pairs of pieces are considered, the domains are updated + * several times. A and B refer to these updated domains + * (kept track of in "list1" and "list2"), while A' and B' refer + * to the original domains of the pieces. It is safe to use these + * original domains because the difference between, say, A' and A is + * the domains of pw2-pieces that have been removed before and + * those domains are disjoint from B. A' is used instead of A + * because the continued updating of A may result in this domain + * getting broken up into more disjuncts. + * + * After the updated domains have been computed, the result is constructed + * from "pw1", "pw2", "list1" and "list2". If there are any pieces + * in "pw1" and "pw2" with the same function value expression, then + * they are combined into a single piece in the result. + * In order to be able to do this efficiently, the pieces of "pw1" and + * "pw2" are first sorted according to their function value expressions. + */ +static __isl_give PW *FN(PW,union_opt_cmp)( + __isl_take PW *pw1, __isl_take PW *pw2, + __isl_give isl_set *(*cmp)(__isl_take EL *el1, __isl_take EL *el2)) +{ + int i, j; + PW *res = NULL; + isl_ctx *ctx; + isl_set *set = NULL; + isl_set_list *list1 = NULL, *list2 = NULL; + + if (!pw1 || !pw2) + goto error; + + ctx = isl_space_get_ctx(pw1->dim); + if (!isl_space_is_equal(pw1->dim, pw2->dim)) + isl_die(ctx, isl_error_invalid, + "arguments should live in the same space", goto error); + + if (FN(PW,is_empty)(pw1)) { + FN(PW,free)(pw1); + return pw2; + } + + if (FN(PW,is_empty)(pw2)) { + FN(PW,free)(pw2); + return pw1; + } + + pw1 = FN(PW,sort)(pw1); + pw2 = FN(PW,sort)(pw2); + if (!pw1 || !pw2) + goto error; + + list1 = FN(PW,extract_domains)(pw1); + list2 = FN(PW,extract_domains)(pw2); + + for (i = 0; i < pw1->n; ++i) { + for (j = 0; j < pw2->n; ++j) { + isl_bool disjoint; + isl_set *better, *set_i, *set_j; + + disjoint = isl_set_is_disjoint(pw1->p[i].set, + pw2->p[j].set); + if (disjoint < 0) + goto error; + if (disjoint) + continue; + better = FN(PW,better)(pw2->p[j].FIELD, + pw1->p[i].FIELD, cmp); + set_i = isl_set_list_get_set(list1, i); + set_j = isl_set_copy(pw2->p[j].set); + set_i = FN(PW,worse_or_out)(set_i, + isl_set_copy(better), set_j); + list1 = isl_set_list_set_set(list1, i, set_i); + set_i = isl_set_copy(pw1->p[i].set); + set_j = isl_set_list_get_set(list2, j); + set_j = FN(PW,better_or_out)(set_j, better, set_i); + list2 = isl_set_list_set_set(list2, j, set_j); + } + } + + res = FN(PW,merge)(pw1, pw2, list1, list2); + + return res; +error: + isl_set_list_free(list1); + isl_set_list_free(list2); + FN(PW,free)(pw1); + FN(PW,free)(pw2); + isl_set_free(set); + return FN(PW,free)(res); +} Index: lib/Analysis/isl/isl_range.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_range.h @@ -0,0 +1,6 @@ +#include + +int isl_qpolynomial_bound_on_domain_range(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct isl_bound *bound); +__isl_give isl_qpolynomial *isl_qpolynomial_terms_of_sign( + __isl_keep isl_qpolynomial *poly, int *signs, int sign); Index: lib/Analysis/isl/isl_range.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_range.c @@ -0,0 +1,539 @@ +#include +#include +#include +#include +#include +#include + +struct range_data { + struct isl_bound *bound; + int *signs; + int sign; + int test_monotonicity; + int monotonicity; + int tight; + isl_qpolynomial *poly; + isl_pw_qpolynomial_fold *pwf; + isl_pw_qpolynomial_fold *pwf_tight; +}; + +static isl_stat propagate_on_domain(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct range_data *data); + +/* Check whether the polynomial "poly" has sign "sign" over "bset", + * i.e., if sign == 1, check that the lower bound on the polynomial + * is non-negative and if sign == -1, check that the upper bound on + * the polynomial is non-positive. + */ +static int has_sign(__isl_keep isl_basic_set *bset, + __isl_keep isl_qpolynomial *poly, int sign, int *signs) +{ + struct range_data data_m; + unsigned nparam; + isl_space *dim; + isl_val *opt; + int r; + enum isl_fold type; + + nparam = isl_basic_set_dim(bset, isl_dim_param); + + bset = isl_basic_set_copy(bset); + poly = isl_qpolynomial_copy(poly); + + bset = isl_basic_set_move_dims(bset, isl_dim_set, 0, + isl_dim_param, 0, nparam); + poly = isl_qpolynomial_move_dims(poly, isl_dim_in, 0, + isl_dim_param, 0, nparam); + + dim = isl_qpolynomial_get_space(poly); + dim = isl_space_params(dim); + dim = isl_space_from_domain(dim); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + + data_m.test_monotonicity = 0; + data_m.signs = signs; + data_m.sign = -sign; + type = data_m.sign < 0 ? isl_fold_min : isl_fold_max; + data_m.pwf = isl_pw_qpolynomial_fold_zero(dim, type); + data_m.tight = 0; + data_m.pwf_tight = NULL; + + if (propagate_on_domain(bset, poly, &data_m) < 0) + goto error; + + if (sign > 0) + opt = isl_pw_qpolynomial_fold_min(data_m.pwf); + else + opt = isl_pw_qpolynomial_fold_max(data_m.pwf); + + if (!opt) + r = -1; + else if (isl_val_is_nan(opt) || + isl_val_is_infty(opt) || + isl_val_is_neginfty(opt)) + r = 0; + else + r = sign * isl_val_sgn(opt) >= 0; + + isl_val_free(opt); + + return r; +error: + isl_pw_qpolynomial_fold_free(data_m.pwf); + return -1; +} + +/* Return 1 if poly is monotonically increasing in the last set variable, + * -1 if poly is monotonically decreasing in the last set variable, + * 0 if no conclusion, + * -2 on error. + * + * We simply check the sign of p(x+1)-p(x) + */ +static int monotonicity(__isl_keep isl_basic_set *bset, + __isl_keep isl_qpolynomial *poly, struct range_data *data) +{ + isl_ctx *ctx; + isl_space *dim; + isl_qpolynomial *sub = NULL; + isl_qpolynomial *diff = NULL; + int result = 0; + int s; + unsigned nvar; + + ctx = isl_qpolynomial_get_ctx(poly); + dim = isl_qpolynomial_get_domain_space(poly); + + nvar = isl_basic_set_dim(bset, isl_dim_set); + + sub = isl_qpolynomial_var_on_domain(isl_space_copy(dim), isl_dim_set, nvar - 1); + sub = isl_qpolynomial_add(sub, + isl_qpolynomial_rat_cst_on_domain(dim, ctx->one, ctx->one)); + + diff = isl_qpolynomial_substitute(isl_qpolynomial_copy(poly), + isl_dim_in, nvar - 1, 1, &sub); + diff = isl_qpolynomial_sub(diff, isl_qpolynomial_copy(poly)); + + s = has_sign(bset, diff, 1, data->signs); + if (s < 0) + goto error; + if (s) + result = 1; + else { + s = has_sign(bset, diff, -1, data->signs); + if (s < 0) + goto error; + if (s) + result = -1; + } + + isl_qpolynomial_free(diff); + isl_qpolynomial_free(sub); + + return result; +error: + isl_qpolynomial_free(diff); + isl_qpolynomial_free(sub); + return -2; +} + +/* Return a positive ("sign" > 0) or negative ("sign" < 0) infinite polynomial + * with domain space "space". + */ +static __isl_give isl_qpolynomial *signed_infty(__isl_take isl_space *space, + int sign) +{ + if (sign > 0) + return isl_qpolynomial_infty_on_domain(space); + else + return isl_qpolynomial_neginfty_on_domain(space); +} + +static __isl_give isl_qpolynomial *bound2poly(__isl_take isl_constraint *bound, + __isl_take isl_space *space, unsigned pos, int sign) +{ + if (!bound) + return signed_infty(space, sign); + isl_space_free(space); + return isl_qpolynomial_from_constraint(bound, isl_dim_set, pos); +} + +static int bound_is_integer(__isl_take isl_constraint *bound, unsigned pos) +{ + isl_int c; + int is_int; + + if (!bound) + return 1; + + isl_int_init(c); + isl_constraint_get_coefficient(bound, isl_dim_set, pos, &c); + is_int = isl_int_is_one(c) || isl_int_is_negone(c); + isl_int_clear(c); + + return is_int; +} + +struct isl_fixed_sign_data { + int *signs; + int sign; + isl_qpolynomial *poly; +}; + +/* Add term "term" to data->poly if it has sign data->sign. + * The sign is determined based on the signs of the parameters + * and variables in data->signs. The integer divisions, if + * any, are assumed to be non-negative. + */ +static isl_stat collect_fixed_sign_terms(__isl_take isl_term *term, void *user) +{ + struct isl_fixed_sign_data *data = (struct isl_fixed_sign_data *)user; + isl_int n; + int i; + int sign; + unsigned nparam; + unsigned nvar; + + if (!term) + return isl_stat_error; + + nparam = isl_term_dim(term, isl_dim_param); + nvar = isl_term_dim(term, isl_dim_set); + + isl_int_init(n); + + isl_term_get_num(term, &n); + + sign = isl_int_sgn(n); + for (i = 0; i < nparam; ++i) { + if (data->signs[i] > 0) + continue; + if (isl_term_get_exp(term, isl_dim_param, i) % 2) + sign = -sign; + } + for (i = 0; i < nvar; ++i) { + if (data->signs[nparam + i] > 0) + continue; + if (isl_term_get_exp(term, isl_dim_set, i) % 2) + sign = -sign; + } + + if (sign == data->sign) { + isl_qpolynomial *t = isl_qpolynomial_from_term(term); + + data->poly = isl_qpolynomial_add(data->poly, t); + } else + isl_term_free(term); + + isl_int_clear(n); + + return isl_stat_ok; +} + +/* Construct and return a polynomial that consists of the terms + * in "poly" that have sign "sign". The integer divisions, if + * any, are assumed to be non-negative. + */ +__isl_give isl_qpolynomial *isl_qpolynomial_terms_of_sign( + __isl_keep isl_qpolynomial *poly, int *signs, int sign) +{ + isl_space *space; + struct isl_fixed_sign_data data = { signs, sign }; + + space = isl_qpolynomial_get_domain_space(poly); + data.poly = isl_qpolynomial_zero_on_domain(space); + + if (isl_qpolynomial_foreach_term(poly, collect_fixed_sign_terms, &data) < 0) + goto error; + + return data.poly; +error: + isl_qpolynomial_free(data.poly); + return NULL; +} + +/* Helper function to add a guarded polynomial to either pwf_tight or pwf, + * depending on whether the result has been determined to be tight. + */ +static isl_stat add_guarded_poly(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct range_data *data) +{ + enum isl_fold type = data->sign < 0 ? isl_fold_min : isl_fold_max; + isl_set *set; + isl_qpolynomial_fold *fold; + isl_pw_qpolynomial_fold *pwf; + + bset = isl_basic_set_params(bset); + poly = isl_qpolynomial_project_domain_on_params(poly); + + fold = isl_qpolynomial_fold_alloc(type, poly); + set = isl_set_from_basic_set(bset); + pwf = isl_pw_qpolynomial_fold_alloc(type, set, fold); + if (data->tight) + data->pwf_tight = isl_pw_qpolynomial_fold_fold( + data->pwf_tight, pwf); + else + data->pwf = isl_pw_qpolynomial_fold_fold(data->pwf, pwf); + + return isl_stat_ok; +} + +/* Plug in "sub" for the variable at position "pos" in "poly". + * + * If "sub" is an infinite polynomial and if the variable actually + * appears in "poly", then calling isl_qpolynomial_substitute + * to perform the substitution may result in a NaN result. + * In such cases, return positive or negative infinity instead, + * depending on whether an upper bound or a lower bound is being computed, + * and mark the result as not being tight. + */ +static __isl_give isl_qpolynomial *plug_in_at_pos( + __isl_take isl_qpolynomial *poly, int pos, + __isl_take isl_qpolynomial *sub, struct range_data *data) +{ + isl_bool involves, infty; + + involves = isl_qpolynomial_involves_dims(poly, isl_dim_in, pos, 1); + if (involves < 0) + goto error; + if (!involves) { + isl_qpolynomial_free(sub); + return poly; + } + + infty = isl_qpolynomial_is_infty(sub); + if (infty >= 0 && !infty) + infty = isl_qpolynomial_is_neginfty(sub); + if (infty < 0) + goto error; + if (infty) { + isl_space *space = isl_qpolynomial_get_domain_space(poly); + data->tight = 0; + isl_qpolynomial_free(poly); + isl_qpolynomial_free(sub); + return signed_infty(space, data->sign); + } + + poly = isl_qpolynomial_substitute(poly, isl_dim_in, pos, 1, &sub); + isl_qpolynomial_free(sub); + + return poly; +error: + isl_qpolynomial_free(poly); + isl_qpolynomial_free(sub); + return NULL; +} + +/* Given a lower and upper bound on the final variable and constraints + * on the remaining variables where these bounds are active, + * eliminate the variable from data->poly based on these bounds. + * If the polynomial has been determined to be monotonic + * in the variable, then simply plug in the appropriate bound. + * If the current polynomial is tight and if this bound is integer, + * then the result is still tight. In all other cases, the results + * may not be tight. + * Otherwise, plug in the largest bound (in absolute value) in + * the positive terms (if an upper bound is wanted) or the negative terms + * (if a lower bounded is wanted) and the other bound in the other terms. + * + * If all variables have been eliminated, then record the result. + * Ohterwise, recurse on the next variable. + */ +static isl_stat propagate_on_bound_pair(__isl_take isl_constraint *lower, + __isl_take isl_constraint *upper, __isl_take isl_basic_set *bset, + void *user) +{ + struct range_data *data = (struct range_data *)user; + int save_tight = data->tight; + isl_qpolynomial *poly; + isl_stat r; + unsigned nvar; + + nvar = isl_basic_set_dim(bset, isl_dim_set); + + if (data->monotonicity) { + isl_qpolynomial *sub; + isl_space *dim = isl_qpolynomial_get_domain_space(data->poly); + if (data->monotonicity * data->sign > 0) { + if (data->tight) + data->tight = bound_is_integer(upper, nvar); + sub = bound2poly(upper, dim, nvar, 1); + isl_constraint_free(lower); + } else { + if (data->tight) + data->tight = bound_is_integer(lower, nvar); + sub = bound2poly(lower, dim, nvar, -1); + isl_constraint_free(upper); + } + poly = isl_qpolynomial_copy(data->poly); + poly = plug_in_at_pos(poly, nvar, sub, data); + poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, nvar, 1); + } else { + isl_qpolynomial *l, *u; + isl_qpolynomial *pos, *neg; + isl_space *dim = isl_qpolynomial_get_domain_space(data->poly); + unsigned nparam = isl_basic_set_dim(bset, isl_dim_param); + int sign = data->sign * data->signs[nparam + nvar]; + + data->tight = 0; + + u = bound2poly(upper, isl_space_copy(dim), nvar, 1); + l = bound2poly(lower, dim, nvar, -1); + + pos = isl_qpolynomial_terms_of_sign(data->poly, data->signs, sign); + neg = isl_qpolynomial_terms_of_sign(data->poly, data->signs, -sign); + + pos = plug_in_at_pos(pos, nvar, u, data); + neg = plug_in_at_pos(neg, nvar, l, data); + + poly = isl_qpolynomial_add(pos, neg); + poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, nvar, 1); + } + + if (isl_basic_set_dim(bset, isl_dim_set) == 0) + r = add_guarded_poly(bset, poly, data); + else + r = propagate_on_domain(bset, poly, data); + + data->tight = save_tight; + + return r; +} + +/* Recursively perform range propagation on the polynomial "poly" + * defined over the basic set "bset" and collect the results in "data". + */ +static isl_stat propagate_on_domain(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct range_data *data) +{ + isl_ctx *ctx; + isl_qpolynomial *save_poly = data->poly; + int save_monotonicity = data->monotonicity; + unsigned d; + + if (!bset || !poly) + goto error; + + ctx = isl_basic_set_get_ctx(bset); + d = isl_basic_set_dim(bset, isl_dim_set); + isl_assert(ctx, d >= 1, goto error); + + if (isl_qpolynomial_is_cst(poly, NULL, NULL)) { + bset = isl_basic_set_project_out(bset, isl_dim_set, 0, d); + poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, 0, d); + return add_guarded_poly(bset, poly, data); + } + + if (data->test_monotonicity) + data->monotonicity = monotonicity(bset, poly, data); + else + data->monotonicity = 0; + if (data->monotonicity < -1) + goto error; + + data->poly = poly; + if (isl_basic_set_foreach_bound_pair(bset, isl_dim_set, d - 1, + &propagate_on_bound_pair, data) < 0) + goto error; + + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + data->monotonicity = save_monotonicity; + data->poly = save_poly; + + return isl_stat_ok; +error: + isl_basic_set_free(bset); + isl_qpolynomial_free(poly); + data->monotonicity = save_monotonicity; + data->poly = save_poly; + return isl_stat_error; +} + +static isl_stat basic_guarded_poly_bound(__isl_take isl_basic_set *bset, + void *user) +{ + struct range_data *data = (struct range_data *)user; + isl_ctx *ctx; + unsigned nparam = isl_basic_set_dim(bset, isl_dim_param); + unsigned dim = isl_basic_set_dim(bset, isl_dim_set); + isl_stat r; + + data->signs = NULL; + + ctx = isl_basic_set_get_ctx(bset); + data->signs = isl_alloc_array(ctx, int, + isl_basic_set_dim(bset, isl_dim_all)); + + if (isl_basic_set_dims_get_sign(bset, isl_dim_set, 0, dim, + data->signs + nparam) < 0) + goto error; + if (isl_basic_set_dims_get_sign(bset, isl_dim_param, 0, nparam, + data->signs) < 0) + goto error; + + r = propagate_on_domain(bset, isl_qpolynomial_copy(data->poly), data); + + free(data->signs); + + return r; +error: + free(data->signs); + isl_basic_set_free(bset); + return isl_stat_error; +} + +static int qpolynomial_bound_on_domain_range(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct range_data *data) +{ + unsigned nparam = isl_basic_set_dim(bset, isl_dim_param); + unsigned nvar = isl_basic_set_dim(bset, isl_dim_set); + isl_set *set = NULL; + + if (!bset) + goto error; + + if (nvar == 0) + return add_guarded_poly(bset, poly, data); + + set = isl_set_from_basic_set(bset); + set = isl_set_split_dims(set, isl_dim_param, 0, nparam); + set = isl_set_split_dims(set, isl_dim_set, 0, nvar); + + data->poly = poly; + + data->test_monotonicity = 1; + if (isl_set_foreach_basic_set(set, &basic_guarded_poly_bound, data) < 0) + goto error; + + isl_set_free(set); + isl_qpolynomial_free(poly); + + return 0; +error: + isl_set_free(set); + isl_qpolynomial_free(poly); + return -1; +} + +int isl_qpolynomial_bound_on_domain_range(__isl_take isl_basic_set *bset, + __isl_take isl_qpolynomial *poly, struct isl_bound *bound) +{ + struct range_data data; + int r; + + data.pwf = bound->pwf; + data.pwf_tight = bound->pwf_tight; + data.tight = bound->check_tight; + if (bound->type == isl_fold_min) + data.sign = -1; + else + data.sign = 1; + + r = qpolynomial_bound_on_domain_range(bset, poly, &data); + + bound->pwf = data.pwf; + bound->pwf_tight = data.pwf_tight; + + return r; +} Index: lib/Analysis/isl/isl_reordering.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_reordering.h @@ -0,0 +1,31 @@ +#ifndef ISL_REORDERING_H +#define ISL_REORDERING_H + +#include + +/* pos maps original dimensions to new dimensions. + * The final dimension is given by dim. + * The number of dimensions (i.e., the range of values) in the result + * may be larger than the number of dimensions in the input. + * In particular, the possible values of the entries in pos ranges from 0 to + * the total dimension of dim - 1, unless isl_reordering_extend + * has been called. + */ +struct isl_reordering { + int ref; + isl_space *dim; + unsigned len; + int pos[1]; +}; +typedef struct isl_reordering isl_reordering; + +__isl_give isl_reordering *isl_parameter_alignment_reordering( + __isl_keep isl_space *alignee, __isl_keep isl_space *aligner); +__isl_give isl_reordering *isl_reordering_copy(__isl_keep isl_reordering *exp); +void *isl_reordering_free(__isl_take isl_reordering *exp); +__isl_give isl_reordering *isl_reordering_extend_space( + __isl_take isl_reordering *exp, __isl_take isl_space *dim); +__isl_give isl_reordering *isl_reordering_extend(__isl_take isl_reordering *exp, + unsigned extra); + +#endif Index: lib/Analysis/isl/isl_reordering.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_reordering.c @@ -0,0 +1,205 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include +#include +#include + +__isl_give isl_reordering *isl_reordering_alloc(isl_ctx *ctx, int len) +{ + isl_reordering *exp; + + exp = isl_alloc(ctx, struct isl_reordering, + sizeof(struct isl_reordering) + (len - 1) * sizeof(int)); + if (!exp) + return NULL; + + exp->ref = 1; + exp->len = len; + exp->dim = NULL; + + return exp; +} + +__isl_give isl_reordering *isl_reordering_copy(__isl_keep isl_reordering *exp) +{ + if (!exp) + return NULL; + + exp->ref++; + return exp; +} + +__isl_give isl_reordering *isl_reordering_dup(__isl_keep isl_reordering *r) +{ + int i; + isl_reordering *dup; + + if (!r) + return NULL; + + dup = isl_reordering_alloc(r->dim->ctx, r->len); + if (!dup) + return NULL; + + dup->dim = isl_space_copy(r->dim); + if (!dup->dim) + return isl_reordering_free(dup); + for (i = 0; i < dup->len; ++i) + dup->pos[i] = r->pos[i]; + + return dup; +} + +__isl_give isl_reordering *isl_reordering_cow(__isl_take isl_reordering *r) +{ + if (!r) + return NULL; + + if (r->ref == 1) + return r; + r->ref--; + return isl_reordering_dup(r); +} + +void *isl_reordering_free(__isl_take isl_reordering *exp) +{ + if (!exp) + return NULL; + + if (--exp->ref > 0) + return NULL; + + isl_space_free(exp->dim); + free(exp); + return NULL; +} + +/* Construct a reordering that maps the parameters of "alignee" + * to the corresponding parameters in a new dimension specification + * that has the parameters of "aligner" first, followed by + * any remaining parameters of "alignee" that do not occur in "aligner". + */ +__isl_give isl_reordering *isl_parameter_alignment_reordering( + __isl_keep isl_space *alignee, __isl_keep isl_space *aligner) +{ + int i, j; + isl_reordering *exp; + + if (!alignee || !aligner) + return NULL; + + exp = isl_reordering_alloc(alignee->ctx, alignee->nparam); + if (!exp) + return NULL; + + exp->dim = isl_space_copy(aligner); + + for (i = 0; i < alignee->nparam; ++i) { + isl_id *id_i; + id_i = isl_space_get_dim_id(alignee, isl_dim_param, i); + if (!id_i) + isl_die(alignee->ctx, isl_error_invalid, + "cannot align unnamed parameters", goto error); + for (j = 0; j < aligner->nparam; ++j) { + isl_id *id_j; + id_j = isl_space_get_dim_id(aligner, isl_dim_param, j); + isl_id_free(id_j); + if (id_i == id_j) + break; + } + if (j < aligner->nparam) { + exp->pos[i] = j; + isl_id_free(id_i); + } else { + int pos; + pos = isl_space_dim(exp->dim, isl_dim_param); + exp->dim = isl_space_add_dims(exp->dim, isl_dim_param, 1); + exp->dim = isl_space_set_dim_id(exp->dim, + isl_dim_param, pos, id_i); + exp->pos[i] = pos; + } + } + + if (!exp->dim) + return isl_reordering_free(exp); + return exp; +error: + isl_reordering_free(exp); + return NULL; +} + +__isl_give isl_reordering *isl_reordering_extend(__isl_take isl_reordering *exp, + unsigned extra) +{ + int i; + isl_reordering *res; + int offset; + + if (!exp) + return NULL; + if (extra == 0) + return exp; + + offset = isl_space_dim(exp->dim, isl_dim_all) - exp->len; + res = isl_reordering_alloc(exp->dim->ctx, exp->len + extra); + if (!res) + goto error; + res->dim = isl_space_copy(exp->dim); + for (i = 0; i < exp->len; ++i) + res->pos[i] = exp->pos[i]; + for (i = exp->len; i < res->len; ++i) + res->pos[i] = offset + i; + + isl_reordering_free(exp); + + return res; +error: + isl_reordering_free(exp); + return NULL; +} + +__isl_give isl_reordering *isl_reordering_extend_space( + __isl_take isl_reordering *exp, __isl_take isl_space *dim) +{ + isl_reordering *res; + + if (!exp || !dim) + goto error; + + res = isl_reordering_extend(isl_reordering_copy(exp), + isl_space_dim(dim, isl_dim_all) - exp->len); + res = isl_reordering_cow(res); + if (!res) + goto error; + isl_space_free(res->dim); + res->dim = isl_space_replace(dim, isl_dim_param, exp->dim); + + isl_reordering_free(exp); + + if (!res->dim) + return isl_reordering_free(res); + + return res; +error: + isl_reordering_free(exp); + isl_space_free(dim); + return NULL; +} + +void isl_reordering_dump(__isl_keep isl_reordering *exp) +{ + int i; + + isl_space_dump(exp->dim); + for (i = 0; i < exp->len; ++i) + fprintf(stderr, "%d -> %d; ", i, exp->pos[i]); + fprintf(stderr, "\n"); +} Index: lib/Analysis/isl/isl_sample.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_sample.h @@ -0,0 +1,35 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_SAMPLE_H +#define ISL_SAMPLE_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +__isl_give isl_vec *isl_basic_set_sample_vec(__isl_take isl_basic_set *bset); +struct isl_vec *isl_basic_set_sample_bounded(struct isl_basic_set *bset); +__isl_give isl_vec *isl_basic_set_sample_with_cone( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *cone); + +__isl_give isl_basic_set *isl_basic_set_from_vec(__isl_take isl_vec *vec); + +int isl_tab_set_initial_basis_with_cone(struct isl_tab *tab, + struct isl_tab *tab_cone); +struct isl_vec *isl_tab_sample(struct isl_tab *tab); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/isl_sample.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_sample.c @@ -0,0 +1,1303 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include "isl_sample.h" +#include +#include +#include +#include "isl_equalities.h" +#include "isl_tab.h" +#include "isl_basis_reduction.h" +#include +#include +#include +#include + +static struct isl_vec *empty_sample(struct isl_basic_set *bset) +{ + struct isl_vec *vec; + + vec = isl_vec_alloc(bset->ctx, 0); + isl_basic_set_free(bset); + return vec; +} + +/* Construct a zero sample of the same dimension as bset. + * As a special case, if bset is zero-dimensional, this + * function creates a zero-dimensional sample point. + */ +static struct isl_vec *zero_sample(struct isl_basic_set *bset) +{ + unsigned dim; + struct isl_vec *sample; + + dim = isl_basic_set_total_dim(bset); + sample = isl_vec_alloc(bset->ctx, 1 + dim); + if (sample) { + isl_int_set_si(sample->el[0], 1); + isl_seq_clr(sample->el + 1, dim); + } + isl_basic_set_free(bset); + return sample; +} + +static struct isl_vec *interval_sample(struct isl_basic_set *bset) +{ + int i; + isl_int t; + struct isl_vec *sample; + + bset = isl_basic_set_simplify(bset); + if (!bset) + return NULL; + if (isl_basic_set_plain_is_empty(bset)) + return empty_sample(bset); + if (bset->n_eq == 0 && bset->n_ineq == 0) + return zero_sample(bset); + + sample = isl_vec_alloc(bset->ctx, 2); + if (!sample) + goto error; + if (!bset) + return NULL; + isl_int_set_si(sample->block.data[0], 1); + + if (bset->n_eq > 0) { + isl_assert(bset->ctx, bset->n_eq == 1, goto error); + isl_assert(bset->ctx, bset->n_ineq == 0, goto error); + if (isl_int_is_one(bset->eq[0][1])) + isl_int_neg(sample->el[1], bset->eq[0][0]); + else { + isl_assert(bset->ctx, isl_int_is_negone(bset->eq[0][1]), + goto error); + isl_int_set(sample->el[1], bset->eq[0][0]); + } + isl_basic_set_free(bset); + return sample; + } + + isl_int_init(t); + if (isl_int_is_one(bset->ineq[0][1])) + isl_int_neg(sample->block.data[1], bset->ineq[0][0]); + else + isl_int_set(sample->block.data[1], bset->ineq[0][0]); + for (i = 1; i < bset->n_ineq; ++i) { + isl_seq_inner_product(sample->block.data, + bset->ineq[i], 2, &t); + if (isl_int_is_neg(t)) + break; + } + isl_int_clear(t); + if (i < bset->n_ineq) { + isl_vec_free(sample); + return empty_sample(bset); + } + + isl_basic_set_free(bset); + return sample; +error: + isl_basic_set_free(bset); + isl_vec_free(sample); + return NULL; +} + +/* Find a sample integer point, if any, in bset, which is known + * to have equalities. If bset contains no integer points, then + * return a zero-length vector. + * We simply remove the known equalities, compute a sample + * in the resulting bset, using the specified recurse function, + * and then transform the sample back to the original space. + */ +static struct isl_vec *sample_eq(struct isl_basic_set *bset, + struct isl_vec *(*recurse)(struct isl_basic_set *)) +{ + struct isl_mat *T; + struct isl_vec *sample; + + if (!bset) + return NULL; + + bset = isl_basic_set_remove_equalities(bset, &T, NULL); + sample = recurse(bset); + if (!sample || sample->size == 0) + isl_mat_free(T); + else + sample = isl_mat_vec_product(T, sample); + return sample; +} + +/* Return a matrix containing the equalities of the tableau + * in constraint form. The tableau is assumed to have + * an associated bset that has been kept up-to-date. + */ +static struct isl_mat *tab_equalities(struct isl_tab *tab) +{ + int i, j; + int n_eq; + struct isl_mat *eq; + struct isl_basic_set *bset; + + if (!tab) + return NULL; + + bset = isl_tab_peek_bset(tab); + isl_assert(tab->mat->ctx, bset, return NULL); + + n_eq = tab->n_var - tab->n_col + tab->n_dead; + if (tab->empty || n_eq == 0) + return isl_mat_alloc(tab->mat->ctx, 0, tab->n_var); + if (n_eq == tab->n_var) + return isl_mat_identity(tab->mat->ctx, tab->n_var); + + eq = isl_mat_alloc(tab->mat->ctx, n_eq, tab->n_var); + if (!eq) + return NULL; + for (i = 0, j = 0; i < tab->n_con; ++i) { + if (tab->con[i].is_row) + continue; + if (tab->con[i].index >= 0 && tab->con[i].index >= tab->n_dead) + continue; + if (i < bset->n_eq) + isl_seq_cpy(eq->row[j], bset->eq[i] + 1, tab->n_var); + else + isl_seq_cpy(eq->row[j], + bset->ineq[i - bset->n_eq] + 1, tab->n_var); + ++j; + } + isl_assert(bset->ctx, j == n_eq, goto error); + return eq; +error: + isl_mat_free(eq); + return NULL; +} + +/* Compute and return an initial basis for the bounded tableau "tab". + * + * If the tableau is either full-dimensional or zero-dimensional, + * the we simply return an identity matrix. + * Otherwise, we construct a basis whose first directions correspond + * to equalities. + */ +static struct isl_mat *initial_basis(struct isl_tab *tab) +{ + int n_eq; + struct isl_mat *eq; + struct isl_mat *Q; + + tab->n_unbounded = 0; + tab->n_zero = n_eq = tab->n_var - tab->n_col + tab->n_dead; + if (tab->empty || n_eq == 0 || n_eq == tab->n_var) + return isl_mat_identity(tab->mat->ctx, 1 + tab->n_var); + + eq = tab_equalities(tab); + eq = isl_mat_left_hermite(eq, 0, NULL, &Q); + if (!eq) + return NULL; + isl_mat_free(eq); + + Q = isl_mat_lin_to_aff(Q); + return Q; +} + +/* Compute the minimum of the current ("level") basis row over "tab" + * and store the result in position "level" of "min". + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +static enum isl_lp_result compute_min(isl_ctx *ctx, struct isl_tab *tab, + __isl_keep isl_vec *min, int level) +{ + return isl_tab_min(tab, tab->basis->row[1 + level], + ctx->one, &min->el[level], NULL, 0); +} + +/* Compute the maximum of the current ("level") basis row over "tab" + * and store the result in position "level" of "max". + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +static enum isl_lp_result compute_max(isl_ctx *ctx, struct isl_tab *tab, + __isl_keep isl_vec *max, int level) +{ + enum isl_lp_result res; + unsigned dim = tab->n_var; + + isl_seq_neg(tab->basis->row[1 + level] + 1, + tab->basis->row[1 + level] + 1, dim); + res = isl_tab_min(tab, tab->basis->row[1 + level], + ctx->one, &max->el[level], NULL, 0); + isl_seq_neg(tab->basis->row[1 + level] + 1, + tab->basis->row[1 + level] + 1, dim); + isl_int_neg(max->el[level], max->el[level]); + + return res; +} + +/* Perform a greedy search for an integer point in the set represented + * by "tab", given that the minimal rational value (rounded up to the + * nearest integer) at "level" is smaller than the maximal rational + * value (rounded down to the nearest integer). + * + * Return 1 if we have found an integer point (if tab->n_unbounded > 0 + * then we may have only found integer values for the bounded dimensions + * and it is the responsibility of the caller to extend this solution + * to the unbounded dimensions). + * Return 0 if greedy search did not result in a solution. + * Return -1 if some error occurred. + * + * We assign a value half-way between the minimum and the maximum + * to the current dimension and check if the minimal value of the + * next dimension is still smaller than (or equal) to the maximal value. + * We continue this process until either + * - the minimal value (rounded up) is greater than the maximal value + * (rounded down). In this case, greedy search has failed. + * - we have exhausted all bounded dimensions, meaning that we have + * found a solution. + * - the sample value of the tableau is integral. + * - some error has occurred. + */ +static int greedy_search(isl_ctx *ctx, struct isl_tab *tab, + __isl_keep isl_vec *min, __isl_keep isl_vec *max, int level) +{ + struct isl_tab_undo *snap; + enum isl_lp_result res; + + snap = isl_tab_snap(tab); + + do { + isl_int_add(tab->basis->row[1 + level][0], + min->el[level], max->el[level]); + isl_int_fdiv_q_ui(tab->basis->row[1 + level][0], + tab->basis->row[1 + level][0], 2); + isl_int_neg(tab->basis->row[1 + level][0], + tab->basis->row[1 + level][0]); + if (isl_tab_add_valid_eq(tab, tab->basis->row[1 + level]) < 0) + return -1; + isl_int_set_si(tab->basis->row[1 + level][0], 0); + + if (++level >= tab->n_var - tab->n_unbounded) + return 1; + if (isl_tab_sample_is_integer(tab)) + return 1; + + res = compute_min(ctx, tab, min, level); + if (res == isl_lp_error) + return -1; + if (res != isl_lp_ok) + isl_die(ctx, isl_error_internal, + "expecting bounded rational solution", + return -1); + res = compute_max(ctx, tab, max, level); + if (res == isl_lp_error) + return -1; + if (res != isl_lp_ok) + isl_die(ctx, isl_error_internal, + "expecting bounded rational solution", + return -1); + } while (isl_int_le(min->el[level], max->el[level])); + + if (isl_tab_rollback(tab, snap) < 0) + return -1; + + return 0; +} + +/* Given a tableau representing a set, find and return + * an integer point in the set, if there is any. + * + * We perform a depth first search + * for an integer point, by scanning all possible values in the range + * attained by a basis vector, where an initial basis may have been set + * by the calling function. Otherwise an initial basis that exploits + * the equalities in the tableau is created. + * tab->n_zero is currently ignored and is clobbered by this function. + * + * The tableau is allowed to have unbounded direction, but then + * the calling function needs to set an initial basis, with the + * unbounded directions last and with tab->n_unbounded set + * to the number of unbounded directions. + * Furthermore, the calling functions needs to add shifted copies + * of all constraints involving unbounded directions to ensure + * that any feasible rational value in these directions can be rounded + * up to yield a feasible integer value. + * In particular, let B define the given basis x' = B x + * and let T be the inverse of B, i.e., X = T x'. + * Let a x + c >= 0 be a constraint of the set represented by the tableau, + * or a T x' + c >= 0 in terms of the given basis. Assume that + * the bounded directions have an integer value, then we can safely + * round up the values for the unbounded directions if we make sure + * that x' not only satisfies the original constraint, but also + * the constraint "a T x' + c + s >= 0" with s the sum of all + * negative values in the last n_unbounded entries of "a T". + * The calling function therefore needs to add the constraint + * a x + c + s >= 0. The current function then scans the first + * directions for an integer value and once those have been found, + * it can compute "T ceil(B x)" to yield an integer point in the set. + * Note that during the search, the first rows of B may be changed + * by a basis reduction, but the last n_unbounded rows of B remain + * unaltered and are also not mixed into the first rows. + * + * The search is implemented iteratively. "level" identifies the current + * basis vector. "init" is true if we want the first value at the current + * level and false if we want the next value. + * + * At the start of each level, we first check if we can find a solution + * using greedy search. If not, we continue with the exhaustive search. + * + * The initial basis is the identity matrix. If the range in some direction + * contains more than one integer value, we perform basis reduction based + * on the value of ctx->opt->gbr + * - ISL_GBR_NEVER: never perform basis reduction + * - ISL_GBR_ONCE: only perform basis reduction the first + * time such a range is encountered + * - ISL_GBR_ALWAYS: always perform basis reduction when + * such a range is encountered + * + * When ctx->opt->gbr is set to ISL_GBR_ALWAYS, then we allow the basis + * reduction computation to return early. That is, as soon as it + * finds a reasonable first direction. + */ +struct isl_vec *isl_tab_sample(struct isl_tab *tab) +{ + unsigned dim; + unsigned gbr; + struct isl_ctx *ctx; + struct isl_vec *sample; + struct isl_vec *min; + struct isl_vec *max; + enum isl_lp_result res; + int level; + int init; + int reduced; + struct isl_tab_undo **snap; + + if (!tab) + return NULL; + if (tab->empty) + return isl_vec_alloc(tab->mat->ctx, 0); + + if (!tab->basis) + tab->basis = initial_basis(tab); + if (!tab->basis) + return NULL; + isl_assert(tab->mat->ctx, tab->basis->n_row == tab->n_var + 1, + return NULL); + isl_assert(tab->mat->ctx, tab->basis->n_col == tab->n_var + 1, + return NULL); + + ctx = tab->mat->ctx; + dim = tab->n_var; + gbr = ctx->opt->gbr; + + if (tab->n_unbounded == tab->n_var) { + sample = isl_tab_get_sample_value(tab); + sample = isl_mat_vec_product(isl_mat_copy(tab->basis), sample); + sample = isl_vec_ceil(sample); + sample = isl_mat_vec_inverse_product(isl_mat_copy(tab->basis), + sample); + return sample; + } + + if (isl_tab_extend_cons(tab, dim + 1) < 0) + return NULL; + + min = isl_vec_alloc(ctx, dim); + max = isl_vec_alloc(ctx, dim); + snap = isl_alloc_array(ctx, struct isl_tab_undo *, dim); + + if (!min || !max || !snap) + goto error; + + level = 0; + init = 1; + reduced = 0; + + while (level >= 0) { + if (init) { + int choice; + + res = compute_min(ctx, tab, min, level); + if (res == isl_lp_error) + goto error; + if (res != isl_lp_ok) + isl_die(ctx, isl_error_internal, + "expecting bounded rational solution", + goto error); + if (isl_tab_sample_is_integer(tab)) + break; + res = compute_max(ctx, tab, max, level); + if (res == isl_lp_error) + goto error; + if (res != isl_lp_ok) + isl_die(ctx, isl_error_internal, + "expecting bounded rational solution", + goto error); + if (isl_tab_sample_is_integer(tab)) + break; + choice = isl_int_lt(min->el[level], max->el[level]); + if (choice) { + int g; + g = greedy_search(ctx, tab, min, max, level); + if (g < 0) + goto error; + if (g) + break; + } + if (!reduced && choice && + ctx->opt->gbr != ISL_GBR_NEVER) { + unsigned gbr_only_first; + if (ctx->opt->gbr == ISL_GBR_ONCE) + ctx->opt->gbr = ISL_GBR_NEVER; + tab->n_zero = level; + gbr_only_first = ctx->opt->gbr_only_first; + ctx->opt->gbr_only_first = + ctx->opt->gbr == ISL_GBR_ALWAYS; + tab = isl_tab_compute_reduced_basis(tab); + ctx->opt->gbr_only_first = gbr_only_first; + if (!tab || !tab->basis) + goto error; + reduced = 1; + continue; + } + reduced = 0; + snap[level] = isl_tab_snap(tab); + } else + isl_int_add_ui(min->el[level], min->el[level], 1); + + if (isl_int_gt(min->el[level], max->el[level])) { + level--; + init = 0; + if (level >= 0) + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + continue; + } + isl_int_neg(tab->basis->row[1 + level][0], min->el[level]); + if (isl_tab_add_valid_eq(tab, tab->basis->row[1 + level]) < 0) + goto error; + isl_int_set_si(tab->basis->row[1 + level][0], 0); + if (level + tab->n_unbounded < dim - 1) { + ++level; + init = 1; + continue; + } + break; + } + + if (level >= 0) { + sample = isl_tab_get_sample_value(tab); + if (!sample) + goto error; + if (tab->n_unbounded && !isl_int_is_one(sample->el[0])) { + sample = isl_mat_vec_product(isl_mat_copy(tab->basis), + sample); + sample = isl_vec_ceil(sample); + sample = isl_mat_vec_inverse_product( + isl_mat_copy(tab->basis), sample); + } + } else + sample = isl_vec_alloc(ctx, 0); + + ctx->opt->gbr = gbr; + isl_vec_free(min); + isl_vec_free(max); + free(snap); + return sample; +error: + ctx->opt->gbr = gbr; + isl_vec_free(min); + isl_vec_free(max); + free(snap); + return NULL; +} + +static struct isl_vec *sample_bounded(struct isl_basic_set *bset); + +/* Compute a sample point of the given basic set, based on the given, + * non-trivial factorization. + */ +static __isl_give isl_vec *factored_sample(__isl_take isl_basic_set *bset, + __isl_take isl_factorizer *f) +{ + int i, n; + isl_vec *sample = NULL; + isl_ctx *ctx; + unsigned nparam; + unsigned nvar; + + ctx = isl_basic_set_get_ctx(bset); + if (!ctx) + goto error; + + nparam = isl_basic_set_dim(bset, isl_dim_param); + nvar = isl_basic_set_dim(bset, isl_dim_set); + + sample = isl_vec_alloc(ctx, 1 + isl_basic_set_total_dim(bset)); + if (!sample) + goto error; + isl_int_set_si(sample->el[0], 1); + + bset = isl_morph_basic_set(isl_morph_copy(f->morph), bset); + + for (i = 0, n = 0; i < f->n_group; ++i) { + isl_basic_set *bset_i; + isl_vec *sample_i; + + bset_i = isl_basic_set_copy(bset); + bset_i = isl_basic_set_drop_constraints_involving(bset_i, + nparam + n + f->len[i], nvar - n - f->len[i]); + bset_i = isl_basic_set_drop_constraints_involving(bset_i, + nparam, n); + bset_i = isl_basic_set_drop(bset_i, isl_dim_set, + n + f->len[i], nvar - n - f->len[i]); + bset_i = isl_basic_set_drop(bset_i, isl_dim_set, 0, n); + + sample_i = sample_bounded(bset_i); + if (!sample_i) + goto error; + if (sample_i->size == 0) { + isl_basic_set_free(bset); + isl_factorizer_free(f); + isl_vec_free(sample); + return sample_i; + } + isl_seq_cpy(sample->el + 1 + nparam + n, + sample_i->el + 1, f->len[i]); + isl_vec_free(sample_i); + + n += f->len[i]; + } + + f->morph = isl_morph_inverse(f->morph); + sample = isl_morph_vec(isl_morph_copy(f->morph), sample); + + isl_basic_set_free(bset); + isl_factorizer_free(f); + return sample; +error: + isl_basic_set_free(bset); + isl_factorizer_free(f); + isl_vec_free(sample); + return NULL; +} + +/* Given a basic set that is known to be bounded, find and return + * an integer point in the basic set, if there is any. + * + * After handling some trivial cases, we construct a tableau + * and then use isl_tab_sample to find a sample, passing it + * the identity matrix as initial basis. + */ +static struct isl_vec *sample_bounded(struct isl_basic_set *bset) +{ + unsigned dim; + struct isl_vec *sample; + struct isl_tab *tab = NULL; + isl_factorizer *f; + + if (!bset) + return NULL; + + if (isl_basic_set_plain_is_empty(bset)) + return empty_sample(bset); + + dim = isl_basic_set_total_dim(bset); + if (dim == 0) + return zero_sample(bset); + if (dim == 1) + return interval_sample(bset); + if (bset->n_eq > 0) + return sample_eq(bset, sample_bounded); + + f = isl_basic_set_factorizer(bset); + if (!f) + goto error; + if (f->n_group != 0) + return factored_sample(bset, f); + isl_factorizer_free(f); + + tab = isl_tab_from_basic_set(bset, 1); + if (tab && tab->empty) { + isl_tab_free(tab); + ISL_F_SET(bset, ISL_BASIC_SET_EMPTY); + sample = isl_vec_alloc(isl_basic_set_get_ctx(bset), 0); + isl_basic_set_free(bset); + return sample; + } + + if (!ISL_F_ISSET(bset, ISL_BASIC_SET_NO_IMPLICIT)) + if (isl_tab_detect_implicit_equalities(tab) < 0) + goto error; + + sample = isl_tab_sample(tab); + if (!sample) + goto error; + + if (sample->size > 0) { + isl_vec_free(bset->sample); + bset->sample = isl_vec_copy(sample); + } + + isl_basic_set_free(bset); + isl_tab_free(tab); + return sample; +error: + isl_basic_set_free(bset); + isl_tab_free(tab); + return NULL; +} + +/* Given a basic set "bset" and a value "sample" for the first coordinates + * of bset, plug in these values and drop the corresponding coordinates. + * + * We do this by computing the preimage of the transformation + * + * [ 1 0 ] + * x = [ s 0 ] x' + * [ 0 I ] + * + * where [1 s] is the sample value and I is the identity matrix of the + * appropriate dimension. + */ +static struct isl_basic_set *plug_in(struct isl_basic_set *bset, + struct isl_vec *sample) +{ + int i; + unsigned total; + struct isl_mat *T; + + if (!bset || !sample) + goto error; + + total = isl_basic_set_total_dim(bset); + T = isl_mat_alloc(bset->ctx, 1 + total, 1 + total - (sample->size - 1)); + if (!T) + goto error; + + for (i = 0; i < sample->size; ++i) { + isl_int_set(T->row[i][0], sample->el[i]); + isl_seq_clr(T->row[i] + 1, T->n_col - 1); + } + for (i = 0; i < T->n_col - 1; ++i) { + isl_seq_clr(T->row[sample->size + i], T->n_col); + isl_int_set_si(T->row[sample->size + i][1 + i], 1); + } + isl_vec_free(sample); + + bset = isl_basic_set_preimage(bset, T); + return bset; +error: + isl_basic_set_free(bset); + isl_vec_free(sample); + return NULL; +} + +/* Given a basic set "bset", return any (possibly non-integer) point + * in the basic set. + */ +static struct isl_vec *rational_sample(struct isl_basic_set *bset) +{ + struct isl_tab *tab; + struct isl_vec *sample; + + if (!bset) + return NULL; + + tab = isl_tab_from_basic_set(bset, 0); + sample = isl_tab_get_sample_value(tab); + isl_tab_free(tab); + + isl_basic_set_free(bset); + + return sample; +} + +/* Given a linear cone "cone" and a rational point "vec", + * construct a polyhedron with shifted copies of the constraints in "cone", + * i.e., a polyhedron with "cone" as its recession cone, such that each + * point x in this polyhedron is such that the unit box positioned at x + * lies entirely inside the affine cone 'vec + cone'. + * Any rational point in this polyhedron may therefore be rounded up + * to yield an integer point that lies inside said affine cone. + * + * Denote the constraints of cone by " >= 0" and the rational + * point "vec" by v/d. + * Let b_i = . Then the affine cone 'vec + cone' is given + * by - b/d >= 0. + * The polyhedron - ceil{b/d} >= 0 is a subset of this affine cone. + * We prefer this polyhedron over the actual affine cone because it doesn't + * require a scaling of the constraints. + * If each of the vertices of the unit cube positioned at x lies inside + * this polyhedron, then the whole unit cube at x lies inside the affine cone. + * We therefore impose that x' = x + \sum e_i, for any selection of unit + * vectors lies inside the polyhedron, i.e., + * + * - ceil{b/d} = + sum a_i - ceil{b/d} >= 0 + * + * The most stringent of these constraints is the one that selects + * all negative a_i, so the polyhedron we are looking for has constraints + * + * + sum_{a_i < 0} a_i - ceil{b/d} >= 0 + * + * Note that if cone were known to have only non-negative rays + * (which can be accomplished by a unimodular transformation), + * then we would only have to check the points x' = x + e_i + * and we only have to add the smallest negative a_i (if any) + * instead of the sum of all negative a_i. + */ +static struct isl_basic_set *shift_cone(struct isl_basic_set *cone, + struct isl_vec *vec) +{ + int i, j, k; + unsigned total; + + struct isl_basic_set *shift = NULL; + + if (!cone || !vec) + goto error; + + isl_assert(cone->ctx, cone->n_eq == 0, goto error); + + total = isl_basic_set_total_dim(cone); + + shift = isl_basic_set_alloc_space(isl_basic_set_get_space(cone), + 0, 0, cone->n_ineq); + + for (i = 0; i < cone->n_ineq; ++i) { + k = isl_basic_set_alloc_inequality(shift); + if (k < 0) + goto error; + isl_seq_cpy(shift->ineq[k] + 1, cone->ineq[i] + 1, total); + isl_seq_inner_product(shift->ineq[k] + 1, vec->el + 1, total, + &shift->ineq[k][0]); + isl_int_cdiv_q(shift->ineq[k][0], + shift->ineq[k][0], vec->el[0]); + isl_int_neg(shift->ineq[k][0], shift->ineq[k][0]); + for (j = 0; j < total; ++j) { + if (isl_int_is_nonneg(shift->ineq[k][1 + j])) + continue; + isl_int_add(shift->ineq[k][0], + shift->ineq[k][0], shift->ineq[k][1 + j]); + } + } + + isl_basic_set_free(cone); + isl_vec_free(vec); + + return isl_basic_set_finalize(shift); +error: + isl_basic_set_free(shift); + isl_basic_set_free(cone); + isl_vec_free(vec); + return NULL; +} + +/* Given a rational point vec in a (transformed) basic set, + * such that cone is the recession cone of the original basic set, + * "round up" the rational point to an integer point. + * + * We first check if the rational point just happens to be integer. + * If not, we transform the cone in the same way as the basic set, + * pick a point x in this cone shifted to the rational point such that + * the whole unit cube at x is also inside this affine cone. + * Then we simply round up the coordinates of x and return the + * resulting integer point. + */ +static struct isl_vec *round_up_in_cone(struct isl_vec *vec, + struct isl_basic_set *cone, struct isl_mat *U) +{ + unsigned total; + + if (!vec || !cone || !U) + goto error; + + isl_assert(vec->ctx, vec->size != 0, goto error); + if (isl_int_is_one(vec->el[0])) { + isl_mat_free(U); + isl_basic_set_free(cone); + return vec; + } + + total = isl_basic_set_total_dim(cone); + cone = isl_basic_set_preimage(cone, U); + cone = isl_basic_set_remove_dims(cone, isl_dim_set, + 0, total - (vec->size - 1)); + + cone = shift_cone(cone, vec); + + vec = rational_sample(cone); + vec = isl_vec_ceil(vec); + return vec; +error: + isl_mat_free(U); + isl_vec_free(vec); + isl_basic_set_free(cone); + return NULL; +} + +/* Concatenate two integer vectors, i.e., two vectors with denominator + * (stored in element 0) equal to 1. + */ +static struct isl_vec *vec_concat(struct isl_vec *vec1, struct isl_vec *vec2) +{ + struct isl_vec *vec; + + if (!vec1 || !vec2) + goto error; + isl_assert(vec1->ctx, vec1->size > 0, goto error); + isl_assert(vec2->ctx, vec2->size > 0, goto error); + isl_assert(vec1->ctx, isl_int_is_one(vec1->el[0]), goto error); + isl_assert(vec2->ctx, isl_int_is_one(vec2->el[0]), goto error); + + vec = isl_vec_alloc(vec1->ctx, vec1->size + vec2->size - 1); + if (!vec) + goto error; + + isl_seq_cpy(vec->el, vec1->el, vec1->size); + isl_seq_cpy(vec->el + vec1->size, vec2->el + 1, vec2->size - 1); + + isl_vec_free(vec1); + isl_vec_free(vec2); + + return vec; +error: + isl_vec_free(vec1); + isl_vec_free(vec2); + return NULL; +} + +/* Give a basic set "bset" with recession cone "cone", compute and + * return an integer point in bset, if any. + * + * If the recession cone is full-dimensional, then we know that + * bset contains an infinite number of integer points and it is + * fairly easy to pick one of them. + * If the recession cone is not full-dimensional, then we first + * transform bset such that the bounded directions appear as + * the first dimensions of the transformed basic set. + * We do this by using a unimodular transformation that transforms + * the equalities in the recession cone to equalities on the first + * dimensions. + * + * The transformed set is then projected onto its bounded dimensions. + * Note that to compute this projection, we can simply drop all constraints + * involving any of the unbounded dimensions since these constraints + * cannot be combined to produce a constraint on the bounded dimensions. + * To see this, assume that there is such a combination of constraints + * that produces a constraint on the bounded dimensions. This means + * that some combination of the unbounded dimensions has both an upper + * bound and a lower bound in terms of the bounded dimensions, but then + * this combination would be a bounded direction too and would have been + * transformed into a bounded dimensions. + * + * We then compute a sample value in the bounded dimensions. + * If no such value can be found, then the original set did not contain + * any integer points and we are done. + * Otherwise, we plug in the value we found in the bounded dimensions, + * project out these bounded dimensions and end up with a set with + * a full-dimensional recession cone. + * A sample point in this set is computed by "rounding up" any + * rational point in the set. + * + * The sample points in the bounded and unbounded dimensions are + * then combined into a single sample point and transformed back + * to the original space. + */ +__isl_give isl_vec *isl_basic_set_sample_with_cone( + __isl_take isl_basic_set *bset, __isl_take isl_basic_set *cone) +{ + unsigned total; + unsigned cone_dim; + struct isl_mat *M, *U; + struct isl_vec *sample; + struct isl_vec *cone_sample; + struct isl_ctx *ctx; + struct isl_basic_set *bounded; + + if (!bset || !cone) + goto error; + + ctx = isl_basic_set_get_ctx(bset); + total = isl_basic_set_total_dim(cone); + cone_dim = total - cone->n_eq; + + M = isl_mat_sub_alloc6(ctx, cone->eq, 0, cone->n_eq, 1, total); + M = isl_mat_left_hermite(M, 0, &U, NULL); + if (!M) + goto error; + isl_mat_free(M); + + U = isl_mat_lin_to_aff(U); + bset = isl_basic_set_preimage(bset, isl_mat_copy(U)); + + bounded = isl_basic_set_copy(bset); + bounded = isl_basic_set_drop_constraints_involving(bounded, + total - cone_dim, cone_dim); + bounded = isl_basic_set_drop_dims(bounded, total - cone_dim, cone_dim); + sample = sample_bounded(bounded); + if (!sample || sample->size == 0) { + isl_basic_set_free(bset); + isl_basic_set_free(cone); + isl_mat_free(U); + return sample; + } + bset = plug_in(bset, isl_vec_copy(sample)); + cone_sample = rational_sample(bset); + cone_sample = round_up_in_cone(cone_sample, cone, isl_mat_copy(U)); + sample = vec_concat(sample, cone_sample); + sample = isl_mat_vec_product(U, sample); + return sample; +error: + isl_basic_set_free(cone); + isl_basic_set_free(bset); + return NULL; +} + +static void vec_sum_of_neg(struct isl_vec *v, isl_int *s) +{ + int i; + + isl_int_set_si(*s, 0); + + for (i = 0; i < v->size; ++i) + if (isl_int_is_neg(v->el[i])) + isl_int_add(*s, *s, v->el[i]); +} + +/* Given a tableau "tab", a tableau "tab_cone" that corresponds + * to the recession cone and the inverse of a new basis U = inv(B), + * with the unbounded directions in B last, + * add constraints to "tab" that ensure any rational value + * in the unbounded directions can be rounded up to an integer value. + * + * The new basis is given by x' = B x, i.e., x = U x'. + * For any rational value of the last tab->n_unbounded coordinates + * in the update tableau, the value that is obtained by rounding + * up this value should be contained in the original tableau. + * For any constraint "a x + c >= 0", we therefore need to add + * a constraint "a x + c + s >= 0", with s the sum of all negative + * entries in the last elements of "a U". + * + * Since we are not interested in the first entries of any of the "a U", + * we first drop the columns of U that correpond to bounded directions. + */ +static int tab_shift_cone(struct isl_tab *tab, + struct isl_tab *tab_cone, struct isl_mat *U) +{ + int i; + isl_int v; + struct isl_basic_set *bset = NULL; + + if (tab && tab->n_unbounded == 0) { + isl_mat_free(U); + return 0; + } + isl_int_init(v); + if (!tab || !tab_cone || !U) + goto error; + bset = isl_tab_peek_bset(tab_cone); + U = isl_mat_drop_cols(U, 0, tab->n_var - tab->n_unbounded); + for (i = 0; i < bset->n_ineq; ++i) { + int ok; + struct isl_vec *row = NULL; + if (isl_tab_is_equality(tab_cone, tab_cone->n_eq + i)) + continue; + row = isl_vec_alloc(bset->ctx, tab_cone->n_var); + if (!row) + goto error; + isl_seq_cpy(row->el, bset->ineq[i] + 1, tab_cone->n_var); + row = isl_vec_mat_product(row, isl_mat_copy(U)); + if (!row) + goto error; + vec_sum_of_neg(row, &v); + isl_vec_free(row); + if (isl_int_is_zero(v)) + continue; + if (isl_tab_extend_cons(tab, 1) < 0) + goto error; + isl_int_add(bset->ineq[i][0], bset->ineq[i][0], v); + ok = isl_tab_add_ineq(tab, bset->ineq[i]) >= 0; + isl_int_sub(bset->ineq[i][0], bset->ineq[i][0], v); + if (!ok) + goto error; + } + + isl_mat_free(U); + isl_int_clear(v); + return 0; +error: + isl_mat_free(U); + isl_int_clear(v); + return -1; +} + +/* Compute and return an initial basis for the possibly + * unbounded tableau "tab". "tab_cone" is a tableau + * for the corresponding recession cone. + * Additionally, add constraints to "tab" that ensure + * that any rational value for the unbounded directions + * can be rounded up to an integer value. + * + * If the tableau is bounded, i.e., if the recession cone + * is zero-dimensional, then we just use inital_basis. + * Otherwise, we construct a basis whose first directions + * correspond to equalities, followed by bounded directions, + * i.e., equalities in the recession cone. + * The remaining directions are then unbounded. + */ +int isl_tab_set_initial_basis_with_cone(struct isl_tab *tab, + struct isl_tab *tab_cone) +{ + struct isl_mat *eq; + struct isl_mat *cone_eq; + struct isl_mat *U, *Q; + + if (!tab || !tab_cone) + return -1; + + if (tab_cone->n_col == tab_cone->n_dead) { + tab->basis = initial_basis(tab); + return tab->basis ? 0 : -1; + } + + eq = tab_equalities(tab); + if (!eq) + return -1; + tab->n_zero = eq->n_row; + cone_eq = tab_equalities(tab_cone); + eq = isl_mat_concat(eq, cone_eq); + if (!eq) + return -1; + tab->n_unbounded = tab->n_var - (eq->n_row - tab->n_zero); + eq = isl_mat_left_hermite(eq, 0, &U, &Q); + if (!eq) + return -1; + isl_mat_free(eq); + tab->basis = isl_mat_lin_to_aff(Q); + if (tab_shift_cone(tab, tab_cone, U) < 0) + return -1; + if (!tab->basis) + return -1; + return 0; +} + +/* Compute and return a sample point in bset using generalized basis + * reduction. We first check if the input set has a non-trivial + * recession cone. If so, we perform some extra preprocessing in + * sample_with_cone. Otherwise, we directly perform generalized basis + * reduction. + */ +static struct isl_vec *gbr_sample(struct isl_basic_set *bset) +{ + unsigned dim; + struct isl_basic_set *cone; + + dim = isl_basic_set_total_dim(bset); + + cone = isl_basic_set_recession_cone(isl_basic_set_copy(bset)); + if (!cone) + goto error; + + if (cone->n_eq < dim) + return isl_basic_set_sample_with_cone(bset, cone); + + isl_basic_set_free(cone); + return sample_bounded(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +static struct isl_vec *basic_set_sample(struct isl_basic_set *bset, int bounded) +{ + struct isl_ctx *ctx; + unsigned dim; + if (!bset) + return NULL; + + ctx = bset->ctx; + if (isl_basic_set_plain_is_empty(bset)) + return empty_sample(bset); + + dim = isl_basic_set_n_dim(bset); + isl_assert(ctx, isl_basic_set_n_param(bset) == 0, goto error); + isl_assert(ctx, bset->n_div == 0, goto error); + + if (bset->sample && bset->sample->size == 1 + dim) { + int contains = isl_basic_set_contains(bset, bset->sample); + if (contains < 0) + goto error; + if (contains) { + struct isl_vec *sample = isl_vec_copy(bset->sample); + isl_basic_set_free(bset); + return sample; + } + } + isl_vec_free(bset->sample); + bset->sample = NULL; + + if (bset->n_eq > 0) + return sample_eq(bset, bounded ? isl_basic_set_sample_bounded + : isl_basic_set_sample_vec); + if (dim == 0) + return zero_sample(bset); + if (dim == 1) + return interval_sample(bset); + + return bounded ? sample_bounded(bset) : gbr_sample(bset); +error: + isl_basic_set_free(bset); + return NULL; +} + +__isl_give isl_vec *isl_basic_set_sample_vec(__isl_take isl_basic_set *bset) +{ + return basic_set_sample(bset, 0); +} + +/* Compute an integer sample in "bset", where the caller guarantees + * that "bset" is bounded. + */ +struct isl_vec *isl_basic_set_sample_bounded(struct isl_basic_set *bset) +{ + return basic_set_sample(bset, 1); +} + +__isl_give isl_basic_set *isl_basic_set_from_vec(__isl_take isl_vec *vec) +{ + int i; + int k; + struct isl_basic_set *bset = NULL; + struct isl_ctx *ctx; + unsigned dim; + + if (!vec) + return NULL; + ctx = vec->ctx; + isl_assert(ctx, vec->size != 0, goto error); + + bset = isl_basic_set_alloc(ctx, 0, vec->size - 1, 0, vec->size - 1, 0); + if (!bset) + goto error; + dim = isl_basic_set_n_dim(bset); + for (i = dim - 1; i >= 0; --i) { + k = isl_basic_set_alloc_equality(bset); + if (k < 0) + goto error; + isl_seq_clr(bset->eq[k], 1 + dim); + isl_int_neg(bset->eq[k][0], vec->el[1 + i]); + isl_int_set(bset->eq[k][1 + i], vec->el[0]); + } + bset->sample = vec; + + return bset; +error: + isl_basic_set_free(bset); + isl_vec_free(vec); + return NULL; +} + +__isl_give isl_basic_map *isl_basic_map_sample(__isl_take isl_basic_map *bmap) +{ + struct isl_basic_set *bset; + struct isl_vec *sample_vec; + + bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap)); + sample_vec = isl_basic_set_sample_vec(bset); + if (!sample_vec) + goto error; + if (sample_vec->size == 0) { + isl_vec_free(sample_vec); + return isl_basic_map_set_to_empty(bmap); + } + isl_vec_free(bmap->sample); + bmap->sample = isl_vec_copy(sample_vec); + bset = isl_basic_set_from_vec(sample_vec); + return isl_basic_map_overlying_set(bset, bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +__isl_give isl_basic_set *isl_basic_set_sample(__isl_take isl_basic_set *bset) +{ + return isl_basic_map_sample(bset); +} + +__isl_give isl_basic_map *isl_map_sample(__isl_take isl_map *map) +{ + int i; + isl_basic_map *sample = NULL; + + if (!map) + goto error; + + for (i = 0; i < map->n; ++i) { + sample = isl_basic_map_sample(isl_basic_map_copy(map->p[i])); + if (!sample) + goto error; + if (!ISL_F_ISSET(sample, ISL_BASIC_MAP_EMPTY)) + break; + isl_basic_map_free(sample); + } + if (i == map->n) + sample = isl_basic_map_empty(isl_map_get_space(map)); + isl_map_free(map); + return sample; +error: + isl_map_free(map); + return NULL; +} + +__isl_give isl_basic_set *isl_set_sample(__isl_take isl_set *set) +{ + return (isl_basic_set *) isl_map_sample((isl_map *)set); +} + +__isl_give isl_point *isl_basic_set_sample_point(__isl_take isl_basic_set *bset) +{ + isl_vec *vec; + isl_space *dim; + + dim = isl_basic_set_get_space(bset); + bset = isl_basic_set_underlying_set(bset); + vec = isl_basic_set_sample_vec(bset); + + return isl_point_alloc(dim, vec); +} + +__isl_give isl_point *isl_set_sample_point(__isl_take isl_set *set) +{ + int i; + isl_point *pnt; + + if (!set) + return NULL; + + for (i = 0; i < set->n; ++i) { + pnt = isl_basic_set_sample_point(isl_basic_set_copy(set->p[i])); + if (!pnt) + goto error; + if (!isl_point_is_void(pnt)) + break; + isl_point_free(pnt); + } + if (i == set->n) + pnt = isl_point_void(isl_set_get_space(set)); + + isl_set_free(set); + return pnt; +error: + isl_set_free(set); + return NULL; +} Index: lib/Analysis/isl/isl_scan.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_scan.h @@ -0,0 +1,25 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_SCAN_H +#define ISL_SCAN_H + +#include +#include + +struct isl_scan_callback { + isl_stat (*add)(struct isl_scan_callback *cb, + __isl_take isl_vec *sample); +}; + +int isl_basic_set_scan(struct isl_basic_set *bset, + struct isl_scan_callback *callback); +int isl_set_scan(__isl_take isl_set *set, struct isl_scan_callback *callback); + +#endif Index: lib/Analysis/isl/isl_scan.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_scan.c @@ -0,0 +1,324 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include "isl_basis_reduction.h" +#include "isl_scan.h" +#include +#include "isl_tab.h" +#include +#include + +struct isl_counter { + struct isl_scan_callback callback; + isl_int count; + isl_int max; +}; + +static isl_stat increment_counter(struct isl_scan_callback *cb, + __isl_take isl_vec *sample) +{ + struct isl_counter *cnt = (struct isl_counter *)cb; + + isl_int_add_ui(cnt->count, cnt->count, 1); + + isl_vec_free(sample); + + if (isl_int_is_zero(cnt->max) || isl_int_lt(cnt->count, cnt->max)) + return isl_stat_ok; + return isl_stat_error; +} + +static int increment_range(struct isl_scan_callback *cb, isl_int min, isl_int max) +{ + struct isl_counter *cnt = (struct isl_counter *)cb; + + isl_int_add(cnt->count, cnt->count, max); + isl_int_sub(cnt->count, cnt->count, min); + isl_int_add_ui(cnt->count, cnt->count, 1); + + if (isl_int_is_zero(cnt->max) || isl_int_lt(cnt->count, cnt->max)) + return 0; + isl_int_set(cnt->count, cnt->max); + return -1; +} + +/* Call callback->add with the current sample value of the tableau "tab". + */ +static int add_solution(struct isl_tab *tab, struct isl_scan_callback *callback) +{ + struct isl_vec *sample; + + if (!tab) + return -1; + sample = isl_tab_get_sample_value(tab); + if (!sample) + return -1; + + return callback->add(callback, sample); +} + +static int scan_0D(struct isl_basic_set *bset, + struct isl_scan_callback *callback) +{ + struct isl_vec *sample; + + sample = isl_vec_alloc(bset->ctx, 1); + isl_basic_set_free(bset); + + if (!sample) + return -1; + + isl_int_set_si(sample->el[0], 1); + + return callback->add(callback, sample); +} + +/* Look for all integer points in "bset", which is assumed to be bounded, + * and call callback->add on each of them. + * + * We first compute a reduced basis for the set and then scan + * the set in the directions of this basis. + * We basically perform a depth first search, where in each level i + * we compute the range in the i-th basis vector direction, given + * fixed values in the directions of the previous basis vector. + * We then add an equality to the tableau fixing the value in the + * direction of the current basis vector to each value in the range + * in turn and then continue to the next level. + * + * The search is implemented iteratively. "level" identifies the current + * basis vector. "init" is true if we want the first value at the current + * level and false if we want the next value. + * Solutions are added in the leaves of the search tree, i.e., after + * we have fixed a value in each direction of the basis. + */ +int isl_basic_set_scan(struct isl_basic_set *bset, + struct isl_scan_callback *callback) +{ + unsigned dim; + struct isl_mat *B = NULL; + struct isl_tab *tab = NULL; + struct isl_vec *min; + struct isl_vec *max; + struct isl_tab_undo **snap; + int level; + int init; + enum isl_lp_result res; + + if (!bset) + return -1; + + dim = isl_basic_set_total_dim(bset); + if (dim == 0) + return scan_0D(bset, callback); + + min = isl_vec_alloc(bset->ctx, dim); + max = isl_vec_alloc(bset->ctx, dim); + snap = isl_alloc_array(bset->ctx, struct isl_tab_undo *, dim); + + if (!min || !max || !snap) + goto error; + + tab = isl_tab_from_basic_set(bset, 0); + if (!tab) + goto error; + if (isl_tab_extend_cons(tab, dim + 1) < 0) + goto error; + + tab->basis = isl_mat_identity(bset->ctx, 1 + dim); + if (1) + tab = isl_tab_compute_reduced_basis(tab); + if (!tab) + goto error; + B = isl_mat_copy(tab->basis); + if (!B) + goto error; + + level = 0; + init = 1; + + while (level >= 0) { + int empty = 0; + if (init) { + res = isl_tab_min(tab, B->row[1 + level], + bset->ctx->one, &min->el[level], NULL, 0); + if (res == isl_lp_empty) + empty = 1; + if (res == isl_lp_error || res == isl_lp_unbounded) + goto error; + isl_seq_neg(B->row[1 + level] + 1, + B->row[1 + level] + 1, dim); + res = isl_tab_min(tab, B->row[1 + level], + bset->ctx->one, &max->el[level], NULL, 0); + isl_seq_neg(B->row[1 + level] + 1, + B->row[1 + level] + 1, dim); + isl_int_neg(max->el[level], max->el[level]); + if (res == isl_lp_empty) + empty = 1; + if (res == isl_lp_error || res == isl_lp_unbounded) + goto error; + snap[level] = isl_tab_snap(tab); + } else + isl_int_add_ui(min->el[level], min->el[level], 1); + + if (empty || isl_int_gt(min->el[level], max->el[level])) { + level--; + init = 0; + if (level >= 0) + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + continue; + } + if (level == dim - 1 && callback->add == increment_counter) { + if (increment_range(callback, + min->el[level], max->el[level])) + goto error; + level--; + init = 0; + if (level >= 0) + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + continue; + } + isl_int_neg(B->row[1 + level][0], min->el[level]); + if (isl_tab_add_valid_eq(tab, B->row[1 + level]) < 0) + goto error; + isl_int_set_si(B->row[1 + level][0], 0); + if (level < dim - 1) { + ++level; + init = 1; + continue; + } + if (add_solution(tab, callback) < 0) + goto error; + init = 0; + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + } + + isl_tab_free(tab); + free(snap); + isl_vec_free(min); + isl_vec_free(max); + isl_basic_set_free(bset); + isl_mat_free(B); + return 0; +error: + isl_tab_free(tab); + free(snap); + isl_vec_free(min); + isl_vec_free(max); + isl_basic_set_free(bset); + isl_mat_free(B); + return -1; +} + +int isl_set_scan(__isl_take isl_set *set, struct isl_scan_callback *callback) +{ + int i; + + if (!set || !callback) + goto error; + + set = isl_set_cow(set); + set = isl_set_make_disjoint(set); + set = isl_set_compute_divs(set); + if (!set) + goto error; + + for (i = 0; i < set->n; ++i) + if (isl_basic_set_scan(isl_basic_set_copy(set->p[i]), + callback) < 0) + goto error; + + isl_set_free(set); + return 0; +error: + isl_set_free(set); + return -1; +} + +int isl_basic_set_count_upto(__isl_keep isl_basic_set *bset, + isl_int max, isl_int *count) +{ + struct isl_counter cnt = { { &increment_counter } }; + + if (!bset) + return -1; + + isl_int_init(cnt.count); + isl_int_init(cnt.max); + + isl_int_set_si(cnt.count, 0); + isl_int_set(cnt.max, max); + if (isl_basic_set_scan(isl_basic_set_copy(bset), &cnt.callback) < 0 && + isl_int_lt(cnt.count, cnt.max)) + goto error; + + isl_int_set(*count, cnt.count); + isl_int_clear(cnt.max); + isl_int_clear(cnt.count); + + return 0; +error: + isl_int_clear(cnt.count); + return -1; +} + +int isl_set_count_upto(__isl_keep isl_set *set, isl_int max, isl_int *count) +{ + struct isl_counter cnt = { { &increment_counter } }; + + if (!set) + return -1; + + isl_int_init(cnt.count); + isl_int_init(cnt.max); + + isl_int_set_si(cnt.count, 0); + isl_int_set(cnt.max, max); + if (isl_set_scan(isl_set_copy(set), &cnt.callback) < 0 && + isl_int_lt(cnt.count, cnt.max)) + goto error; + + isl_int_set(*count, cnt.count); + isl_int_clear(cnt.max); + isl_int_clear(cnt.count); + + return 0; +error: + isl_int_clear(cnt.count); + return -1; +} + +int isl_set_count(__isl_keep isl_set *set, isl_int *count) +{ + if (!set) + return -1; + return isl_set_count_upto(set, set->ctx->zero, count); +} + +/* Count the total number of elements in "set" (in an inefficient way) and + * return the result. + */ +__isl_give isl_val *isl_set_count_val(__isl_keep isl_set *set) +{ + isl_val *v; + + if (!set) + return NULL; + v = isl_val_zero(isl_set_get_ctx(set)); + v = isl_val_cow(v); + if (!v) + return NULL; + if (isl_set_count(set, &v->n) < 0) + v = isl_val_free(v); + return v; +} Index: lib/Analysis/isl/isl_schedule.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule.c @@ -0,0 +1,1185 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2012-2014 Ecole Normale Superieure + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Return a schedule encapsulating the given schedule tree. + * + * We currently only allow schedule trees with a domain or extension as root. + * + * The leaf field is initialized as a leaf node so that it can be + * used to represent leaves in the constructed schedule. + * The reference count is set to -1 since the isl_schedule_tree + * should never be freed. It is up to the (internal) users of + * these leaves to ensure that they are only used while the schedule + * is still alive. + */ +__isl_give isl_schedule *isl_schedule_from_schedule_tree(isl_ctx *ctx, + __isl_take isl_schedule_tree *tree) +{ + enum isl_schedule_node_type type; + isl_schedule *schedule; + + if (!tree) + return NULL; + type = isl_schedule_tree_get_type(tree); + if (type != isl_schedule_node_domain && + type != isl_schedule_node_extension) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported, + "root of schedule tree should be a domain or extension", + goto error); + + schedule = isl_calloc_type(ctx, isl_schedule); + if (!schedule) + goto error; + + schedule->ref = 1; + schedule->root = tree; + schedule->leaf = isl_schedule_tree_leaf(ctx); + + if (!schedule->leaf) + return isl_schedule_free(schedule); + return schedule; +error: + isl_schedule_tree_free(tree); + return NULL; +} + +/* Return a pointer to a schedule with as single node + * a domain node with the given domain. + */ +__isl_give isl_schedule *isl_schedule_from_domain( + __isl_take isl_union_set *domain) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + ctx = isl_union_set_get_ctx(domain); + tree = isl_schedule_tree_from_domain(domain); + return isl_schedule_from_schedule_tree(ctx, tree); +} + +/* Return a pointer to a schedule with as single node + * a domain node with an empty domain. + */ +__isl_give isl_schedule *isl_schedule_empty(__isl_take isl_space *space) +{ + return isl_schedule_from_domain(isl_union_set_empty(space)); +} + +/* Return a new reference to "sched". + */ +__isl_give isl_schedule *isl_schedule_copy(__isl_keep isl_schedule *sched) +{ + if (!sched) + return NULL; + + sched->ref++; + return sched; +} + +/* Return an isl_schedule that is equal to "schedule" and that has only + * a single reference. + * + * We only need and support this function when the schedule is represented + * as a schedule tree. + */ +__isl_give isl_schedule *isl_schedule_cow(__isl_take isl_schedule *schedule) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!schedule) + return NULL; + if (schedule->ref == 1) + return schedule; + + ctx = isl_schedule_get_ctx(schedule); + if (!schedule->root) + isl_die(ctx, isl_error_internal, + "only for schedule tree based schedules", + return isl_schedule_free(schedule)); + schedule->ref--; + tree = isl_schedule_tree_copy(schedule->root); + return isl_schedule_from_schedule_tree(ctx, tree); +} + +__isl_null isl_schedule *isl_schedule_free(__isl_take isl_schedule *sched) +{ + if (!sched) + return NULL; + + if (--sched->ref > 0) + return NULL; + + isl_band_list_free(sched->band_forest); + isl_schedule_tree_free(sched->root); + isl_schedule_tree_free(sched->leaf); + free(sched); + return NULL; +} + +/* Replace the root of "schedule" by "tree". + */ +__isl_give isl_schedule *isl_schedule_set_root( + __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree) +{ + if (!schedule || !tree) + goto error; + if (schedule->root == tree) { + isl_schedule_tree_free(tree); + return schedule; + } + + schedule = isl_schedule_cow(schedule); + if (!schedule) + goto error; + isl_schedule_tree_free(schedule->root); + schedule->root = tree; + + return schedule; +error: + isl_schedule_free(schedule); + isl_schedule_tree_free(tree); + return NULL; +} + +isl_ctx *isl_schedule_get_ctx(__isl_keep isl_schedule *schedule) +{ + return schedule ? isl_schedule_tree_get_ctx(schedule->leaf) : NULL; +} + +/* Return a pointer to the leaf of "schedule". + */ +__isl_keep isl_schedule_tree *isl_schedule_peek_leaf( + __isl_keep isl_schedule *schedule) +{ + return schedule ? schedule->leaf : NULL; +} + +/* Are "schedule1" and "schedule2" obviously equal to each other? + */ +isl_bool isl_schedule_plain_is_equal(__isl_keep isl_schedule *schedule1, + __isl_keep isl_schedule *schedule2) +{ + if (!schedule1 || !schedule2) + return isl_bool_error; + if (schedule1 == schedule2) + return isl_bool_true; + return isl_schedule_tree_plain_is_equal(schedule1->root, + schedule2->root); +} + +/* Return the (parameter) space of the schedule, i.e., the space + * of the root domain. + */ +__isl_give isl_space *isl_schedule_get_space( + __isl_keep isl_schedule *schedule) +{ + enum isl_schedule_node_type type; + isl_space *space; + isl_union_set *domain; + + if (!schedule) + return NULL; + if (!schedule->root) + isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid, + "schedule tree representation not available", + return NULL); + type = isl_schedule_tree_get_type(schedule->root); + if (type != isl_schedule_node_domain) + isl_die(isl_schedule_get_ctx(schedule), isl_error_internal, + "root node not a domain node", return NULL); + + domain = isl_schedule_tree_domain_get_domain(schedule->root); + space = isl_union_set_get_space(domain); + isl_union_set_free(domain); + + return space; +} + +/* Return a pointer to the root of "schedule". + */ +__isl_give isl_schedule_node *isl_schedule_get_root( + __isl_keep isl_schedule *schedule) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + isl_schedule_tree_list *ancestors; + + if (!schedule) + return NULL; + + if (!schedule->root) + isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid, + "schedule tree representation not available", + return NULL); + + ctx = isl_schedule_get_ctx(schedule); + tree = isl_schedule_tree_copy(schedule->root); + schedule = isl_schedule_copy(schedule); + ancestors = isl_schedule_tree_list_alloc(ctx, 0); + return isl_schedule_node_alloc(schedule, tree, ancestors, NULL); +} + +/* Set max_out to the maximal number of output dimensions over + * all maps. + */ +static isl_stat update_max_out(__isl_take isl_map *map, void *user) +{ + int *max_out = user; + int n_out = isl_map_dim(map, isl_dim_out); + + if (n_out > *max_out) + *max_out = n_out; + + isl_map_free(map); + return isl_stat_ok; +} + +/* Internal data structure for map_pad_range. + * + * "max_out" is the maximal schedule dimension. + * "res" collects the results. + */ +struct isl_pad_schedule_map_data { + int max_out; + isl_union_map *res; +}; + +/* Pad the range of the given map with zeros to data->max_out and + * then add the result to data->res. + */ +static isl_stat map_pad_range(__isl_take isl_map *map, void *user) +{ + struct isl_pad_schedule_map_data *data = user; + int i; + int n_out = isl_map_dim(map, isl_dim_out); + + map = isl_map_add_dims(map, isl_dim_out, data->max_out - n_out); + for (i = n_out; i < data->max_out; ++i) + map = isl_map_fix_si(map, isl_dim_out, i, 0); + + data->res = isl_union_map_add_map(data->res, map); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Pad the ranges of the maps in the union map with zeros such they all have + * the same dimension. + */ +static __isl_give isl_union_map *pad_schedule_map( + __isl_take isl_union_map *umap) +{ + struct isl_pad_schedule_map_data data; + + if (!umap) + return NULL; + if (isl_union_map_n_map(umap) <= 1) + return umap; + + data.max_out = 0; + if (isl_union_map_foreach_map(umap, &update_max_out, &data.max_out) < 0) + return isl_union_map_free(umap); + + data.res = isl_union_map_empty(isl_union_map_get_space(umap)); + if (isl_union_map_foreach_map(umap, &map_pad_range, &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_union_map_free(umap); + return data.res; +} + +/* Return the domain of the root domain node of "schedule". + */ +__isl_give isl_union_set *isl_schedule_get_domain( + __isl_keep isl_schedule *schedule) +{ + if (!schedule) + return NULL; + if (!schedule->root) + isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid, + "schedule tree representation not available", + return NULL); + return isl_schedule_tree_domain_get_domain(schedule->root); +} + +/* Traverse all nodes of "sched" in depth first preorder. + * + * If "fn" returns -1 on any of the nodes, then the traversal is aborted. + * If "fn" returns 0 on any of the nodes, then the subtree rooted + * at that node is skipped. + * + * Return 0 on success and -1 on failure. + */ +isl_stat isl_schedule_foreach_schedule_node_top_down( + __isl_keep isl_schedule *sched, + isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user), + void *user) +{ + isl_schedule_node *node; + isl_stat r; + + if (!sched) + return isl_stat_error; + + node = isl_schedule_get_root(sched); + r = isl_schedule_node_foreach_descendant_top_down(node, fn, user); + isl_schedule_node_free(node); + + return r; +} + +/* Traverse the node of "sched" in depth first postorder, + * allowing the user to modify the visited node. + * The traversal continues from the node returned by the callback function. + * It is the responsibility of the user to ensure that this does not + * lead to an infinite loop. It is safest to always return a pointer + * to the same position (same ancestors and child positions) as the input node. + */ +__isl_give isl_schedule *isl_schedule_map_schedule_node_bottom_up( + __isl_take isl_schedule *schedule, + __isl_give isl_schedule_node *(*fn)( + __isl_take isl_schedule_node *node, void *user), void *user) +{ + isl_schedule_node *node; + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + + node = isl_schedule_node_map_descendant_bottom_up(node, fn, user); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +} + +/* Wrapper around isl_schedule_node_reset_user for use as + * an isl_schedule_map_schedule_node_bottom_up callback. + */ +static __isl_give isl_schedule_node *reset_user( + __isl_take isl_schedule_node *node, void *user) +{ + return isl_schedule_node_reset_user(node); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * in the schedule "schedule". + */ +__isl_give isl_schedule *isl_schedule_reset_user( + __isl_take isl_schedule *schedule) +{ + return isl_schedule_map_schedule_node_bottom_up(schedule, &reset_user, + NULL); +} + +/* Wrapper around isl_schedule_node_align_params for use as + * an isl_schedule_map_schedule_node_bottom_up callback. + */ +static __isl_give isl_schedule_node *align_params( + __isl_take isl_schedule_node *node, void *user) +{ + isl_space *space = user; + + return isl_schedule_node_align_params(node, isl_space_copy(space)); +} + +/* Align the parameters of all nodes in schedule "schedule" + * to those of "space". + */ +__isl_give isl_schedule *isl_schedule_align_params( + __isl_take isl_schedule *schedule, __isl_take isl_space *space) +{ + schedule = isl_schedule_map_schedule_node_bottom_up(schedule, + &align_params, space); + isl_space_free(space); + return schedule; +} + +/* Wrapper around isl_schedule_node_pullback_union_pw_multi_aff for use as + * an isl_schedule_map_schedule_node_bottom_up callback. + */ +static __isl_give isl_schedule_node *pullback_upma( + __isl_take isl_schedule_node *node, void *user) +{ + isl_union_pw_multi_aff *upma = user; + + return isl_schedule_node_pullback_union_pw_multi_aff(node, + isl_union_pw_multi_aff_copy(upma)); +} + +/* Compute the pullback of "schedule" by the function represented by "upma". + * In other words, plug in "upma" in the iteration domains of "schedule". + * + * The schedule tree is not allowed to contain any expansion nodes. + */ +__isl_give isl_schedule *isl_schedule_pullback_union_pw_multi_aff( + __isl_take isl_schedule *schedule, + __isl_take isl_union_pw_multi_aff *upma) +{ + schedule = isl_schedule_map_schedule_node_bottom_up(schedule, + &pullback_upma, upma); + isl_union_pw_multi_aff_free(upma); + return schedule; +} + +/* Expand the schedule "schedule" by extending all leaves + * with an expansion node with as subtree the tree of "expansion". + * The expansion of the expansion node is determined by "contraction" + * and the domain of "expansion". That is, the domain of "expansion" + * is contracted according to "contraction". + * + * Call isl_schedule_node_expand after extracting the required + * information from "expansion". + */ +__isl_give isl_schedule *isl_schedule_expand(__isl_take isl_schedule *schedule, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_schedule *expansion) +{ + isl_union_set *domain; + isl_schedule_node *node; + isl_schedule_tree *tree; + + domain = isl_schedule_get_domain(expansion); + + node = isl_schedule_get_root(expansion); + node = isl_schedule_node_child(node, 0); + tree = isl_schedule_node_get_tree(node); + isl_schedule_node_free(node); + isl_schedule_free(expansion); + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + node = isl_schedule_node_expand(node, contraction, domain, tree); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +} + +/* Intersect the domain of the schedule "schedule" with "domain". + * The root of "schedule" is required to be a domain node. + */ +__isl_give isl_schedule *isl_schedule_intersect_domain( + __isl_take isl_schedule *schedule, __isl_take isl_union_set *domain) +{ + enum isl_schedule_node_type root_type; + isl_schedule_node *node; + + if (!schedule || !domain) + goto error; + + root_type = isl_schedule_tree_get_type(schedule->root); + if (root_type != isl_schedule_node_domain) + isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid, + "root node must be a domain node", goto error); + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + node = isl_schedule_node_domain_intersect_domain(node, domain); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +error: + isl_schedule_free(schedule); + isl_union_set_free(domain); + return NULL; +} + +/* Replace the domain of the schedule "schedule" with the gist + * of the original domain with respect to the parameter domain "context". + */ +__isl_give isl_schedule *isl_schedule_gist_domain_params( + __isl_take isl_schedule *schedule, __isl_take isl_set *context) +{ + enum isl_schedule_node_type root_type; + isl_schedule_node *node; + + if (!schedule || !context) + goto error; + + root_type = isl_schedule_tree_get_type(schedule->root); + if (root_type != isl_schedule_node_domain) + isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid, + "root node must be a domain node", goto error); + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + node = isl_schedule_node_domain_gist_params(node, context); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +error: + isl_schedule_free(schedule); + isl_set_free(context); + return NULL; +} + +/* Return an isl_union_map representation of the schedule. + * If we still have access to the schedule tree, then we return + * an isl_union_map corresponding to the subtree schedule of the child + * of the root domain node. That is, we do not intersect the domain + * of the returned isl_union_map with the domain constraints. + * Otherwise, we must have removed it because we created a band forest. + * If so, we extract the isl_union_map from the forest. + * This reconstructed schedule map + * then needs to be padded with zeros to unify the schedule space + * since the result of isl_band_list_get_suffix_schedule may not have + * a unified schedule space. + */ +__isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched) +{ + enum isl_schedule_node_type type; + isl_schedule_node *node; + isl_union_map *umap; + + if (!sched) + return NULL; + + if (sched->root) { + type = isl_schedule_tree_get_type(sched->root); + if (type != isl_schedule_node_domain) + isl_die(isl_schedule_get_ctx(sched), isl_error_internal, + "root node not a domain node", return NULL); + + node = isl_schedule_get_root(sched); + node = isl_schedule_node_child(node, 0); + umap = isl_schedule_node_get_subtree_schedule_union_map(node); + isl_schedule_node_free(node); + + return umap; + } + + umap = isl_band_list_get_suffix_schedule(sched->band_forest); + return pad_schedule_map(umap); +} + +static __isl_give isl_band_list *construct_band_list( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain, + __isl_keep isl_band *parent); + +/* Construct an isl_band structure from the given schedule tree node, + * which may be either a band node or a leaf node. + * In the latter case, construct a zero-dimensional band. + * "domain" is the universe set of the domain elements that reach "node". + * "parent" is the parent isl_band of the isl_band constructed + * by this function. + * + * In case of a band node, we copy the properties (except tilability, + * which is implicit in an isl_band) to the isl_band. + * We assume that the band node is not zero-dimensional. + * If the child of the band node is not a leaf node, + * then we extract the children of the isl_band from this child. + */ +static __isl_give isl_band *construct_band(__isl_take isl_schedule_node *node, + __isl_take isl_union_set *domain, __isl_keep isl_band *parent) +{ + int i; + isl_ctx *ctx; + isl_band *band = NULL; + isl_multi_union_pw_aff *mupa; + + if (!node || !domain) + goto error; + + ctx = isl_schedule_node_get_ctx(node); + band = isl_band_alloc(ctx); + if (!band) + goto error; + + band->schedule = node->schedule; + band->parent = parent; + + if (isl_schedule_node_get_type(node) == isl_schedule_node_leaf) { + band->n = 0; + band->pma = isl_union_pw_multi_aff_from_domain(domain); + isl_schedule_node_free(node); + return band; + } + + band->n = isl_schedule_node_band_n_member(node); + if (band->n == 0) + isl_die(ctx, isl_error_unsupported, + "zero-dimensional band nodes not supported", + goto error); + band->coincident = isl_alloc_array(ctx, int, band->n); + if (band->n && !band->coincident) + goto error; + for (i = 0; i < band->n; ++i) + band->coincident[i] = + isl_schedule_node_band_member_get_coincident(node, i); + mupa = isl_schedule_node_band_get_partial_schedule(node); + band->pma = isl_union_pw_multi_aff_from_multi_union_pw_aff(mupa); + if (!band->pma) + goto error; + + node = isl_schedule_node_child(node, 0); + if (isl_schedule_node_get_type(node) == isl_schedule_node_leaf) { + isl_schedule_node_free(node); + isl_union_set_free(domain); + return band; + } + + band->children = construct_band_list(node, domain, band); + if (!band->children) + return isl_band_free(band); + + return band; +error: + isl_union_set_free(domain); + isl_schedule_node_free(node); + isl_band_free(band); + return NULL; +} + +/* Construct a list of isl_band structures from the children of "node". + * "node" itself is a sequence or set node, so that each of the child nodes + * is a filter node and the list returned by node_construct_band_list + * consists of a single element. + * "domain" is the universe set of the domain elements that reach "node". + * "parent" is the parent isl_band of the isl_band structures constructed + * by this function. + */ +static __isl_give isl_band_list *construct_band_list_from_children( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain, + __isl_keep isl_band *parent) +{ + int i, n; + isl_ctx *ctx; + isl_band_list *list; + + n = isl_schedule_node_n_children(node); + + ctx = isl_schedule_node_get_ctx(node); + list = isl_band_list_alloc(ctx, 0); + for (i = 0; i < n; ++i) { + isl_schedule_node *child; + isl_band_list *list_i; + + child = isl_schedule_node_get_child(node, i); + list_i = construct_band_list(child, isl_union_set_copy(domain), + parent); + list = isl_band_list_concat(list, list_i); + } + + isl_union_set_free(domain); + isl_schedule_node_free(node); + + return list; +} + +/* Construct an isl_band structure from the given sequence node + * (or set node that is treated as a sequence node). + * A single-dimensional band is created with as schedule for each of + * filters of the children, the corresponding child position. + * "domain" is the universe set of the domain elements that reach "node". + * "parent" is the parent isl_band of the isl_band constructed + * by this function. + */ +static __isl_give isl_band_list *construct_band_list_sequence( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain, + __isl_keep isl_band *parent) +{ + int i, n; + isl_ctx *ctx; + isl_band *band = NULL; + isl_space *space; + isl_union_pw_multi_aff *upma; + + if (!node || !domain) + goto error; + + ctx = isl_schedule_node_get_ctx(node); + band = isl_band_alloc(ctx); + if (!band) + goto error; + + band->schedule = node->schedule; + band->parent = parent; + band->n = 1; + band->coincident = isl_calloc_array(ctx, int, band->n); + if (!band->coincident) + goto error; + + n = isl_schedule_node_n_children(node); + space = isl_union_set_get_space(domain); + upma = isl_union_pw_multi_aff_empty(isl_space_copy(space)); + + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, 1); + + for (i = 0; i < n; ++i) { + isl_schedule_node *child; + isl_union_set *filter; + isl_val *v; + isl_val_list *vl; + isl_multi_val *mv; + isl_union_pw_multi_aff *upma_i; + + child = isl_schedule_node_get_child(node, i); + filter = isl_schedule_node_filter_get_filter(child); + isl_schedule_node_free(child); + filter = isl_union_set_intersect(filter, + isl_union_set_copy(domain)); + v = isl_val_int_from_si(ctx, i); + vl = isl_val_list_from_val(v); + mv = isl_multi_val_from_val_list(isl_space_copy(space), vl); + upma_i = isl_union_pw_multi_aff_multi_val_on_domain(filter, mv); + upma = isl_union_pw_multi_aff_union_add(upma, upma_i); + } + + isl_space_free(space); + + band->pma = upma; + if (!band->pma) + goto error; + + band->children = construct_band_list_from_children(node, domain, band); + if (!band->children) + band = isl_band_free(band); + return isl_band_list_from_band(band); +error: + isl_union_set_free(domain); + isl_schedule_node_free(node); + isl_band_free(band); + return NULL; +} + +/* Construct a list of isl_band structures from "node" depending + * on the type of "node". + * "domain" is the universe set of the domain elements that reach "node". + * "parent" is the parent isl_band of the isl_band structures constructed + * by this function. + * + * If schedule_separate_components is set then set nodes are treated + * as sequence nodes. Otherwise, we directly extract an (implicitly + * parallel) list of isl_band structures. + * + * If "node" is a filter, then "domain" is updated by the filter. + */ +static __isl_give isl_band_list *construct_band_list( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain, + __isl_keep isl_band *parent) +{ + enum isl_schedule_node_type type; + isl_ctx *ctx; + isl_band *band; + isl_band_list *list; + isl_union_set *filter; + + if (!node || !domain) + goto error; + + type = isl_schedule_node_get_type(node); + switch (type) { + case isl_schedule_node_error: + goto error; + case isl_schedule_node_context: + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "context nodes not supported", goto error); + case isl_schedule_node_domain: + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "internal domain nodes not allowed", goto error); + case isl_schedule_node_expansion: + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "expansion nodes not supported", goto error); + case isl_schedule_node_extension: + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "extension nodes not supported", goto error); + case isl_schedule_node_filter: + filter = isl_schedule_node_filter_get_filter(node); + domain = isl_union_set_intersect(domain, filter); + node = isl_schedule_node_child(node, 0); + return construct_band_list(node, domain, parent); + case isl_schedule_node_guard: + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "guard nodes not supported", goto error); + case isl_schedule_node_mark: + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "mark nodes not supported", goto error); + case isl_schedule_node_set: + ctx = isl_schedule_node_get_ctx(node); + if (isl_options_get_schedule_separate_components(ctx)) + return construct_band_list_sequence(node, domain, + parent); + else + return construct_band_list_from_children(node, domain, + parent); + case isl_schedule_node_sequence: + return construct_band_list_sequence(node, domain, parent); + case isl_schedule_node_leaf: + case isl_schedule_node_band: + band = construct_band(node, domain, parent); + list = isl_band_list_from_band(band); + break; + } + + return list; +error: + isl_union_set_free(domain); + isl_schedule_node_free(node); + return NULL; +} + +/* Return the roots of a band forest representation of the schedule. + * The band forest is constructed from the schedule tree, + * but once such a band forest is + * constructed, we forget about the original schedule tree since + * the user may modify the schedule through the band forest. + */ +__isl_give isl_band_list *isl_schedule_get_band_forest( + __isl_keep isl_schedule *schedule) +{ + isl_schedule_node *node; + isl_union_set *domain; + + if (!schedule) + return NULL; + if (schedule->root) { + node = isl_schedule_get_root(schedule); + domain = isl_schedule_node_domain_get_domain(node); + domain = isl_union_set_universe(domain); + node = isl_schedule_node_child(node, 0); + + schedule->band_forest = construct_band_list(node, domain, NULL); + schedule->root = isl_schedule_tree_free(schedule->root); + } + return isl_band_list_dup(schedule->band_forest); +} + +/* Call "fn" on each band in the schedule in depth-first post-order. + */ +int isl_schedule_foreach_band(__isl_keep isl_schedule *sched, + int (*fn)(__isl_keep isl_band *band, void *user), void *user) +{ + int r; + isl_band_list *forest; + + if (!sched) + return -1; + + forest = isl_schedule_get_band_forest(sched); + r = isl_band_list_foreach_band(forest, fn, user); + isl_band_list_free(forest); + + return r; +} + +static __isl_give isl_printer *print_band_list(__isl_take isl_printer *p, + __isl_keep isl_band_list *list); + +static __isl_give isl_printer *print_band(__isl_take isl_printer *p, + __isl_keep isl_band *band) +{ + isl_band_list *children; + + p = isl_printer_start_line(p); + p = isl_printer_print_union_pw_multi_aff(p, band->pma); + p = isl_printer_end_line(p); + + if (!isl_band_has_children(band)) + return p; + + children = isl_band_get_children(band); + + p = isl_printer_indent(p, 4); + p = print_band_list(p, children); + p = isl_printer_indent(p, -4); + + isl_band_list_free(children); + + return p; +} + +static __isl_give isl_printer *print_band_list(__isl_take isl_printer *p, + __isl_keep isl_band_list *list) +{ + int i, n; + + n = isl_band_list_n_band(list); + for (i = 0; i < n; ++i) { + isl_band *band; + band = isl_band_list_get_band(list, i); + p = print_band(p, band); + isl_band_free(band); + } + + return p; +} + +/* Insert a band node with partial schedule "partial" between the domain + * root node of "schedule" and its single child. + * Return a pointer to the updated schedule. + * + * If any of the nodes in the tree depend on the set of outer band nodes + * then we refuse to insert the band node. + */ +__isl_give isl_schedule *isl_schedule_insert_partial_schedule( + __isl_take isl_schedule *schedule, + __isl_take isl_multi_union_pw_aff *partial) +{ + isl_schedule_node *node; + int anchored; + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + if (!node) + goto error; + if (isl_schedule_node_get_type(node) != isl_schedule_node_domain) + isl_die(isl_schedule_node_get_ctx(node), isl_error_internal, + "root node not a domain node", goto error); + + node = isl_schedule_node_child(node, 0); + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot insert band node in anchored subtree", + goto error); + node = isl_schedule_node_insert_partial_schedule(node, partial); + + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +error: + isl_schedule_node_free(node); + isl_multi_union_pw_aff_free(partial); + return NULL; +} + +/* Insert a context node with constraints "context" between the domain + * root node of "schedule" and its single child. + * Return a pointer to the updated schedule. + */ +__isl_give isl_schedule *isl_schedule_insert_context( + __isl_take isl_schedule *schedule, __isl_take isl_set *context) +{ + isl_schedule_node *node; + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + node = isl_schedule_node_child(node, 0); + node = isl_schedule_node_insert_context(node, context); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +} + +/* Insert a guard node with constraints "guard" between the domain + * root node of "schedule" and its single child. + * Return a pointer to the updated schedule. + */ +__isl_give isl_schedule *isl_schedule_insert_guard( + __isl_take isl_schedule *schedule, __isl_take isl_set *guard) +{ + isl_schedule_node *node; + + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + node = isl_schedule_node_child(node, 0); + node = isl_schedule_node_insert_guard(node, guard); + schedule = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + return schedule; +} + +/* Return a tree with as top-level node a filter corresponding to "filter" and + * as child, the (single) child of "tree". + * However, if this single child is of type "type", then the filter is inserted + * in the children of this single child instead. + */ +static __isl_give isl_schedule_tree *insert_filter_in_child_of_type( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter, + enum isl_schedule_node_type type) +{ + if (!isl_schedule_tree_has_children(tree)) { + isl_schedule_tree_free(tree); + return isl_schedule_tree_from_filter(filter); + } else { + tree = isl_schedule_tree_child(tree, 0); + } + + if (isl_schedule_tree_get_type(tree) == type) + tree = isl_schedule_tree_children_insert_filter(tree, filter); + else + tree = isl_schedule_tree_insert_filter(tree, filter); + + return tree; +} + +/* Construct a schedule that combines the schedules "schedule1" and "schedule2" + * with a top-level node (underneath the domain node) of type "type", + * either isl_schedule_node_sequence or isl_schedule_node_set. + * The domains of the two schedules are assumed to be disjoint. + * + * The new schedule has as domain the union of the domains of the two + * schedules. The child of the domain node is a node of type "type" + * with two filters corresponding to the domains of the input schedules. + * If one (or both) of the top-level nodes of the two schedules is itself + * of type "type", then the filter is pushed into the children of that + * node and the sequence of set is flattened. + */ +__isl_give isl_schedule *isl_schedule_pair(enum isl_schedule_node_type type, + __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2) +{ + int disjoint; + isl_ctx *ctx; + enum isl_schedule_node_type root_type; + isl_schedule_tree *tree1, *tree2; + isl_union_set *filter1, *filter2, *domain; + + if (!schedule1 || !schedule2) + goto error; + + root_type = isl_schedule_tree_get_type(schedule1->root); + if (root_type != isl_schedule_node_domain) + isl_die(isl_schedule_get_ctx(schedule1), isl_error_internal, + "root node not a domain node", goto error); + root_type = isl_schedule_tree_get_type(schedule2->root); + if (root_type != isl_schedule_node_domain) + isl_die(isl_schedule_get_ctx(schedule1), isl_error_internal, + "root node not a domain node", goto error); + + ctx = isl_schedule_get_ctx(schedule1); + tree1 = isl_schedule_tree_copy(schedule1->root); + filter1 = isl_schedule_tree_domain_get_domain(tree1); + tree2 = isl_schedule_tree_copy(schedule2->root); + filter2 = isl_schedule_tree_domain_get_domain(tree2); + + isl_schedule_free(schedule1); + isl_schedule_free(schedule2); + + disjoint = isl_union_set_is_disjoint(filter1, filter2); + if (disjoint < 0) + filter1 = isl_union_set_free(filter1); + if (!disjoint) + isl_die(ctx, isl_error_invalid, + "schedule domains not disjoint", + filter1 = isl_union_set_free(filter1)); + + domain = isl_union_set_union(isl_union_set_copy(filter1), + isl_union_set_copy(filter2)); + filter1 = isl_union_set_gist(filter1, isl_union_set_copy(domain)); + filter2 = isl_union_set_gist(filter2, isl_union_set_copy(domain)); + + tree1 = insert_filter_in_child_of_type(tree1, filter1, type); + tree2 = insert_filter_in_child_of_type(tree2, filter2, type); + + tree1 = isl_schedule_tree_from_pair(type, tree1, tree2); + tree1 = isl_schedule_tree_insert_domain(tree1, domain); + + return isl_schedule_from_schedule_tree(ctx, tree1); +error: + isl_schedule_free(schedule1); + isl_schedule_free(schedule2); + return NULL; +} + +/* Construct a schedule that combines the schedules "schedule1" and "schedule2" + * through a sequence node. + * The domains of the input schedules are assumed to be disjoint. + */ +__isl_give isl_schedule *isl_schedule_sequence( + __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2) +{ + return isl_schedule_pair(isl_schedule_node_sequence, + schedule1, schedule2); +} + +/* Construct a schedule that combines the schedules "schedule1" and "schedule2" + * through a set node. + * The domains of the input schedules are assumed to be disjoint. + */ +__isl_give isl_schedule *isl_schedule_set( + __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2) +{ + return isl_schedule_pair(isl_schedule_node_set, schedule1, schedule2); +} + +/* Print "schedule" to "p". + * + * If "schedule" was created from a schedule tree, then we print + * the schedule tree representation. Otherwise, we print + * the band forest representation. + */ +__isl_give isl_printer *isl_printer_print_schedule(__isl_take isl_printer *p, + __isl_keep isl_schedule *schedule) +{ + isl_band_list *forest; + + if (!schedule) + return isl_printer_free(p); + + if (schedule->root) + return isl_printer_print_schedule_tree(p, schedule->root); + + forest = isl_schedule_get_band_forest(schedule); + + p = print_band_list(p, forest); + + isl_band_list_free(forest); + + return p; +} + +void isl_schedule_dump(__isl_keep isl_schedule *schedule) +{ + isl_printer *printer; + + if (!schedule) + return; + + printer = isl_printer_to_file(isl_schedule_get_ctx(schedule), stderr); + printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK); + printer = isl_printer_print_schedule(printer, schedule); + + isl_printer_free(printer); +} + +/* Return a string representation of "schedule". + * Print the schedule in flow format. + */ +__isl_give char *isl_schedule_to_str(__isl_keep isl_schedule *schedule) +{ + isl_printer *printer; + char *s; + + if (!schedule) + return NULL; + + printer = isl_printer_to_str(isl_schedule_get_ctx(schedule)); + printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_FLOW); + printer = isl_printer_print_schedule(printer, schedule); + s = isl_printer_get_str(printer); + isl_printer_free(printer); + + return s; +} Index: lib/Analysis/isl/isl_schedule_band.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_band.h @@ -0,0 +1,125 @@ +#ifndef ISL_SCHEDULE_BAND_H +#define ISL_SCHEDULE_BAND_H + +#include +#include +#include + +/* Information about a band within a schedule. + * + * n is the number of scheduling dimensions within the band. + * coincident is an array of length n, indicating whether a scheduling dimension + * satisfies the coincidence constraints in the sense that + * the corresponding dependence distances are zero. + * permutable is set if the band is permutable. + * mupa is the partial schedule corresponding to this band. The dimension + * of mupa is equal to n. + * loop_type contains the loop AST generation types for the members + * in the band. It may be NULL, if all members are + * of type isl_ast_loop_default. + * isolate_loop_type contains the loop AST generation types for the members + * in the band for the isolated part. It may be NULL, if all members are + * of type isl_ast_loop_default. + * ast_build_options are the remaining AST build options associated + * to the band. + * anchored is set if the node depends on its position in the schedule tree. + * In particular, it is set if the AST build options include + * an isolate option. + */ +struct isl_schedule_band { + int ref; + + int n; + int *coincident; + int permutable; + + isl_multi_union_pw_aff *mupa; + + int anchored; + isl_union_set *ast_build_options; + enum isl_ast_loop_type *loop_type; + enum isl_ast_loop_type *isolate_loop_type; +}; +typedef struct isl_schedule_band isl_schedule_band; + +__isl_give isl_schedule_band *isl_schedule_band_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa); +__isl_give isl_schedule_band *isl_schedule_band_copy( + __isl_keep isl_schedule_band *band); +__isl_null isl_schedule_band *isl_schedule_band_free( + __isl_take isl_schedule_band *band); + +isl_ctx *isl_schedule_band_get_ctx(__isl_keep isl_schedule_band *band); + +isl_bool isl_schedule_band_plain_is_equal(__isl_keep isl_schedule_band *band1, + __isl_keep isl_schedule_band *band2); + +int isl_schedule_band_is_anchored(__isl_keep isl_schedule_band *band); + +__isl_give isl_space *isl_schedule_band_get_space( + __isl_keep isl_schedule_band *band); +__isl_give isl_schedule_band *isl_schedule_band_intersect_domain( + __isl_take isl_schedule_band *band, __isl_take isl_union_set *domain); +__isl_give isl_multi_union_pw_aff *isl_schedule_band_get_partial_schedule( + __isl_keep isl_schedule_band *band); +__isl_give isl_schedule_band *isl_schedule_band_set_partial_schedule( + __isl_take isl_schedule_band *band, + __isl_take isl_multi_union_pw_aff *schedule); +enum isl_ast_loop_type isl_schedule_band_member_get_ast_loop_type( + __isl_keep isl_schedule_band *band, int pos); +__isl_give isl_schedule_band *isl_schedule_band_member_set_ast_loop_type( + __isl_take isl_schedule_band *band, int pos, + enum isl_ast_loop_type type); +enum isl_ast_loop_type isl_schedule_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_band *band, int pos); +__isl_give isl_schedule_band * +isl_schedule_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_band *band, int pos, + enum isl_ast_loop_type type); +__isl_give isl_union_set *isl_schedule_band_get_ast_build_options( + __isl_keep isl_schedule_band *band); +__isl_give isl_schedule_band *isl_schedule_band_set_ast_build_options( + __isl_take isl_schedule_band *band, __isl_take isl_union_set *options); +__isl_give isl_set *isl_schedule_band_get_ast_isolate_option( + __isl_keep isl_schedule_band *band, int depth); +__isl_give isl_schedule_band *isl_schedule_band_replace_ast_build_option( + __isl_take isl_schedule_band *band, __isl_take isl_set *drop, + __isl_take isl_set *add); + +int isl_schedule_band_n_member(__isl_keep isl_schedule_band *band); +isl_bool isl_schedule_band_member_get_coincident( + __isl_keep isl_schedule_band *band, int pos); +__isl_give isl_schedule_band *isl_schedule_band_member_set_coincident( + __isl_take isl_schedule_band *band, int pos, int coincident); +isl_bool isl_schedule_band_get_permutable(__isl_keep isl_schedule_band *band); +__isl_give isl_schedule_band *isl_schedule_band_set_permutable( + __isl_take isl_schedule_band *band, int permutable); + +__isl_give isl_schedule_band *isl_schedule_band_scale( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_band *isl_schedule_band_scale_down( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_band *isl_schedule_band_mod( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_band *isl_schedule_band_tile( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *sizes); +__isl_give isl_schedule_band *isl_schedule_band_point( + __isl_take isl_schedule_band *band, __isl_keep isl_schedule_band *tile, + __isl_take isl_multi_val *sizes); +__isl_give isl_schedule_band *isl_schedule_band_shift( + __isl_take isl_schedule_band *band, + __isl_take isl_multi_union_pw_aff *shift); +__isl_give isl_schedule_band *isl_schedule_band_drop( + __isl_take isl_schedule_band *band, int pos, int n); +__isl_give isl_schedule_band *isl_schedule_band_gist( + __isl_take isl_schedule_band *band, __isl_take isl_union_set *context); + +__isl_give isl_schedule_band *isl_schedule_band_reset_user( + __isl_take isl_schedule_band *band); +__isl_give isl_schedule_band *isl_schedule_band_align_params( + __isl_take isl_schedule_band *band, __isl_take isl_space *space); +__isl_give isl_schedule_band *isl_schedule_band_pullback_union_pw_multi_aff( + __isl_take isl_schedule_band *band, + __isl_take isl_union_pw_multi_aff *upma); + +#endif Index: lib/Analysis/isl/isl_schedule_band.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_band.c @@ -0,0 +1,1296 @@ +/* + * Copyright 2013-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include + +isl_ctx *isl_schedule_band_get_ctx(__isl_keep isl_schedule_band *band) +{ + return band ? isl_multi_union_pw_aff_get_ctx(band->mupa) : NULL; +} + +/* Return a new uninitialized isl_schedule_band. + */ +static __isl_give isl_schedule_band *isl_schedule_band_alloc(isl_ctx *ctx) +{ + isl_schedule_band *band; + + band = isl_calloc_type(ctx, isl_schedule_band); + if (!band) + return NULL; + + band->ref = 1; + + return band; +} + +/* Return a new isl_schedule_band with partial schedule "mupa". + * First replace "mupa" by its greatest integer part to ensure + * that the schedule is always integral. + * The band is not marked permutable, the dimensions are not + * marked coincident and the AST build options are empty. + * Since there are no build options, the node is not anchored. + */ +__isl_give isl_schedule_band *isl_schedule_band_from_multi_union_pw_aff( + __isl_take isl_multi_union_pw_aff *mupa) +{ + isl_ctx *ctx; + isl_schedule_band *band; + isl_space *space; + + mupa = isl_multi_union_pw_aff_floor(mupa); + if (!mupa) + return NULL; + ctx = isl_multi_union_pw_aff_get_ctx(mupa); + band = isl_schedule_band_alloc(ctx); + if (!band) + goto error; + + band->n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set); + band->coincident = isl_calloc_array(ctx, int, band->n); + band->mupa = mupa; + space = isl_space_params_alloc(ctx, 0); + band->ast_build_options = isl_union_set_empty(space); + band->anchored = 0; + + if ((band->n && !band->coincident) || !band->ast_build_options) + return isl_schedule_band_free(band); + + return band; +error: + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Create a duplicate of the given isl_schedule_band. + */ +__isl_give isl_schedule_band *isl_schedule_band_dup( + __isl_keep isl_schedule_band *band) +{ + int i; + isl_ctx *ctx; + isl_schedule_band *dup; + + if (!band) + return NULL; + + ctx = isl_schedule_band_get_ctx(band); + dup = isl_schedule_band_alloc(ctx); + if (!dup) + return NULL; + + dup->n = band->n; + dup->coincident = isl_alloc_array(ctx, int, band->n); + if (band->n && !dup->coincident) + return isl_schedule_band_free(dup); + + for (i = 0; i < band->n; ++i) + dup->coincident[i] = band->coincident[i]; + dup->permutable = band->permutable; + + dup->mupa = isl_multi_union_pw_aff_copy(band->mupa); + dup->ast_build_options = isl_union_set_copy(band->ast_build_options); + if (!dup->mupa || !dup->ast_build_options) + return isl_schedule_band_free(dup); + + if (band->loop_type) { + dup->loop_type = isl_alloc_array(ctx, + enum isl_ast_loop_type, band->n); + if (band->n && !dup->loop_type) + return isl_schedule_band_free(dup); + for (i = 0; i < band->n; ++i) + dup->loop_type[i] = band->loop_type[i]; + } + if (band->isolate_loop_type) { + dup->isolate_loop_type = isl_alloc_array(ctx, + enum isl_ast_loop_type, band->n); + if (band->n && !dup->isolate_loop_type) + return isl_schedule_band_free(dup); + for (i = 0; i < band->n; ++i) + dup->isolate_loop_type[i] = band->isolate_loop_type[i]; + } + + return dup; +} + +/* Return an isl_schedule_band that is equal to "band" and that has only + * a single reference. + */ +__isl_give isl_schedule_band *isl_schedule_band_cow( + __isl_take isl_schedule_band *band) +{ + if (!band) + return NULL; + + if (band->ref == 1) + return band; + band->ref--; + return isl_schedule_band_dup(band); +} + +/* Return a new reference to "band". + */ +__isl_give isl_schedule_band *isl_schedule_band_copy( + __isl_keep isl_schedule_band *band) +{ + if (!band) + return NULL; + + band->ref++; + return band; +} + +/* Free a reference to "band" and return NULL. + */ +__isl_null isl_schedule_band *isl_schedule_band_free( + __isl_take isl_schedule_band *band) +{ + if (!band) + return NULL; + + if (--band->ref > 0) + return NULL; + + isl_multi_union_pw_aff_free(band->mupa); + isl_union_set_free(band->ast_build_options); + free(band->loop_type); + free(band->isolate_loop_type); + free(band->coincident); + free(band); + + return NULL; +} + +/* Are "band1" and "band2" obviously equal? + */ +isl_bool isl_schedule_band_plain_is_equal(__isl_keep isl_schedule_band *band1, + __isl_keep isl_schedule_band *band2) +{ + int i; + isl_bool equal; + + if (!band1 || !band2) + return isl_bool_error; + if (band1 == band2) + return isl_bool_true; + + if (band1->n != band2->n) + return isl_bool_false; + for (i = 0; i < band1->n; ++i) + if (band1->coincident[i] != band2->coincident[i]) + return isl_bool_false; + if (band1->permutable != band2->permutable) + return isl_bool_false; + + equal = isl_multi_union_pw_aff_plain_is_equal(band1->mupa, band2->mupa); + if (equal < 0 || !equal) + return equal; + + if (!band1->loop_type != !band2->loop_type) + return isl_bool_false; + if (band1->loop_type) + for (i = 0; i < band1->n; ++i) + if (band1->loop_type[i] != band2->loop_type[i]) + return isl_bool_false; + + if (!band1->isolate_loop_type != !band2->isolate_loop_type) + return isl_bool_false; + if (band1->isolate_loop_type) + for (i = 0; i < band1->n; ++i) + if (band1->isolate_loop_type[i] != + band2->isolate_loop_type[i]) + return isl_bool_false; + + return isl_union_set_is_equal(band1->ast_build_options, + band2->ast_build_options); +} + +/* Return the number of scheduling dimensions in the band. + */ +int isl_schedule_band_n_member(__isl_keep isl_schedule_band *band) +{ + return band ? band->n : 0; +} + +/* Is the given scheduling dimension coincident within the band and + * with respect to the coincidence constraints? + */ +isl_bool isl_schedule_band_member_get_coincident( + __isl_keep isl_schedule_band *band, int pos) +{ + if (!band) + return isl_bool_error; + + if (pos < 0 || pos >= band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "invalid member position", return isl_bool_error); + + return band->coincident[pos]; +} + +/* Mark the given scheduling dimension as being coincident or not + * according to "coincident". + */ +__isl_give isl_schedule_band *isl_schedule_band_member_set_coincident( + __isl_take isl_schedule_band *band, int pos, int coincident) +{ + if (!band) + return NULL; + if (isl_schedule_band_member_get_coincident(band, pos) == coincident) + return band; + band = isl_schedule_band_cow(band); + if (!band) + return NULL; + + if (pos < 0 || pos >= band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "invalid member position", + isl_schedule_band_free(band)); + + band->coincident[pos] = coincident; + + return band; +} + +/* Is the schedule band mark permutable? + */ +isl_bool isl_schedule_band_get_permutable(__isl_keep isl_schedule_band *band) +{ + if (!band) + return isl_bool_error; + return band->permutable; +} + +/* Mark the schedule band permutable or not according to "permutable"? + */ +__isl_give isl_schedule_band *isl_schedule_band_set_permutable( + __isl_take isl_schedule_band *band, int permutable) +{ + if (!band) + return NULL; + if (band->permutable == permutable) + return band; + band = isl_schedule_band_cow(band); + if (!band) + return NULL; + + band->permutable = permutable; + + return band; +} + +/* Is the band node "node" anchored? That is, does it reference + * the outer band nodes? + */ +int isl_schedule_band_is_anchored(__isl_keep isl_schedule_band *band) +{ + return band ? band->anchored : -1; +} + +/* Return the schedule space of the band. + */ +__isl_give isl_space *isl_schedule_band_get_space( + __isl_keep isl_schedule_band *band) +{ + if (!band) + return NULL; + return isl_multi_union_pw_aff_get_space(band->mupa); +} + +/* Intersect the domain of the band schedule of "band" with "domain". + */ +__isl_give isl_schedule_band *isl_schedule_band_intersect_domain( + __isl_take isl_schedule_band *band, __isl_take isl_union_set *domain) +{ + band = isl_schedule_band_cow(band); + if (!band || !domain) + goto error; + + band->mupa = isl_multi_union_pw_aff_intersect_domain(band->mupa, + domain); + if (!band->mupa) + return isl_schedule_band_free(band); + + return band; +error: + isl_schedule_band_free(band); + isl_union_set_free(domain); + return NULL; +} + +/* Return the schedule of the band in isolation. + */ +__isl_give isl_multi_union_pw_aff *isl_schedule_band_get_partial_schedule( + __isl_keep isl_schedule_band *band) +{ + return band ? isl_multi_union_pw_aff_copy(band->mupa) : NULL; +} + +/* Replace the schedule of "band" by "schedule". + */ +__isl_give isl_schedule_band *isl_schedule_band_set_partial_schedule( + __isl_take isl_schedule_band *band, + __isl_take isl_multi_union_pw_aff *schedule) +{ + band = isl_schedule_band_cow(band); + if (!band || !schedule) + goto error; + + isl_multi_union_pw_aff_free(band->mupa); + band->mupa = schedule; + + return band; +error: + isl_schedule_band_free(band); + isl_multi_union_pw_aff_free(schedule); + return NULL; +} + +/* Return the loop AST generation type for the band member of "band" + * at position "pos". + */ +enum isl_ast_loop_type isl_schedule_band_member_get_ast_loop_type( + __isl_keep isl_schedule_band *band, int pos) +{ + if (!band) + return isl_ast_loop_error; + + if (pos < 0 || pos >= band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "invalid member position", return -1); + + if (!band->loop_type) + return isl_ast_loop_default; + + return band->loop_type[pos]; +} + +/* Set the loop AST generation type for the band member of "band" + * at position "pos" to "type". + */ +__isl_give isl_schedule_band *isl_schedule_band_member_set_ast_loop_type( + __isl_take isl_schedule_band *band, int pos, + enum isl_ast_loop_type type) +{ + if (!band) + return NULL; + if (isl_schedule_band_member_get_ast_loop_type(band, pos) == type) + return band; + + if (pos < 0 || pos >= band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "invalid member position", + isl_schedule_band_free(band)); + + band = isl_schedule_band_cow(band); + if (!band) + return isl_schedule_band_free(band); + + if (!band->loop_type) { + isl_ctx *ctx; + + ctx = isl_schedule_band_get_ctx(band); + band->loop_type = isl_calloc_array(ctx, + enum isl_ast_loop_type, band->n); + if (band->n && !band->loop_type) + return isl_schedule_band_free(band); + } + + band->loop_type[pos] = type; + + return band; +} + +/* Return the loop AST generation type for the band member of "band" + * at position "pos" for the part that has been isolated by the isolate option. + */ +enum isl_ast_loop_type isl_schedule_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_band *band, int pos) +{ + if (!band) + return isl_ast_loop_error; + + if (pos < 0 || pos >= band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "invalid member position", return -1); + + if (!band->isolate_loop_type) + return isl_ast_loop_default; + + return band->isolate_loop_type[pos]; +} + +/* Set the loop AST generation type for the band member of "band" + * at position "pos" to "type" for the part that has been isolated + * by the isolate option. + */ +__isl_give isl_schedule_band * +isl_schedule_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_band *band, int pos, + enum isl_ast_loop_type type) +{ + if (!band) + return NULL; + if (isl_schedule_band_member_get_isolate_ast_loop_type(band, pos) == + type) + return band; + + if (pos < 0 || pos >= band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "invalid member position", + isl_schedule_band_free(band)); + + band = isl_schedule_band_cow(band); + if (!band) + return isl_schedule_band_free(band); + + if (!band->isolate_loop_type) { + isl_ctx *ctx; + + ctx = isl_schedule_band_get_ctx(band); + band->isolate_loop_type = isl_calloc_array(ctx, + enum isl_ast_loop_type, band->n); + if (band->n && !band->isolate_loop_type) + return isl_schedule_band_free(band); + } + + band->isolate_loop_type[pos] = type; + + return band; +} + +static const char *option_str[] = { + [isl_ast_loop_atomic] = "atomic", + [isl_ast_loop_unroll] = "unroll", + [isl_ast_loop_separate] = "separate" +}; + +/* Given a parameter space "space", extend it to a set space + * + * { type[x] } + * + * or + * + * { [isolate[] -> type[x]] } + * + * depending on whether "isolate" is set. + * These can be used to encode loop AST generation options of the given type. + */ +static __isl_give isl_space *loop_type_space(__isl_take isl_space *space, + enum isl_ast_loop_type type, int isolate) +{ + const char *name; + + name = option_str[type]; + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, 1); + space = isl_space_set_tuple_name(space, isl_dim_set, name); + if (!isolate) + return space; + space = isl_space_from_range(space); + space = isl_space_set_tuple_name(space, isl_dim_in, "isolate"); + space = isl_space_wrap(space); + + return space; +} + +/* Add encodings of the "n" loop AST generation options "type" to "options". + * If "isolate" is set, then these options refer to the isolated part. + * + * In particular, for each sequence of consecutive identical types "t", + * different from the default, add an option + * + * { t[x] : first <= x <= last } + * + * or + * + * { [isolate[] -> t[x]] : first <= x <= last } + */ +static __isl_give isl_union_set *add_loop_types( + __isl_take isl_union_set *options, int n, enum isl_ast_loop_type *type, + int isolate) +{ + int i; + isl_ctx *ctx; + + if (!type) + return options; + if (!options) + return NULL; + + ctx = isl_union_set_get_ctx(options); + for (i = 0; i < n; ++i) { + int first; + isl_space *space; + isl_set *option; + + if (type[i] == isl_ast_loop_default) + continue; + + first = i; + while (i + 1 < n && type[i + 1] == type[i]) + ++i; + + space = isl_union_set_get_space(options); + space = loop_type_space(space, type[i], isolate); + option = isl_set_universe(space); + option = isl_set_lower_bound_si(option, isl_dim_set, 0, first); + option = isl_set_upper_bound_si(option, isl_dim_set, 0, i); + options = isl_union_set_add_set(options, option); + } + + return options; +} + +/* Return the AST build options associated to "band". + */ +__isl_give isl_union_set *isl_schedule_band_get_ast_build_options( + __isl_keep isl_schedule_band *band) +{ + isl_union_set *options; + + if (!band) + return NULL; + + options = isl_union_set_copy(band->ast_build_options); + options = add_loop_types(options, band->n, band->loop_type, 0); + options = add_loop_types(options, band->n, band->isolate_loop_type, 1); + + return options; +} + +/* Does "uset" contain any set that satisfies "is"? + * "is" is assumed to set its integer argument to 1 if it is satisfied. + */ +static int has_any(__isl_keep isl_union_set *uset, + isl_stat (*is)(__isl_take isl_set *set, void *user)) +{ + int found = 0; + + if (isl_union_set_foreach_set(uset, is, &found) < 0 && !found) + return -1; + + return found; +} + +/* Does "set" live in a space of the form + * + * isolate[[...] -> [...]] + * + * ? + * + * If so, set *found and abort the search. + */ +static isl_stat is_isolate(__isl_take isl_set *set, void *user) +{ + int *found = user; + + if (isl_set_has_tuple_name(set)) { + const char *name; + name = isl_set_get_tuple_name(set); + if (isl_set_is_wrapping(set) && !strcmp(name, "isolate")) + *found = 1; + } + isl_set_free(set); + + return *found ? isl_stat_error : isl_stat_ok; +} + +/* Does "options" include an option of the ofrm + * + * isolate[[...] -> [...]] + * + * ? + */ +static int has_isolate_option(__isl_keep isl_union_set *options) +{ + return has_any(options, &is_isolate); +} + +/* Does "set" encode a loop AST generation option? + */ +static isl_stat is_loop_type_option(__isl_take isl_set *set, void *user) +{ + int *found = user; + + if (isl_set_dim(set, isl_dim_set) == 1 && + isl_set_has_tuple_name(set)) { + const char *name; + enum isl_ast_loop_type type; + name = isl_set_get_tuple_name(set); + for (type = isl_ast_loop_atomic; + type <= isl_ast_loop_separate; ++type) { + if (strcmp(name, option_str[type])) + continue; + *found = 1; + break; + } + } + isl_set_free(set); + + return *found ? isl_stat_error : isl_stat_ok; +} + +/* Does "set" encode a loop AST generation option for the isolated part? + * That is, is of the form + * + * { [isolate[] -> t[x]] } + * + * with t equal to "atomic", "unroll" or "separate"? + */ +static isl_stat is_isolate_loop_type_option(__isl_take isl_set *set, void *user) +{ + int *found = user; + const char *name; + enum isl_ast_loop_type type; + isl_map *map; + + if (!isl_set_is_wrapping(set)) { + isl_set_free(set); + return isl_stat_ok; + } + map = isl_set_unwrap(set); + if (!isl_map_has_tuple_name(map, isl_dim_in) || + !isl_map_has_tuple_name(map, isl_dim_out)) { + isl_map_free(map); + return isl_stat_ok; + } + name = isl_map_get_tuple_name(map, isl_dim_in); + if (!strcmp(name, "isolate")) { + name = isl_map_get_tuple_name(map, isl_dim_out); + for (type = isl_ast_loop_atomic; + type <= isl_ast_loop_separate; ++type) { + if (strcmp(name, option_str[type])) + continue; + *found = 1; + break; + } + } + isl_map_free(map); + + return *found ? isl_stat_error : isl_stat_ok; +} + +/* Does "options" encode any loop AST generation options + * for the isolated part? + */ +static int has_isolate_loop_type_options(__isl_keep isl_union_set *options) +{ + return has_any(options, &is_isolate_loop_type_option); +} + +/* Does "options" encode any loop AST generation options? + */ +static int has_loop_type_options(__isl_keep isl_union_set *options) +{ + return has_any(options, &is_loop_type_option); +} + +/* Extract the loop AST generation type for the band member + * at position "pos" from "options". + * If "isolate" is set, then extract the loop types for the isolated part. + */ +static enum isl_ast_loop_type extract_loop_type( + __isl_keep isl_union_set *options, int pos, int isolate) +{ + isl_ctx *ctx; + enum isl_ast_loop_type type, res = isl_ast_loop_default; + + ctx = isl_union_set_get_ctx(options); + for (type = isl_ast_loop_atomic; + type <= isl_ast_loop_separate; ++type) { + isl_space *space; + isl_set *option; + int empty; + + space = isl_union_set_get_space(options); + space = loop_type_space(space, type, isolate); + option = isl_union_set_extract_set(options, space); + option = isl_set_fix_si(option, isl_dim_set, 0, pos); + empty = isl_set_is_empty(option); + isl_set_free(option); + + if (empty < 0) + return isl_ast_loop_error; + if (empty) + continue; + if (res != isl_ast_loop_default) + isl_die(ctx, isl_error_invalid, + "conflicting loop type options", + return isl_ast_loop_error); + res = type; + } + + return res; +} + +/* Extract the loop AST generation types for the members of "band" + * from "options" and store them in band->loop_type. + * Return -1 on error. + */ +static int extract_loop_types(__isl_keep isl_schedule_band *band, + __isl_keep isl_union_set *options) +{ + int i; + + if (!band->loop_type) { + isl_ctx *ctx = isl_schedule_band_get_ctx(band); + band->loop_type = isl_alloc_array(ctx, + enum isl_ast_loop_type, band->n); + if (band->n && !band->loop_type) + return -1; + } + for (i = 0; i < band->n; ++i) { + band->loop_type[i] = extract_loop_type(options, i, 0); + if (band->loop_type[i] == isl_ast_loop_error) + return -1; + } + + return 0; +} + +/* Extract the loop AST generation types for the members of "band" + * from "options" for the isolated part and + * store them in band->isolate_loop_type. + * Return -1 on error. + */ +static int extract_isolate_loop_types(__isl_keep isl_schedule_band *band, + __isl_keep isl_union_set *options) +{ + int i; + + if (!band->isolate_loop_type) { + isl_ctx *ctx = isl_schedule_band_get_ctx(band); + band->isolate_loop_type = isl_alloc_array(ctx, + enum isl_ast_loop_type, band->n); + if (band->n && !band->isolate_loop_type) + return -1; + } + for (i = 0; i < band->n; ++i) { + band->isolate_loop_type[i] = extract_loop_type(options, i, 1); + if (band->isolate_loop_type[i] == isl_ast_loop_error) + return -1; + } + + return 0; +} + +/* Construct universe sets of the spaces that encode loop AST generation + * types (for the isolated part if "isolate" is set). That is, construct + * + * { atomic[x]; separate[x]; unroll[x] } + * + * or + * + * { [isolate[] -> atomic[x]]; [isolate[] -> separate[x]]; + * [isolate[] -> unroll[x]] } + */ +static __isl_give isl_union_set *loop_types(__isl_take isl_space *space, + int isolate) +{ + enum isl_ast_loop_type type; + isl_union_set *types; + + types = isl_union_set_empty(space); + for (type = isl_ast_loop_atomic; + type <= isl_ast_loop_separate; ++type) { + isl_set *set; + + space = isl_union_set_get_space(types); + space = loop_type_space(space, type, isolate); + set = isl_set_universe(space); + types = isl_union_set_add_set(types, set); + } + + return types; +} + +/* Remove all elements from spaces that encode loop AST generation types + * from "options". + */ +static __isl_give isl_union_set *clear_loop_types( + __isl_take isl_union_set *options) +{ + isl_union_set *types; + + types = loop_types(isl_union_set_get_space(options), 0); + options = isl_union_set_subtract(options, types); + + return options; +} + +/* Remove all elements from spaces that encode loop AST generation types + * for the isolated part from "options". + */ +static __isl_give isl_union_set *clear_isolate_loop_types( + __isl_take isl_union_set *options) +{ + isl_union_set *types; + + types = loop_types(isl_union_set_get_space(options), 1); + options = isl_union_set_subtract(options, types); + + return options; +} + +/* Replace the AST build options associated to "band" by "options". + * If there are any loop AST generation type options, then they + * are extracted and stored in band->loop_type. Otherwise, + * band->loop_type is removed to indicate that the default applies + * to all members. Similarly for the loop AST generation type options + * for the isolated part, which are stored in band->isolate_loop_type. + * The remaining options are stored in band->ast_build_options. + * + * Set anchored if the options include an isolate option since the + * domain of the wrapped map references the outer band node schedules. + */ +__isl_give isl_schedule_band *isl_schedule_band_set_ast_build_options( + __isl_take isl_schedule_band *band, __isl_take isl_union_set *options) +{ + int has_isolate, has_loop_type, has_isolate_loop_type; + + band = isl_schedule_band_cow(band); + if (!band || !options) + goto error; + has_isolate = has_isolate_option(options); + if (has_isolate < 0) + goto error; + has_loop_type = has_loop_type_options(options); + if (has_loop_type < 0) + goto error; + has_isolate_loop_type = has_isolate_loop_type_options(options); + if (has_isolate_loop_type < 0) + goto error; + + if (!has_loop_type) { + free(band->loop_type); + band->loop_type = NULL; + } else { + if (extract_loop_types(band, options) < 0) + goto error; + options = clear_loop_types(options); + if (!options) + goto error; + } + + if (!has_isolate_loop_type) { + free(band->isolate_loop_type); + band->isolate_loop_type = NULL; + } else { + if (extract_isolate_loop_types(band, options) < 0) + goto error; + options = clear_isolate_loop_types(options); + if (!options) + goto error; + } + + isl_union_set_free(band->ast_build_options); + band->ast_build_options = options; + band->anchored = has_isolate; + + return band; +error: + isl_schedule_band_free(band); + isl_union_set_free(options); + return NULL; +} + +/* Return the "isolate" option associated to "band", assuming + * it at appears at schedule depth "depth". + * + * The isolate option is of the form + * + * isolate[[flattened outer bands] -> band] + */ +__isl_give isl_set *isl_schedule_band_get_ast_isolate_option( + __isl_keep isl_schedule_band *band, int depth) +{ + isl_space *space; + isl_set *isolate; + + if (!band) + return NULL; + + space = isl_schedule_band_get_space(band); + space = isl_space_from_range(space); + space = isl_space_add_dims(space, isl_dim_in, depth); + space = isl_space_wrap(space); + space = isl_space_set_tuple_name(space, isl_dim_set, "isolate"); + + isolate = isl_union_set_extract_set(band->ast_build_options, space); + + return isolate; +} + +/* Replace the option "drop" in the AST build options by "add". + * That is, remove "drop" and add "add". + */ +__isl_give isl_schedule_band *isl_schedule_band_replace_ast_build_option( + __isl_take isl_schedule_band *band, __isl_take isl_set *drop, + __isl_take isl_set *add) +{ + isl_union_set *options; + + band = isl_schedule_band_cow(band); + if (!band) + return NULL; + + options = band->ast_build_options; + options = isl_union_set_subtract(options, isl_union_set_from_set(drop)); + options = isl_union_set_union(options, isl_union_set_from_set(add)); + band->ast_build_options = options; + + if (!band->ast_build_options) + return isl_schedule_band_free(band); + + return band; +} + +/* Multiply the partial schedule of "band" with the factors in "mv". + * Replace the result by its greatest integer part to ensure + * that the schedule is always integral. + */ +__isl_give isl_schedule_band *isl_schedule_band_scale( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv) +{ + band = isl_schedule_band_cow(band); + if (!band || !mv) + goto error; + band->mupa = isl_multi_union_pw_aff_scale_multi_val(band->mupa, mv); + band->mupa = isl_multi_union_pw_aff_floor(band->mupa); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_schedule_band_free(band); + isl_multi_val_free(mv); + return NULL; +} + +/* Divide the partial schedule of "band" by the factors in "mv". + * Replace the result by its greatest integer part to ensure + * that the schedule is always integral. + */ +__isl_give isl_schedule_band *isl_schedule_band_scale_down( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv) +{ + band = isl_schedule_band_cow(band); + if (!band || !mv) + goto error; + band->mupa = isl_multi_union_pw_aff_scale_down_multi_val(band->mupa, + mv); + band->mupa = isl_multi_union_pw_aff_floor(band->mupa); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_schedule_band_free(band); + isl_multi_val_free(mv); + return NULL; +} + +/* Reduce the partial schedule of "band" modulo the factors in "mv". + */ +__isl_give isl_schedule_band *isl_schedule_band_mod( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv) +{ + band = isl_schedule_band_cow(band); + if (!band || !mv) + goto error; + band->mupa = isl_multi_union_pw_aff_mod_multi_val(band->mupa, mv); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_schedule_band_free(band); + isl_multi_val_free(mv); + return NULL; +} + +/* Shift the partial schedule of "band" by "shift" after checking + * that the domain of the partial schedule would not be affected + * by this shift. + */ +__isl_give isl_schedule_band *isl_schedule_band_shift( + __isl_take isl_schedule_band *band, + __isl_take isl_multi_union_pw_aff *shift) +{ + isl_union_set *dom1, *dom2; + isl_bool subset; + + band = isl_schedule_band_cow(band); + if (!band || !shift) + goto error; + dom1 = isl_multi_union_pw_aff_domain( + isl_multi_union_pw_aff_copy(band->mupa)); + dom2 = isl_multi_union_pw_aff_domain( + isl_multi_union_pw_aff_copy(shift)); + subset = isl_union_set_is_subset(dom1, dom2); + isl_union_set_free(dom1); + isl_union_set_free(dom2); + if (subset < 0) + goto error; + if (!subset) + isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid, + "domain of shift needs to include domain of " + "partial schedule", goto error); + band->mupa = isl_multi_union_pw_aff_add(band->mupa, shift); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_schedule_band_free(band); + isl_multi_union_pw_aff_free(shift); + return NULL; +} + +/* Given the schedule of a band, construct the corresponding + * schedule for the tile loops based on the given tile sizes + * and return the result. + * + * If the scale tile loops options is set, then the tile loops + * are scaled by the tile sizes. + * + * That is replace each schedule dimension "i" by either + * "floor(i/s)" or "s * floor(i/s)". + */ +static isl_multi_union_pw_aff *isl_multi_union_pw_aff_tile( + __isl_take isl_multi_union_pw_aff *sched, + __isl_take isl_multi_val *sizes) +{ + isl_ctx *ctx; + int i, n; + isl_val *v; + int scale; + + ctx = isl_multi_val_get_ctx(sizes); + scale = isl_options_get_tile_scale_tile_loops(ctx); + + n = isl_multi_union_pw_aff_dim(sched, isl_dim_set); + for (i = 0; i < n; ++i) { + isl_union_pw_aff *upa; + + upa = isl_multi_union_pw_aff_get_union_pw_aff(sched, i); + v = isl_multi_val_get_val(sizes, i); + + upa = isl_union_pw_aff_scale_down_val(upa, isl_val_copy(v)); + upa = isl_union_pw_aff_floor(upa); + if (scale) + upa = isl_union_pw_aff_scale_val(upa, isl_val_copy(v)); + isl_val_free(v); + + sched = isl_multi_union_pw_aff_set_union_pw_aff(sched, i, upa); + } + + isl_multi_val_free(sizes); + return sched; +} + +/* Replace "band" by a band corresponding to the tile loops of a tiling + * with the given tile sizes. + */ +__isl_give isl_schedule_band *isl_schedule_band_tile( + __isl_take isl_schedule_band *band, __isl_take isl_multi_val *sizes) +{ + band = isl_schedule_band_cow(band); + if (!band || !sizes) + goto error; + band->mupa = isl_multi_union_pw_aff_tile(band->mupa, sizes); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_schedule_band_free(band); + isl_multi_val_free(sizes); + return NULL; +} + +/* Replace "band" by a band corresponding to the point loops of a tiling + * with the given tile sizes. + * "tile" is the corresponding tile loop band. + * + * If the shift point loops option is set, then the point loops + * are shifted to start at zero. That is, each schedule dimension "i" + * is replaced by "i - s * floor(i/s)". + * The expression "floor(i/s)" (or "s * floor(i/s)") is extracted from + * the tile band. + * + * Otherwise, the band is left untouched. + */ +__isl_give isl_schedule_band *isl_schedule_band_point( + __isl_take isl_schedule_band *band, __isl_keep isl_schedule_band *tile, + __isl_take isl_multi_val *sizes) +{ + isl_ctx *ctx; + isl_multi_union_pw_aff *scaled; + + if (!band || !sizes) + goto error; + + ctx = isl_schedule_band_get_ctx(band); + if (!isl_options_get_tile_shift_point_loops(ctx)) { + isl_multi_val_free(sizes); + return band; + } + band = isl_schedule_band_cow(band); + if (!band) + goto error; + + scaled = isl_schedule_band_get_partial_schedule(tile); + if (!isl_options_get_tile_scale_tile_loops(ctx)) + scaled = isl_multi_union_pw_aff_scale_multi_val(scaled, sizes); + else + isl_multi_val_free(sizes); + band->mupa = isl_multi_union_pw_aff_sub(band->mupa, scaled); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_schedule_band_free(band); + isl_multi_val_free(sizes); + return NULL; +} + +/* Drop the "n" dimensions starting at "pos" from "band". + * + * We apply the transformation even if "n" is zero to ensure consistent + * behavior with respect to changes in the schedule space. + * + * The caller is responsible for updating the isolate option. + */ +__isl_give isl_schedule_band *isl_schedule_band_drop( + __isl_take isl_schedule_band *band, int pos, int n) +{ + int i; + + if (pos < 0 || n < 0 || pos + n > band->n) + isl_die(isl_schedule_band_get_ctx(band), isl_error_internal, + "range out of bounds", + return isl_schedule_band_free(band)); + + band = isl_schedule_band_cow(band); + if (!band) + return NULL; + + band->mupa = isl_multi_union_pw_aff_drop_dims(band->mupa, + isl_dim_set, pos, n); + if (!band->mupa) + return isl_schedule_band_free(band); + + for (i = pos + n; i < band->n; ++i) + band->coincident[i - n] = band->coincident[i]; + if (band->loop_type) + for (i = pos + n; i < band->n; ++i) + band->loop_type[i - n] = band->loop_type[i]; + if (band->isolate_loop_type) + for (i = pos + n; i < band->n; ++i) + band->isolate_loop_type[i - n] = + band->isolate_loop_type[i]; + + band->n -= n; + + return band; +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * in "band". + */ +__isl_give isl_schedule_band *isl_schedule_band_reset_user( + __isl_take isl_schedule_band *band) +{ + band = isl_schedule_band_cow(band); + if (!band) + return NULL; + + band->mupa = isl_multi_union_pw_aff_reset_user(band->mupa); + band->ast_build_options = + isl_union_set_reset_user(band->ast_build_options); + if (!band->mupa || !band->ast_build_options) + return isl_schedule_band_free(band); + + return band; +} + +/* Align the parameters of "band" to those of "space". + */ +__isl_give isl_schedule_band *isl_schedule_band_align_params( + __isl_take isl_schedule_band *band, __isl_take isl_space *space) +{ + band = isl_schedule_band_cow(band); + if (!band || !space) + goto error; + + band->mupa = isl_multi_union_pw_aff_align_params(band->mupa, + isl_space_copy(space)); + band->ast_build_options = + isl_union_set_align_params(band->ast_build_options, space); + if (!band->mupa || !band->ast_build_options) + return isl_schedule_band_free(band); + + return band; +error: + isl_space_free(space); + isl_schedule_band_free(band); + return NULL; +} + +/* Compute the pullback of "band" by the function represented by "upma". + * In other words, plug in "upma" in the iteration domains of "band". + */ +__isl_give isl_schedule_band *isl_schedule_band_pullback_union_pw_multi_aff( + __isl_take isl_schedule_band *band, + __isl_take isl_union_pw_multi_aff *upma) +{ + band = isl_schedule_band_cow(band); + if (!band || !upma) + goto error; + + band->mupa = + isl_multi_union_pw_aff_pullback_union_pw_multi_aff(band->mupa, + upma); + if (!band->mupa) + return isl_schedule_band_free(band); + + return band; +error: + isl_union_pw_multi_aff_free(upma); + isl_schedule_band_free(band); + return NULL; +} + +/* Compute the gist of "band" with respect to "context". + * In particular, compute the gist of the associated partial schedule. + */ +__isl_give isl_schedule_band *isl_schedule_band_gist( + __isl_take isl_schedule_band *band, __isl_take isl_union_set *context) +{ + if (!band || !context) + goto error; + if (band->n == 0) { + isl_union_set_free(context); + return band; + } + band = isl_schedule_band_cow(band); + if (!band) + goto error; + band->mupa = isl_multi_union_pw_aff_gist(band->mupa, context); + if (!band->mupa) + return isl_schedule_band_free(band); + return band; +error: + isl_union_set_free(context); + isl_schedule_band_free(band); + return NULL; +} Index: lib/Analysis/isl/isl_schedule_node.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_node.c @@ -0,0 +1,4713 @@ +/* + * Copyright 2013-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include + +/* Create a new schedule node in the given schedule, point at the given + * tree with given ancestors and child positions. + * "child_pos" may be NULL if there are no ancestors. + */ +__isl_give isl_schedule_node *isl_schedule_node_alloc( + __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree, + __isl_take isl_schedule_tree_list *ancestors, int *child_pos) +{ + isl_ctx *ctx; + isl_schedule_node *node; + int i, n; + + if (!schedule || !tree || !ancestors) + goto error; + n = isl_schedule_tree_list_n_schedule_tree(ancestors); + if (n > 0 && !child_pos) + goto error; + ctx = isl_schedule_get_ctx(schedule); + node = isl_calloc_type(ctx, isl_schedule_node); + if (!node) + goto error; + node->ref = 1; + node->schedule = schedule; + node->tree = tree; + node->ancestors = ancestors; + node->child_pos = isl_alloc_array(ctx, int, n); + if (n && !node->child_pos) + return isl_schedule_node_free(node); + for (i = 0; i < n; ++i) + node->child_pos[i] = child_pos[i]; + + return node; +error: + isl_schedule_free(schedule); + isl_schedule_tree_free(tree); + isl_schedule_tree_list_free(ancestors); + return NULL; +} + +/* Return a pointer to the root of a schedule tree with as single + * node a domain node with the given domain. + */ +__isl_give isl_schedule_node *isl_schedule_node_from_domain( + __isl_take isl_union_set *domain) +{ + isl_schedule *schedule; + isl_schedule_node *node; + + schedule = isl_schedule_from_domain(domain); + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + + return node; +} + +/* Return a pointer to the root of a schedule tree with as single + * node a extension node with the given extension. + */ +__isl_give isl_schedule_node *isl_schedule_node_from_extension( + __isl_take isl_union_map *extension) +{ + isl_ctx *ctx; + isl_schedule *schedule; + isl_schedule_tree *tree; + isl_schedule_node *node; + + if (!extension) + return NULL; + + ctx = isl_union_map_get_ctx(extension); + tree = isl_schedule_tree_from_extension(extension); + schedule = isl_schedule_from_schedule_tree(ctx, tree); + node = isl_schedule_get_root(schedule); + isl_schedule_free(schedule); + + return node; +} + +/* Return the isl_ctx to which "node" belongs. + */ +isl_ctx *isl_schedule_node_get_ctx(__isl_keep isl_schedule_node *node) +{ + return node ? isl_schedule_get_ctx(node->schedule) : NULL; +} + +/* Return a pointer to the leaf of the schedule into which "node" points. + */ +__isl_keep isl_schedule_tree *isl_schedule_node_peek_leaf( + __isl_keep isl_schedule_node *node) +{ + return node ? isl_schedule_peek_leaf(node->schedule) : NULL; +} + +/* Return a copy of the leaf of the schedule into which "node" points. + */ +__isl_give isl_schedule_tree *isl_schedule_node_get_leaf( + __isl_keep isl_schedule_node *node) +{ + return isl_schedule_tree_copy(isl_schedule_node_peek_leaf(node)); +} + +/* Return the type of the node or isl_schedule_node_error on error. + */ +enum isl_schedule_node_type isl_schedule_node_get_type( + __isl_keep isl_schedule_node *node) +{ + return node ? isl_schedule_tree_get_type(node->tree) + : isl_schedule_node_error; +} + +/* Return the type of the parent of "node" or isl_schedule_node_error on error. + */ +enum isl_schedule_node_type isl_schedule_node_get_parent_type( + __isl_keep isl_schedule_node *node) +{ + int pos; + int has_parent; + isl_schedule_tree *parent; + enum isl_schedule_node_type type; + + if (!node) + return isl_schedule_node_error; + has_parent = isl_schedule_node_has_parent(node); + if (has_parent < 0) + return isl_schedule_node_error; + if (!has_parent) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "node has no parent", return isl_schedule_node_error); + + pos = isl_schedule_tree_list_n_schedule_tree(node->ancestors) - 1; + parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors, pos); + type = isl_schedule_tree_get_type(parent); + isl_schedule_tree_free(parent); + + return type; +} + +/* Return a copy of the subtree that this node points to. + */ +__isl_give isl_schedule_tree *isl_schedule_node_get_tree( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_copy(node->tree); +} + +/* Return a copy of the schedule into which "node" points. + */ +__isl_give isl_schedule *isl_schedule_node_get_schedule( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + return isl_schedule_copy(node->schedule); +} + +/* Return a fresh copy of "node". + */ +__isl_take isl_schedule_node *isl_schedule_node_dup( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_node_alloc(isl_schedule_copy(node->schedule), + isl_schedule_tree_copy(node->tree), + isl_schedule_tree_list_copy(node->ancestors), + node->child_pos); +} + +/* Return an isl_schedule_node that is equal to "node" and that has only + * a single reference. + */ +__isl_give isl_schedule_node *isl_schedule_node_cow( + __isl_take isl_schedule_node *node) +{ + if (!node) + return NULL; + + if (node->ref == 1) + return node; + node->ref--; + return isl_schedule_node_dup(node); +} + +/* Return a new reference to "node". + */ +__isl_give isl_schedule_node *isl_schedule_node_copy( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + node->ref++; + return node; +} + +/* Free "node" and return NULL. + */ +__isl_null isl_schedule_node *isl_schedule_node_free( + __isl_take isl_schedule_node *node) +{ + if (!node) + return NULL; + if (--node->ref > 0) + return NULL; + + isl_schedule_tree_list_free(node->ancestors); + free(node->child_pos); + isl_schedule_tree_free(node->tree); + isl_schedule_free(node->schedule); + free(node); + + return NULL; +} + +/* Do "node1" and "node2" point to the same position in the same + * schedule? + */ +isl_bool isl_schedule_node_is_equal(__isl_keep isl_schedule_node *node1, + __isl_keep isl_schedule_node *node2) +{ + int i, n1, n2; + + if (!node1 || !node2) + return isl_bool_error; + if (node1 == node2) + return isl_bool_true; + if (node1->schedule != node2->schedule) + return isl_bool_false; + + n1 = isl_schedule_node_get_tree_depth(node1); + n2 = isl_schedule_node_get_tree_depth(node2); + if (n1 != n2) + return isl_bool_false; + for (i = 0; i < n1; ++i) + if (node1->child_pos[i] != node2->child_pos[i]) + return isl_bool_false; + + return isl_bool_true; +} + +/* Return the number of outer schedule dimensions of "node" + * in its schedule tree. + * + * Return -1 on error. + */ +int isl_schedule_node_get_schedule_depth(__isl_keep isl_schedule_node *node) +{ + int i, n; + int depth = 0; + + if (!node) + return -1; + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + for (i = n - 1; i >= 0; --i) { + isl_schedule_tree *tree; + + tree = isl_schedule_tree_list_get_schedule_tree( + node->ancestors, i); + if (!tree) + return -1; + if (tree->type == isl_schedule_node_band) + depth += isl_schedule_tree_band_n_member(tree); + isl_schedule_tree_free(tree); + } + + return depth; +} + +/* Internal data structure for + * isl_schedule_node_get_prefix_schedule_union_pw_multi_aff + * + * "initialized" is set if the filter field has been initialized. + * If "universe_domain" is not set, then the collected filter is intersected + * with the the domain of the root domain node. + * "universe_filter" is set if we are only collecting the universes of filters + * "collect_prefix" is set if we are collecting prefixes. + * "filter" collects all outer filters and is NULL until "initialized" is set. + * "prefix" collects all outer band partial schedules (if "collect_prefix" + * is set). If it is used, then it is initialized by the caller + * of collect_filter_prefix to a zero-dimensional function. + */ +struct isl_schedule_node_get_filter_prefix_data { + int initialized; + int universe_domain; + int universe_filter; + int collect_prefix; + isl_union_set *filter; + isl_multi_union_pw_aff *prefix; +}; + +static int collect_filter_prefix(__isl_keep isl_schedule_tree_list *list, + int n, struct isl_schedule_node_get_filter_prefix_data *data); + +/* Update the filter and prefix information in "data" based on the first "n" + * elements in "list" and the expansion tree root "tree". + * + * We first collect the information from the elements in "list", + * initializing the filter based on the domain of the expansion. + * Then we map the results to the expanded space and combined them + * with the results already in "data". + */ +static int collect_filter_prefix_expansion(__isl_take isl_schedule_tree *tree, + __isl_keep isl_schedule_tree_list *list, int n, + struct isl_schedule_node_get_filter_prefix_data *data) +{ + struct isl_schedule_node_get_filter_prefix_data contracted; + isl_union_pw_multi_aff *c; + isl_union_map *exp, *universe; + isl_union_set *filter; + + c = isl_schedule_tree_expansion_get_contraction(tree); + exp = isl_schedule_tree_expansion_get_expansion(tree); + + contracted.initialized = 1; + contracted.universe_domain = data->universe_domain; + contracted.universe_filter = data->universe_filter; + contracted.collect_prefix = data->collect_prefix; + universe = isl_union_map_universe(isl_union_map_copy(exp)); + filter = isl_union_map_domain(universe); + if (data->collect_prefix) { + isl_space *space = isl_union_set_get_space(filter); + space = isl_space_set_from_params(space); + contracted.prefix = isl_multi_union_pw_aff_zero(space); + } + contracted.filter = filter; + + if (collect_filter_prefix(list, n, &contracted) < 0) + contracted.filter = isl_union_set_free(contracted.filter); + if (data->collect_prefix) { + isl_multi_union_pw_aff *prefix; + + prefix = contracted.prefix; + prefix = + isl_multi_union_pw_aff_pullback_union_pw_multi_aff(prefix, + isl_union_pw_multi_aff_copy(c)); + data->prefix = isl_multi_union_pw_aff_flat_range_product( + prefix, data->prefix); + } + filter = contracted.filter; + if (data->universe_domain) + filter = isl_union_set_preimage_union_pw_multi_aff(filter, + isl_union_pw_multi_aff_copy(c)); + else + filter = isl_union_set_apply(filter, isl_union_map_copy(exp)); + if (!data->initialized) + data->filter = filter; + else + data->filter = isl_union_set_intersect(filter, data->filter); + data->initialized = 1; + + isl_union_pw_multi_aff_free(c); + isl_union_map_free(exp); + isl_schedule_tree_free(tree); + + return 0; +} + +/* Update the filter information in "data" based on the first "n" + * elements in "list" and the extension tree root "tree", in case + * data->universe_domain is set and data->collect_prefix is not. + * + * We collect the universe domain of the elements in "list" and + * add it to the universe range of the extension (intersected + * with the already collected filter, if any). + */ +static int collect_universe_domain_extension(__isl_take isl_schedule_tree *tree, + __isl_keep isl_schedule_tree_list *list, int n, + struct isl_schedule_node_get_filter_prefix_data *data) +{ + struct isl_schedule_node_get_filter_prefix_data data_outer; + isl_union_map *extension; + isl_union_set *filter; + + data_outer.initialized = 0; + data_outer.universe_domain = 1; + data_outer.universe_filter = data->universe_filter; + data_outer.collect_prefix = 0; + data_outer.filter = NULL; + data_outer.prefix = NULL; + + if (collect_filter_prefix(list, n, &data_outer) < 0) + data_outer.filter = isl_union_set_free(data_outer.filter); + + extension = isl_schedule_tree_extension_get_extension(tree); + extension = isl_union_map_universe(extension); + filter = isl_union_map_range(extension); + if (data_outer.initialized) + filter = isl_union_set_union(filter, data_outer.filter); + if (data->initialized) + filter = isl_union_set_intersect(filter, data->filter); + + data->filter = filter; + + isl_schedule_tree_free(tree); + + return 0; +} + +/* Update "data" based on the tree node "tree" in case "data" has + * not been initialized yet. + * + * Return 0 on success and -1 on error. + * + * If "tree" is a filter, then we set data->filter to this filter + * (or its universe). + * If "tree" is a domain, then this means we have reached the root + * of the schedule tree without being able to extract any information. + * We therefore initialize data->filter to the universe of the domain, + * or the domain itself if data->universe_domain is not set. + * If "tree" is a band with at least one member, then we set data->filter + * to the universe of the schedule domain and replace the zero-dimensional + * data->prefix by the band schedule (if data->collect_prefix is set). + */ +static int collect_filter_prefix_init(__isl_keep isl_schedule_tree *tree, + struct isl_schedule_node_get_filter_prefix_data *data) +{ + enum isl_schedule_node_type type; + isl_multi_union_pw_aff *mupa; + isl_union_set *filter; + + type = isl_schedule_tree_get_type(tree); + switch (type) { + case isl_schedule_node_error: + return -1; + case isl_schedule_node_expansion: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "should be handled by caller", return -1); + case isl_schedule_node_extension: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "cannot handle extension nodes", return -1); + case isl_schedule_node_context: + case isl_schedule_node_leaf: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + return 0; + case isl_schedule_node_domain: + filter = isl_schedule_tree_domain_get_domain(tree); + if (data->universe_domain) + filter = isl_union_set_universe(filter); + data->filter = filter; + break; + case isl_schedule_node_band: + if (isl_schedule_tree_band_n_member(tree) == 0) + return 0; + mupa = isl_schedule_tree_band_get_partial_schedule(tree); + if (data->collect_prefix) { + isl_multi_union_pw_aff_free(data->prefix); + mupa = isl_multi_union_pw_aff_reset_tuple_id(mupa, + isl_dim_set); + data->prefix = isl_multi_union_pw_aff_copy(mupa); + } + filter = isl_multi_union_pw_aff_domain(mupa); + filter = isl_union_set_universe(filter); + data->filter = filter; + break; + case isl_schedule_node_filter: + filter = isl_schedule_tree_filter_get_filter(tree); + if (data->universe_filter) + filter = isl_union_set_universe(filter); + data->filter = filter; + break; + } + + if ((data->collect_prefix && !data->prefix) || !data->filter) + return -1; + + data->initialized = 1; + + return 0; +} + +/* Update "data" based on the tree node "tree" in case "data" has + * already been initialized. + * + * Return 0 on success and -1 on error. + * + * If "tree" is a domain and data->universe_domain is not set, then + * intersect data->filter with the domain. + * If "tree" is a filter, then we intersect data->filter with this filter + * (or its universe). + * If "tree" is a band with at least one member and data->collect_prefix + * is set, then we extend data->prefix with the band schedule. + * If "tree" is an extension, then we make sure that we are not collecting + * information on any extended domain elements. + */ +static int collect_filter_prefix_update(__isl_keep isl_schedule_tree *tree, + struct isl_schedule_node_get_filter_prefix_data *data) +{ + enum isl_schedule_node_type type; + isl_multi_union_pw_aff *mupa; + isl_union_set *filter; + isl_union_map *extension; + int empty; + + type = isl_schedule_tree_get_type(tree); + switch (type) { + case isl_schedule_node_error: + return -1; + case isl_schedule_node_expansion: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "should be handled by caller", return -1); + case isl_schedule_node_extension: + extension = isl_schedule_tree_extension_get_extension(tree); + extension = isl_union_map_intersect_range(extension, + isl_union_set_copy(data->filter)); + empty = isl_union_map_is_empty(extension); + isl_union_map_free(extension); + if (empty < 0) + return -1; + if (empty) + break; + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "cannot handle extension nodes", return -1); + case isl_schedule_node_context: + case isl_schedule_node_leaf: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + case isl_schedule_node_domain: + if (data->universe_domain) + break; + filter = isl_schedule_tree_domain_get_domain(tree); + data->filter = isl_union_set_intersect(data->filter, filter); + break; + case isl_schedule_node_band: + if (isl_schedule_tree_band_n_member(tree) == 0) + break; + if (!data->collect_prefix) + break; + mupa = isl_schedule_tree_band_get_partial_schedule(tree); + data->prefix = isl_multi_union_pw_aff_flat_range_product(mupa, + data->prefix); + if (!data->prefix) + return -1; + break; + case isl_schedule_node_filter: + filter = isl_schedule_tree_filter_get_filter(tree); + if (data->universe_filter) + filter = isl_union_set_universe(filter); + data->filter = isl_union_set_intersect(data->filter, filter); + if (!data->filter) + return -1; + break; + } + + return 0; +} + +/* Collect filter and/or prefix information from the first "n" + * elements in "list" (which represent the ancestors of a node). + * Store the results in "data". + * + * Extension nodes are only supported if they do not affect the outcome, + * i.e., if we are collecting information on non-extended domain elements, + * or if we are collecting the universe domain (without prefix). + * + * Return 0 on success and -1 on error. + * + * We traverse the list from innermost ancestor (last element) + * to outermost ancestor (first element), calling collect_filter_prefix_init + * on each node as long as we have not been able to extract any information + * yet and collect_filter_prefix_update afterwards. + * If we come across an expansion node, then we interrupt the traversal + * and call collect_filter_prefix_expansion to restart the traversal + * over the remaining ancestors and to combine the results with those + * that have already been collected. + * If we come across an extension node and we are only computing + * the universe domain, then we interrupt the traversal and call + * collect_universe_domain_extension to restart the traversal + * over the remaining ancestors and to combine the results with those + * that have already been collected. + * On successful return, data->initialized will be set since the outermost + * ancestor is a domain node, which always results in an initialization. + */ +static int collect_filter_prefix(__isl_keep isl_schedule_tree_list *list, + int n, struct isl_schedule_node_get_filter_prefix_data *data) +{ + int i; + + if (!list) + return -1; + + for (i = n - 1; i >= 0; --i) { + isl_schedule_tree *tree; + enum isl_schedule_node_type type; + int r; + + tree = isl_schedule_tree_list_get_schedule_tree(list, i); + if (!tree) + return -1; + type = isl_schedule_tree_get_type(tree); + if (type == isl_schedule_node_expansion) + return collect_filter_prefix_expansion(tree, list, i, + data); + if (type == isl_schedule_node_extension && + data->universe_domain && !data->collect_prefix) + return collect_universe_domain_extension(tree, list, i, + data); + if (!data->initialized) + r = collect_filter_prefix_init(tree, data); + else + r = collect_filter_prefix_update(tree, data); + isl_schedule_tree_free(tree); + if (r < 0) + return -1; + } + + return 0; +} + +/* Return the concatenation of the partial schedules of all outer band + * nodes of "node" interesected with all outer filters + * as an isl_multi_union_pw_aff. + * None of the ancestors of "node" may be an extension node, unless + * there is also a filter ancestor that filters out all the extended + * domain elements. + * + * If "node" is pointing at the root of the schedule tree, then + * there are no domain elements reaching the current node, so + * we return an empty result. + * + * We collect all the filters and partial schedules in collect_filter_prefix + * and intersect the domain of the combined schedule with the combined filter. + */ +__isl_give isl_multi_union_pw_aff * +isl_schedule_node_get_prefix_schedule_multi_union_pw_aff( + __isl_keep isl_schedule_node *node) +{ + int n; + isl_space *space; + struct isl_schedule_node_get_filter_prefix_data data; + + if (!node) + return NULL; + + space = isl_schedule_get_space(node->schedule); + space = isl_space_set_from_params(space); + if (node->tree == node->schedule->root) + return isl_multi_union_pw_aff_zero(space); + + data.initialized = 0; + data.universe_domain = 1; + data.universe_filter = 0; + data.collect_prefix = 1; + data.filter = NULL; + data.prefix = isl_multi_union_pw_aff_zero(space); + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + if (collect_filter_prefix(node->ancestors, n, &data) < 0) + data.prefix = isl_multi_union_pw_aff_free(data.prefix); + + data.prefix = isl_multi_union_pw_aff_intersect_domain(data.prefix, + data.filter); + + return data.prefix; +} + +/* Return the concatenation of the partial schedules of all outer band + * nodes of "node" interesected with all outer filters + * as an isl_union_pw_multi_aff. + * None of the ancestors of "node" may be an extension node, unless + * there is also a filter ancestor that filters out all the extended + * domain elements. + * + * If "node" is pointing at the root of the schedule tree, then + * there are no domain elements reaching the current node, so + * we return an empty result. + * + * We collect all the filters and partial schedules in collect_filter_prefix. + * The partial schedules are collected as an isl_multi_union_pw_aff. + * If this isl_multi_union_pw_aff is zero-dimensional, then it does not + * contain any domain information, so we construct the isl_union_pw_multi_aff + * result as a zero-dimensional function on the collected filter. + * Otherwise, we convert the isl_multi_union_pw_aff to + * an isl_multi_union_pw_aff and intersect the domain with the filter. + */ +__isl_give isl_union_pw_multi_aff * +isl_schedule_node_get_prefix_schedule_union_pw_multi_aff( + __isl_keep isl_schedule_node *node) +{ + int n; + isl_space *space; + isl_union_pw_multi_aff *prefix; + struct isl_schedule_node_get_filter_prefix_data data; + + if (!node) + return NULL; + + space = isl_schedule_get_space(node->schedule); + if (node->tree == node->schedule->root) + return isl_union_pw_multi_aff_empty(space); + + space = isl_space_set_from_params(space); + data.initialized = 0; + data.universe_domain = 1; + data.universe_filter = 0; + data.collect_prefix = 1; + data.filter = NULL; + data.prefix = isl_multi_union_pw_aff_zero(space); + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + if (collect_filter_prefix(node->ancestors, n, &data) < 0) + data.prefix = isl_multi_union_pw_aff_free(data.prefix); + + if (data.prefix && + isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set) == 0) { + isl_multi_union_pw_aff_free(data.prefix); + prefix = isl_union_pw_multi_aff_from_domain(data.filter); + } else { + prefix = + isl_union_pw_multi_aff_from_multi_union_pw_aff(data.prefix); + prefix = isl_union_pw_multi_aff_intersect_domain(prefix, + data.filter); + } + + return prefix; +} + +/* Return the concatenation of the partial schedules of all outer band + * nodes of "node" interesected with all outer filters + * as an isl_union_map. + */ +__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_union_map( + __isl_keep isl_schedule_node *node) +{ + isl_union_pw_multi_aff *upma; + + upma = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node); + return isl_union_map_from_union_pw_multi_aff(upma); +} + +/* Return the concatenation of the partial schedules of all outer band + * nodes of "node" intersected with all outer domain constraints. + * None of the ancestors of "node" may be an extension node, unless + * there is also a filter ancestor that filters out all the extended + * domain elements. + * + * Essentially, this function intersects the domain of the output + * of isl_schedule_node_get_prefix_schedule_union_map with the output + * of isl_schedule_node_get_domain, except that it only traverses + * the ancestors of "node" once. + */ +__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation( + __isl_keep isl_schedule_node *node) +{ + int n; + isl_space *space; + isl_union_map *prefix; + struct isl_schedule_node_get_filter_prefix_data data; + + if (!node) + return NULL; + + space = isl_schedule_get_space(node->schedule); + if (node->tree == node->schedule->root) + return isl_union_map_empty(space); + + space = isl_space_set_from_params(space); + data.initialized = 0; + data.universe_domain = 0; + data.universe_filter = 0; + data.collect_prefix = 1; + data.filter = NULL; + data.prefix = isl_multi_union_pw_aff_zero(space); + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + if (collect_filter_prefix(node->ancestors, n, &data) < 0) + data.prefix = isl_multi_union_pw_aff_free(data.prefix); + + if (data.prefix && + isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set) == 0) { + isl_multi_union_pw_aff_free(data.prefix); + prefix = isl_union_map_from_domain(data.filter); + } else { + prefix = isl_union_map_from_multi_union_pw_aff(data.prefix); + prefix = isl_union_map_intersect_domain(prefix, data.filter); + } + + return prefix; +} + +/* Return the domain elements that reach "node". + * + * If "node" is pointing at the root of the schedule tree, then + * there are no domain elements reaching the current node, so + * we return an empty result. + * None of the ancestors of "node" may be an extension node, unless + * there is also a filter ancestor that filters out all the extended + * domain elements. + * + * Otherwise, we collect all filters reaching the node, + * intersected with the root domain in collect_filter_prefix. + */ +__isl_give isl_union_set *isl_schedule_node_get_domain( + __isl_keep isl_schedule_node *node) +{ + int n; + struct isl_schedule_node_get_filter_prefix_data data; + + if (!node) + return NULL; + + if (node->tree == node->schedule->root) { + isl_space *space; + + space = isl_schedule_get_space(node->schedule); + return isl_union_set_empty(space); + } + + data.initialized = 0; + data.universe_domain = 0; + data.universe_filter = 0; + data.collect_prefix = 0; + data.filter = NULL; + data.prefix = NULL; + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + if (collect_filter_prefix(node->ancestors, n, &data) < 0) + data.filter = isl_union_set_free(data.filter); + + return data.filter; +} + +/* Return the union of universe sets of the domain elements that reach "node". + * + * If "node" is pointing at the root of the schedule tree, then + * there are no domain elements reaching the current node, so + * we return an empty result. + * + * Otherwise, we collect the universes of all filters reaching the node + * in collect_filter_prefix. + */ +__isl_give isl_union_set *isl_schedule_node_get_universe_domain( + __isl_keep isl_schedule_node *node) +{ + int n; + struct isl_schedule_node_get_filter_prefix_data data; + + if (!node) + return NULL; + + if (node->tree == node->schedule->root) { + isl_space *space; + + space = isl_schedule_get_space(node->schedule); + return isl_union_set_empty(space); + } + + data.initialized = 0; + data.universe_domain = 1; + data.universe_filter = 1; + data.collect_prefix = 0; + data.filter = NULL; + data.prefix = NULL; + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + if (collect_filter_prefix(node->ancestors, n, &data) < 0) + data.filter = isl_union_set_free(data.filter); + + return data.filter; +} + +/* Return the subtree schedule of "node". + * + * Since isl_schedule_tree_get_subtree_schedule_union_map does not handle + * trees that do not contain any schedule information, we first + * move down to the first relevant descendant and handle leaves ourselves. + * + * If the subtree rooted at "node" contains any expansion nodes, then + * the returned subtree schedule is formulated in terms of the expanded + * domains. + * The subtree is not allowed to contain any extension nodes. + */ +__isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map( + __isl_keep isl_schedule_node *node) +{ + isl_schedule_tree *tree, *leaf; + isl_union_map *umap; + + tree = isl_schedule_node_get_tree(node); + leaf = isl_schedule_node_peek_leaf(node); + tree = isl_schedule_tree_first_schedule_descendant(tree, leaf); + if (!tree) + return NULL; + if (tree == leaf) { + isl_union_set *domain; + domain = isl_schedule_node_get_universe_domain(node); + isl_schedule_tree_free(tree); + return isl_union_map_from_domain(domain); + } + + umap = isl_schedule_tree_get_subtree_schedule_union_map(tree); + isl_schedule_tree_free(tree); + return umap; +} + +/* Return the number of ancestors of "node" in its schedule tree. + */ +int isl_schedule_node_get_tree_depth(__isl_keep isl_schedule_node *node) +{ + if (!node) + return -1; + return isl_schedule_tree_list_n_schedule_tree(node->ancestors); +} + +/* Does "node" have a parent? + * + * That is, does it point to any node of the schedule other than the root? + */ +isl_bool isl_schedule_node_has_parent(__isl_keep isl_schedule_node *node) +{ + if (!node) + return isl_bool_error; + if (!node->ancestors) + return isl_bool_error; + + return isl_schedule_tree_list_n_schedule_tree(node->ancestors) != 0; +} + +/* Return the position of "node" among the children of its parent. + */ +int isl_schedule_node_get_child_position(__isl_keep isl_schedule_node *node) +{ + int n; + int has_parent; + + if (!node) + return -1; + has_parent = isl_schedule_node_has_parent(node); + if (has_parent < 0) + return -1; + if (!has_parent) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "node has no parent", return -1); + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + return node->child_pos[n - 1]; +} + +/* Does the parent (if any) of "node" have any children with a smaller child + * position than this one? + */ +isl_bool isl_schedule_node_has_previous_sibling( + __isl_keep isl_schedule_node *node) +{ + int n; + isl_bool has_parent; + + if (!node) + return isl_bool_error; + has_parent = isl_schedule_node_has_parent(node); + if (has_parent < 0 || !has_parent) + return has_parent; + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + + return node->child_pos[n - 1] > 0; +} + +/* Does the parent (if any) of "node" have any children with a greater child + * position than this one? + */ +isl_bool isl_schedule_node_has_next_sibling(__isl_keep isl_schedule_node *node) +{ + int n, n_child; + isl_bool has_parent; + isl_schedule_tree *tree; + + if (!node) + return isl_bool_error; + has_parent = isl_schedule_node_has_parent(node); + if (has_parent < 0 || !has_parent) + return has_parent; + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, n - 1); + if (!tree) + return isl_bool_error; + n_child = isl_schedule_tree_list_n_schedule_tree(tree->children); + isl_schedule_tree_free(tree); + + return node->child_pos[n - 1] + 1 < n_child; +} + +/* Does "node" have any children? + * + * Any node other than the leaf nodes is considered to have at least + * one child, even if the corresponding isl_schedule_tree does not + * have any children. + */ +isl_bool isl_schedule_node_has_children(__isl_keep isl_schedule_node *node) +{ + if (!node) + return isl_bool_error; + return !isl_schedule_tree_is_leaf(node->tree); +} + +/* Return the number of children of "node"? + * + * Any node other than the leaf nodes is considered to have at least + * one child, even if the corresponding isl_schedule_tree does not + * have any children. That is, the number of children of "node" is + * only zero if its tree is the explicit empty tree. Otherwise, + * if the isl_schedule_tree has any children, then it is equal + * to the number of children of "node". If it has zero children, + * then "node" still has a leaf node as child. + */ +int isl_schedule_node_n_children(__isl_keep isl_schedule_node *node) +{ + int n; + + if (!node) + return -1; + + if (isl_schedule_tree_is_leaf(node->tree)) + return 0; + + n = isl_schedule_tree_n_children(node->tree); + if (n == 0) + return 1; + + return n; +} + +/* Move the "node" pointer to the ancestor of the given generation + * of the node it currently points to, where generation 0 is the node + * itself and generation 1 is its parent. + */ +__isl_give isl_schedule_node *isl_schedule_node_ancestor( + __isl_take isl_schedule_node *node, int generation) +{ + int n; + isl_schedule_tree *tree; + + if (!node) + return NULL; + if (generation == 0) + return node; + n = isl_schedule_node_get_tree_depth(node); + if (n < 0) + return isl_schedule_node_free(node); + if (generation < 0 || generation > n) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "generation out of bounds", + return isl_schedule_node_free(node)); + node = isl_schedule_node_cow(node); + if (!node) + return NULL; + + tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, + n - generation); + isl_schedule_tree_free(node->tree); + node->tree = tree; + node->ancestors = isl_schedule_tree_list_drop(node->ancestors, + n - generation, generation); + if (!node->ancestors || !node->tree) + return isl_schedule_node_free(node); + + return node; +} + +/* Move the "node" pointer to the parent of the node it currently points to. + */ +__isl_give isl_schedule_node *isl_schedule_node_parent( + __isl_take isl_schedule_node *node) +{ + if (!node) + return NULL; + if (!isl_schedule_node_has_parent(node)) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "node has no parent", + return isl_schedule_node_free(node)); + return isl_schedule_node_ancestor(node, 1); +} + +/* Move the "node" pointer to the root of its schedule tree. + */ +__isl_give isl_schedule_node *isl_schedule_node_root( + __isl_take isl_schedule_node *node) +{ + int n; + + if (!node) + return NULL; + n = isl_schedule_node_get_tree_depth(node); + if (n < 0) + return isl_schedule_node_free(node); + return isl_schedule_node_ancestor(node, n); +} + +/* Move the "node" pointer to the child at position "pos" of the node + * it currently points to. + */ +__isl_give isl_schedule_node *isl_schedule_node_child( + __isl_take isl_schedule_node *node, int pos) +{ + int n; + isl_ctx *ctx; + isl_schedule_tree *tree; + int *child_pos; + + node = isl_schedule_node_cow(node); + if (!node) + return NULL; + if (!isl_schedule_node_has_children(node)) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "node has no children", + return isl_schedule_node_free(node)); + + ctx = isl_schedule_node_get_ctx(node); + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + child_pos = isl_realloc_array(ctx, node->child_pos, int, n + 1); + if (!child_pos) + return isl_schedule_node_free(node); + node->child_pos = child_pos; + node->child_pos[n] = pos; + + node->ancestors = isl_schedule_tree_list_add(node->ancestors, + isl_schedule_tree_copy(node->tree)); + tree = node->tree; + if (isl_schedule_tree_has_children(tree)) + tree = isl_schedule_tree_get_child(tree, pos); + else + tree = isl_schedule_node_get_leaf(node); + isl_schedule_tree_free(node->tree); + node->tree = tree; + + if (!node->tree || !node->ancestors) + return isl_schedule_node_free(node); + + return node; +} + +/* Move the "node" pointer to the first child of the node + * it currently points to. + */ +__isl_give isl_schedule_node *isl_schedule_node_first_child( + __isl_take isl_schedule_node *node) +{ + return isl_schedule_node_child(node, 0); +} + +/* Move the "node" pointer to the child of this node's parent in + * the previous child position. + */ +__isl_give isl_schedule_node *isl_schedule_node_previous_sibling( + __isl_take isl_schedule_node *node) +{ + int n; + isl_schedule_tree *parent, *tree; + + node = isl_schedule_node_cow(node); + if (!node) + return NULL; + if (!isl_schedule_node_has_previous_sibling(node)) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "node has no previous sibling", + return isl_schedule_node_free(node)); + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors, + n - 1); + if (!parent) + return isl_schedule_node_free(node); + node->child_pos[n - 1]--; + tree = isl_schedule_tree_list_get_schedule_tree(parent->children, + node->child_pos[n - 1]); + isl_schedule_tree_free(parent); + if (!tree) + return isl_schedule_node_free(node); + isl_schedule_tree_free(node->tree); + node->tree = tree; + + return node; +} + +/* Move the "node" pointer to the child of this node's parent in + * the next child position. + */ +__isl_give isl_schedule_node *isl_schedule_node_next_sibling( + __isl_take isl_schedule_node *node) +{ + int n; + isl_schedule_tree *parent, *tree; + + node = isl_schedule_node_cow(node); + if (!node) + return NULL; + if (!isl_schedule_node_has_next_sibling(node)) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "node has no next sibling", + return isl_schedule_node_free(node)); + + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors, + n - 1); + if (!parent) + return isl_schedule_node_free(node); + node->child_pos[n - 1]++; + tree = isl_schedule_tree_list_get_schedule_tree(parent->children, + node->child_pos[n - 1]); + isl_schedule_tree_free(parent); + if (!tree) + return isl_schedule_node_free(node); + isl_schedule_tree_free(node->tree); + node->tree = tree; + + return node; +} + +/* Return a copy to the child at position "pos" of "node". + */ +__isl_give isl_schedule_node *isl_schedule_node_get_child( + __isl_keep isl_schedule_node *node, int pos) +{ + return isl_schedule_node_child(isl_schedule_node_copy(node), pos); +} + +/* Traverse the descendant of "node" in depth-first order, including + * "node" itself. Call "enter" whenever a node is entered and "leave" + * whenever a node is left. The callback "enter" is responsible + * for moving to the deepest initial subtree of its argument that + * should be traversed. + */ +static __isl_give isl_schedule_node *traverse( + __isl_take isl_schedule_node *node, + __isl_give isl_schedule_node *(*enter)( + __isl_take isl_schedule_node *node, void *user), + __isl_give isl_schedule_node *(*leave)( + __isl_take isl_schedule_node *node, void *user), + void *user) +{ + int depth; + + if (!node) + return NULL; + + depth = isl_schedule_node_get_tree_depth(node); + do { + node = enter(node, user); + node = leave(node, user); + while (node && isl_schedule_node_get_tree_depth(node) > depth && + !isl_schedule_node_has_next_sibling(node)) { + node = isl_schedule_node_parent(node); + node = leave(node, user); + } + if (node && isl_schedule_node_get_tree_depth(node) > depth) + node = isl_schedule_node_next_sibling(node); + } while (node && isl_schedule_node_get_tree_depth(node) > depth); + + return node; +} + +/* Internal data structure for isl_schedule_node_foreach_descendant_top_down. + * + * "fn" is the user-specified callback function. + * "user" is the user-specified argument for the callback. + */ +struct isl_schedule_node_preorder_data { + isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user); + void *user; +}; + +/* Callback for "traverse" to enter a node and to move + * to the deepest initial subtree that should be traversed + * for use in a preorder visit. + * + * If the user callback returns a negative value, then we abort + * the traversal. If this callback returns zero, then we skip + * the subtree rooted at the current node. Otherwise, we move + * down to the first child and repeat the process until a leaf + * is reached. + */ +static __isl_give isl_schedule_node *preorder_enter( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_schedule_node_preorder_data *data = user; + + if (!node) + return NULL; + + do { + isl_bool r; + + r = data->fn(node, data->user); + if (r < 0) + return isl_schedule_node_free(node); + if (r == isl_bool_false) + return node; + } while (isl_schedule_node_has_children(node) && + (node = isl_schedule_node_first_child(node)) != NULL); + + return node; +} + +/* Callback for "traverse" to leave a node + * for use in a preorder visit. + * Since we already visited the node when we entered it, + * we do not need to do anything here. + */ +static __isl_give isl_schedule_node *preorder_leave( + __isl_take isl_schedule_node *node, void *user) +{ + return node; +} + +/* Traverse the descendants of "node" (including the node itself) + * in depth first preorder. + * + * If "fn" returns -1 on any of the nodes, then the traversal is aborted. + * If "fn" returns 0 on any of the nodes, then the subtree rooted + * at that node is skipped. + * + * Return 0 on success and -1 on failure. + */ +isl_stat isl_schedule_node_foreach_descendant_top_down( + __isl_keep isl_schedule_node *node, + isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user), + void *user) +{ + struct isl_schedule_node_preorder_data data = { fn, user }; + + node = isl_schedule_node_copy(node); + node = traverse(node, &preorder_enter, &preorder_leave, &data); + isl_schedule_node_free(node); + + return node ? isl_stat_ok : isl_stat_error; +} + +/* Internal data structure for isl_schedule_node_map_descendant_bottom_up. + * + * "fn" is the user-specified callback function. + * "user" is the user-specified argument for the callback. + */ +struct isl_schedule_node_postorder_data { + __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node, + void *user); + void *user; +}; + +/* Callback for "traverse" to enter a node and to move + * to the deepest initial subtree that should be traversed + * for use in a postorder visit. + * + * Since we are performing a postorder visit, we only need + * to move to the deepest initial leaf here. + */ +static __isl_give isl_schedule_node *postorder_enter( + __isl_take isl_schedule_node *node, void *user) +{ + while (node && isl_schedule_node_has_children(node)) + node = isl_schedule_node_first_child(node); + + return node; +} + +/* Callback for "traverse" to leave a node + * for use in a postorder visit. + * + * Since we are performing a postorder visit, we need + * to call the user callback here. + */ +static __isl_give isl_schedule_node *postorder_leave( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_schedule_node_postorder_data *data = user; + + return data->fn(node, data->user); +} + +/* Traverse the descendants of "node" (including the node itself) + * in depth first postorder, allowing the user to modify the visited node. + * The traversal continues from the node returned by the callback function. + * It is the responsibility of the user to ensure that this does not + * lead to an infinite loop. It is safest to always return a pointer + * to the same position (same ancestors and child positions) as the input node. + */ +__isl_give isl_schedule_node *isl_schedule_node_map_descendant_bottom_up( + __isl_take isl_schedule_node *node, + __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node, + void *user), void *user) +{ + struct isl_schedule_node_postorder_data data = { fn, user }; + + return traverse(node, &postorder_enter, &postorder_leave, &data); +} + +/* Traverse the ancestors of "node" from the root down to and including + * the parent of "node", calling "fn" on each of them. + * + * If "fn" returns -1 on any of the nodes, then the traversal is aborted. + * + * Return 0 on success and -1 on failure. + */ +isl_stat isl_schedule_node_foreach_ancestor_top_down( + __isl_keep isl_schedule_node *node, + isl_stat (*fn)(__isl_keep isl_schedule_node *node, void *user), + void *user) +{ + int i, n; + + if (!node) + return isl_stat_error; + + n = isl_schedule_node_get_tree_depth(node); + for (i = 0; i < n; ++i) { + isl_schedule_node *ancestor; + isl_stat r; + + ancestor = isl_schedule_node_copy(node); + ancestor = isl_schedule_node_ancestor(ancestor, n - i); + r = fn(ancestor, user); + isl_schedule_node_free(ancestor); + if (r < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Is any node in the subtree rooted at "node" anchored? + * That is, do any of these nodes reference the outer band nodes? + */ +isl_bool isl_schedule_node_is_subtree_anchored( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return isl_bool_error; + return isl_schedule_tree_is_subtree_anchored(node->tree); +} + +/* Return the number of members in the given band node. + */ +unsigned isl_schedule_node_band_n_member(__isl_keep isl_schedule_node *node) +{ + return node ? isl_schedule_tree_band_n_member(node->tree) : 0; +} + +/* Is the band member at position "pos" of the band node "node" + * marked coincident? + */ +isl_bool isl_schedule_node_band_member_get_coincident( + __isl_keep isl_schedule_node *node, int pos) +{ + if (!node) + return isl_bool_error; + return isl_schedule_tree_band_member_get_coincident(node->tree, pos); +} + +/* Mark the band member at position "pos" the band node "node" + * as being coincident or not according to "coincident". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_member_set_coincident( + __isl_take isl_schedule_node *node, int pos, int coincident) +{ + int c; + isl_schedule_tree *tree; + + if (!node) + return NULL; + c = isl_schedule_node_band_member_get_coincident(node, pos); + if (c == coincident) + return node; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_band_member_set_coincident(tree, pos, + coincident); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Is the band node "node" marked permutable? + */ +isl_bool isl_schedule_node_band_get_permutable( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return isl_bool_error; + + return isl_schedule_tree_band_get_permutable(node->tree); +} + +/* Mark the band node "node" permutable or not according to "permutable"? + */ +__isl_give isl_schedule_node *isl_schedule_node_band_set_permutable( + __isl_take isl_schedule_node *node, int permutable) +{ + isl_schedule_tree *tree; + + if (!node) + return NULL; + if (isl_schedule_node_band_get_permutable(node) == permutable) + return node; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_band_set_permutable(tree, permutable); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Return the schedule space of the band node. + */ +__isl_give isl_space *isl_schedule_node_band_get_space( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_band_get_space(node->tree); +} + +/* Return the schedule of the band node in isolation. + */ +__isl_give isl_multi_union_pw_aff *isl_schedule_node_band_get_partial_schedule( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_band_get_partial_schedule(node->tree); +} + +/* Return the schedule of the band node in isolation in the form of + * an isl_union_map. + * + * If the band does not have any members, then we construct a universe map + * with the universe of the domain elements reaching the node as domain. + * Otherwise, we extract an isl_multi_union_pw_aff representation and + * convert that to an isl_union_map. + */ +__isl_give isl_union_map *isl_schedule_node_band_get_partial_schedule_union_map( + __isl_keep isl_schedule_node *node) +{ + isl_multi_union_pw_aff *mupa; + + if (!node) + return NULL; + + if (isl_schedule_node_get_type(node) != isl_schedule_node_band) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a band node", return NULL); + if (isl_schedule_node_band_n_member(node) == 0) { + isl_union_set *domain; + + domain = isl_schedule_node_get_universe_domain(node); + return isl_union_map_from_domain(domain); + } + + mupa = isl_schedule_node_band_get_partial_schedule(node); + return isl_union_map_from_multi_union_pw_aff(mupa); +} + +/* Return the loop AST generation type for the band member of band node "node" + * at position "pos". + */ +enum isl_ast_loop_type isl_schedule_node_band_member_get_ast_loop_type( + __isl_keep isl_schedule_node *node, int pos) +{ + if (!node) + return isl_ast_loop_error; + + return isl_schedule_tree_band_member_get_ast_loop_type(node->tree, pos); +} + +/* Set the loop AST generation type for the band member of band node "node" + * at position "pos" to "type". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_member_set_ast_loop_type( + __isl_take isl_schedule_node *node, int pos, + enum isl_ast_loop_type type) +{ + isl_schedule_tree *tree; + + if (!node) + return NULL; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_band_member_set_ast_loop_type(tree, pos, type); + return isl_schedule_node_graft_tree(node, tree); +} + +/* Return the loop AST generation type for the band member of band node "node" + * at position "pos" for the isolated part. + */ +enum isl_ast_loop_type isl_schedule_node_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_node *node, int pos) +{ + if (!node) + return isl_ast_loop_error; + + return isl_schedule_tree_band_member_get_isolate_ast_loop_type( + node->tree, pos); +} + +/* Set the loop AST generation type for the band member of band node "node" + * at position "pos" for the isolated part to "type". + */ +__isl_give isl_schedule_node * +isl_schedule_node_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_node *node, int pos, + enum isl_ast_loop_type type) +{ + isl_schedule_tree *tree; + + if (!node) + return NULL; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_band_member_set_isolate_ast_loop_type(tree, + pos, type); + return isl_schedule_node_graft_tree(node, tree); +} + +/* Return the AST build options associated to band node "node". + */ +__isl_give isl_union_set *isl_schedule_node_band_get_ast_build_options( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_band_get_ast_build_options(node->tree); +} + +/* Replace the AST build options associated to band node "node" by "options". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_set_ast_build_options( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *options) +{ + isl_schedule_tree *tree; + + if (!node || !options) + goto error; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_band_set_ast_build_options(tree, options); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_schedule_node_free(node); + isl_union_set_free(options); + return NULL; +} + +/* Return the "isolate" option associated to band node "node". + */ +__isl_give isl_set *isl_schedule_node_band_get_ast_isolate_option( + __isl_keep isl_schedule_node *node) +{ + int depth; + + if (!node) + return NULL; + + depth = isl_schedule_node_get_schedule_depth(node); + return isl_schedule_tree_band_get_ast_isolate_option(node->tree, depth); +} + +/* Make sure that that spaces of "node" and "mv" are the same. + * Return -1 on error, reporting the error to the user. + */ +static int check_space_multi_val(__isl_keep isl_schedule_node *node, + __isl_keep isl_multi_val *mv) +{ + isl_space *node_space, *mv_space; + int equal; + + node_space = isl_schedule_node_band_get_space(node); + mv_space = isl_multi_val_get_space(mv); + equal = isl_space_tuple_is_equal(node_space, isl_dim_set, + mv_space, isl_dim_set); + isl_space_free(mv_space); + isl_space_free(node_space); + if (equal < 0) + return -1; + if (!equal) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "spaces don't match", return -1); + + return 0; +} + +/* Multiply the partial schedule of the band node "node" + * with the factors in "mv". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_scale( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv) +{ + isl_schedule_tree *tree; + int anchored; + + if (!node || !mv) + goto error; + if (check_space_multi_val(node, mv) < 0) + goto error; + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot scale band node with anchored subtree", + goto error); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_scale(tree, mv); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_multi_val_free(mv); + isl_schedule_node_free(node); + return NULL; +} + +/* Divide the partial schedule of the band node "node" + * by the factors in "mv". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_scale_down( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv) +{ + isl_schedule_tree *tree; + int anchored; + + if (!node || !mv) + goto error; + if (check_space_multi_val(node, mv) < 0) + goto error; + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot scale down band node with anchored subtree", + goto error); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_scale_down(tree, mv); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_multi_val_free(mv); + isl_schedule_node_free(node); + return NULL; +} + +/* Reduce the partial schedule of the band node "node" + * modulo the factors in "mv". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_mod( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv) +{ + isl_schedule_tree *tree; + isl_bool anchored; + + if (!node || !mv) + goto error; + if (check_space_multi_val(node, mv) < 0) + goto error; + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot perform mod on band node with anchored subtree", + goto error); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_mod(tree, mv); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_multi_val_free(mv); + isl_schedule_node_free(node); + return NULL; +} + +/* Make sure that that spaces of "node" and "mupa" are the same. + * Return isl_stat_error on error, reporting the error to the user. + */ +static isl_stat check_space_multi_union_pw_aff( + __isl_keep isl_schedule_node *node, + __isl_keep isl_multi_union_pw_aff *mupa) +{ + isl_space *node_space, *mupa_space; + isl_bool equal; + + node_space = isl_schedule_node_band_get_space(node); + mupa_space = isl_multi_union_pw_aff_get_space(mupa); + equal = isl_space_tuple_is_equal(node_space, isl_dim_set, + mupa_space, isl_dim_set); + isl_space_free(mupa_space); + isl_space_free(node_space); + if (equal < 0) + return isl_stat_error; + if (!equal) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "spaces don't match", return isl_stat_error); + + return isl_stat_ok; +} + +/* Shift the partial schedule of the band node "node" by "shift". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_shift( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_union_pw_aff *shift) +{ + isl_schedule_tree *tree; + int anchored; + + if (!node || !shift) + goto error; + if (check_space_multi_union_pw_aff(node, shift) < 0) + goto error; + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot shift band node with anchored subtree", + goto error); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_shift(tree, shift); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_multi_union_pw_aff_free(shift); + isl_schedule_node_free(node); + return NULL; +} + +/* Tile "node" with tile sizes "sizes". + * + * The current node is replaced by two nested nodes corresponding + * to the tile dimensions and the point dimensions. + * + * Return a pointer to the outer (tile) node. + * + * If any of the descendants of "node" depend on the set of outer band nodes, + * then we refuse to tile the node. + * + * If the scale tile loops option is set, then the tile loops + * are scaled by the tile sizes. If the shift point loops option is set, + * then the point loops are shifted to start at zero. + * In particular, these options affect the tile and point loop schedules + * as follows + * + * scale shift original tile point + * + * 0 0 i floor(i/s) i + * 1 0 i s * floor(i/s) i + * 0 1 i floor(i/s) i - s * floor(i/s) + * 1 1 i s * floor(i/s) i - s * floor(i/s) + */ +__isl_give isl_schedule_node *isl_schedule_node_band_tile( + __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes) +{ + isl_schedule_tree *tree; + int anchored; + + if (!node || !sizes) + goto error; + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot tile band node with anchored subtree", + goto error); + + if (check_space_multi_val(node, sizes) < 0) + goto error; + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_tile(tree, sizes); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_multi_val_free(sizes); + isl_schedule_node_free(node); + return NULL; +} + +/* Move the band node "node" down to all the leaves in the subtree + * rooted at "node". + * Return a pointer to the node in the resulting tree that is in the same + * position as the node pointed to by "node" in the original tree. + * + * If the node only has a leaf child, then nothing needs to be done. + * Otherwise, the child of the node is removed and the result is + * appended to all the leaves in the subtree rooted at the original child. + * Since the node is moved to the leaves, it needs to be expanded + * according to the expansion, if any, defined by that subtree. + * In the end, the original node is replaced by the result of + * attaching copies of the expanded node to the leaves. + * + * If any of the nodes in the subtree rooted at "node" depend on + * the set of outer band nodes then we refuse to sink the band node. + */ +__isl_give isl_schedule_node *isl_schedule_node_band_sink( + __isl_take isl_schedule_node *node) +{ + enum isl_schedule_node_type type; + isl_schedule_tree *tree, *child; + isl_union_pw_multi_aff *contraction; + int anchored; + + if (!node) + return NULL; + + type = isl_schedule_node_get_type(node); + if (type != isl_schedule_node_band) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a band node", isl_schedule_node_free(node)); + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + return isl_schedule_node_free(node); + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot sink band node in anchored subtree", + isl_schedule_node_free(node)); + if (isl_schedule_tree_n_children(node->tree) == 0) + return node; + + contraction = isl_schedule_node_get_subtree_contraction(node); + + tree = isl_schedule_node_get_tree(node); + child = isl_schedule_tree_get_child(tree, 0); + tree = isl_schedule_tree_reset_children(tree); + tree = isl_schedule_tree_pullback_union_pw_multi_aff(tree, contraction); + tree = isl_schedule_tree_append_to_leaves(child, tree); + + return isl_schedule_node_graft_tree(node, tree); +} + +/* Split "node" into two nested band nodes, one with the first "pos" + * dimensions and one with the remaining dimensions. + * The schedules of the two band nodes live in anonymous spaces. + * The loop AST generation type options and the isolate option + * are split over the the two band nodes. + */ +__isl_give isl_schedule_node *isl_schedule_node_band_split( + __isl_take isl_schedule_node *node, int pos) +{ + int depth; + isl_schedule_tree *tree; + + depth = isl_schedule_node_get_schedule_depth(node); + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_split(tree, pos, depth); + return isl_schedule_node_graft_tree(node, tree); +} + +/* Return the context of the context node "node". + */ +__isl_give isl_set *isl_schedule_node_context_get_context( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_context_get_context(node->tree); +} + +/* Return the domain of the domain node "node". + */ +__isl_give isl_union_set *isl_schedule_node_domain_get_domain( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_domain_get_domain(node->tree); +} + +/* Return the expansion map of expansion node "node". + */ +__isl_give isl_union_map *isl_schedule_node_expansion_get_expansion( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_expansion_get_expansion(node->tree); +} + +/* Return the contraction of expansion node "node". + */ +__isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_expansion_get_contraction(node->tree); +} + +/* Replace the contraction and the expansion of the expansion node "node" + * by "contraction" and "expansion". + */ +__isl_give isl_schedule_node * +isl_schedule_node_expansion_set_contraction_and_expansion( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion) +{ + isl_schedule_tree *tree; + + if (!node || !contraction || !expansion) + goto error; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree, + contraction, expansion); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_schedule_node_free(node); + isl_union_pw_multi_aff_free(contraction); + isl_union_map_free(expansion); + return NULL; +} + +/* Return the extension of the extension node "node". + */ +__isl_give isl_union_map *isl_schedule_node_extension_get_extension( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_extension_get_extension(node->tree); +} + +/* Replace the extension of extension node "node" by "extension". + */ +__isl_give isl_schedule_node *isl_schedule_node_extension_set_extension( + __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension) +{ + isl_schedule_tree *tree; + + if (!node || !extension) + goto error; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_extension_set_extension(tree, extension); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_schedule_node_free(node); + isl_union_map_free(extension); + return NULL; +} + +/* Return the filter of the filter node "node". + */ +__isl_give isl_union_set *isl_schedule_node_filter_get_filter( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_filter_get_filter(node->tree); +} + +/* Replace the filter of filter node "node" by "filter". + */ +__isl_give isl_schedule_node *isl_schedule_node_filter_set_filter( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter) +{ + isl_schedule_tree *tree; + + if (!node || !filter) + goto error; + + tree = isl_schedule_tree_copy(node->tree); + tree = isl_schedule_tree_filter_set_filter(tree, filter); + return isl_schedule_node_graft_tree(node, tree); +error: + isl_schedule_node_free(node); + isl_union_set_free(filter); + return NULL; +} + +/* Intersect the filter of filter node "node" with "filter". + * + * If the filter of the node is already a subset of "filter", + * then leave the node unchanged. + */ +__isl_give isl_schedule_node *isl_schedule_node_filter_intersect_filter( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter) +{ + isl_union_set *node_filter = NULL; + isl_bool subset; + + if (!node || !filter) + goto error; + + node_filter = isl_schedule_node_filter_get_filter(node); + subset = isl_union_set_is_subset(node_filter, filter); + if (subset < 0) + goto error; + if (subset) { + isl_union_set_free(node_filter); + isl_union_set_free(filter); + return node; + } + node_filter = isl_union_set_intersect(node_filter, filter); + node = isl_schedule_node_filter_set_filter(node, node_filter); + return node; +error: + isl_schedule_node_free(node); + isl_union_set_free(node_filter); + isl_union_set_free(filter); + return NULL; +} + +/* Return the guard of the guard node "node". + */ +__isl_give isl_set *isl_schedule_node_guard_get_guard( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_guard_get_guard(node->tree); +} + +/* Return the mark identifier of the mark node "node". + */ +__isl_give isl_id *isl_schedule_node_mark_get_id( + __isl_keep isl_schedule_node *node) +{ + if (!node) + return NULL; + + return isl_schedule_tree_mark_get_id(node->tree); +} + +/* Replace the child at position "pos" of the sequence node "node" + * by the children of sequence root node of "tree". + */ +__isl_give isl_schedule_node *isl_schedule_node_sequence_splice( + __isl_take isl_schedule_node *node, int pos, + __isl_take isl_schedule_tree *tree) +{ + isl_schedule_tree *node_tree; + + if (!node || !tree) + goto error; + if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a sequence node", goto error); + if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a sequence node", goto error); + node_tree = isl_schedule_node_get_tree(node); + node_tree = isl_schedule_tree_sequence_splice(node_tree, pos, tree); + node = isl_schedule_node_graft_tree(node, node_tree); + + return node; +error: + isl_schedule_node_free(node); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Given a sequence node "node", with a child at position "pos" that + * is also a sequence node, attach the children of that node directly + * as children of "node" at that position, replacing the original child. + * + * The filters of these children are intersected with the filter + * of the child at position "pos". + */ +__isl_give isl_schedule_node *isl_schedule_node_sequence_splice_child( + __isl_take isl_schedule_node *node, int pos) +{ + int i, n; + isl_union_set *filter; + isl_schedule_node *child; + isl_schedule_tree *tree; + + if (!node) + return NULL; + if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a sequence node", isl_schedule_node_free(node)); + node = isl_schedule_node_child(node, pos); + node = isl_schedule_node_child(node, 0); + if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a sequence node", isl_schedule_node_free(node)); + child = isl_schedule_node_copy(node); + node = isl_schedule_node_parent(node); + filter = isl_schedule_node_filter_get_filter(node); + n = isl_schedule_node_n_children(child); + for (i = 0; i < n; ++i) { + child = isl_schedule_node_child(child, i); + child = isl_schedule_node_filter_intersect_filter(child, + isl_union_set_copy(filter)); + child = isl_schedule_node_parent(child); + } + isl_union_set_free(filter); + tree = isl_schedule_node_get_tree(child); + isl_schedule_node_free(child); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_sequence_splice(node, pos, tree); + + return node; +} + +/* Update the ancestors of "node" to point to the tree that "node" + * now points to. + * That is, replace the child in the original parent that corresponds + * to the current tree position by node->tree and continue updating + * the ancestors in the same way until the root is reached. + * + * If "fn" is not NULL, then it is called on each ancestor as we move up + * the tree so that it can modify the ancestor before it is added + * to the list of ancestors of the modified node. + * The additional "pos" argument records the position + * of the "tree" argument in the original schedule tree. + * + * If "node" originally points to a leaf of the schedule tree, then make sure + * that in the end it points to a leaf in the updated schedule tree. + */ +static __isl_give isl_schedule_node *update_ancestors( + __isl_take isl_schedule_node *node, + __isl_give isl_schedule_tree *(*fn)(__isl_take isl_schedule_tree *tree, + __isl_keep isl_schedule_node *pos, void *user), void *user) +{ + int i, n; + int is_leaf; + isl_ctx *ctx; + isl_schedule_tree *tree; + isl_schedule_node *pos = NULL; + + if (fn) + pos = isl_schedule_node_copy(node); + + node = isl_schedule_node_cow(node); + if (!node) + return isl_schedule_node_free(pos); + + ctx = isl_schedule_node_get_ctx(node); + n = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + tree = isl_schedule_tree_copy(node->tree); + + for (i = n - 1; i >= 0; --i) { + isl_schedule_tree *parent; + + parent = isl_schedule_tree_list_get_schedule_tree( + node->ancestors, i); + parent = isl_schedule_tree_replace_child(parent, + node->child_pos[i], tree); + if (fn) { + pos = isl_schedule_node_parent(pos); + parent = fn(parent, pos, user); + } + node->ancestors = isl_schedule_tree_list_set_schedule_tree( + node->ancestors, i, isl_schedule_tree_copy(parent)); + + tree = parent; + } + + if (fn) + isl_schedule_node_free(pos); + + is_leaf = isl_schedule_tree_is_leaf(node->tree); + node->schedule = isl_schedule_set_root(node->schedule, tree); + if (is_leaf) { + isl_schedule_tree_free(node->tree); + node->tree = isl_schedule_node_get_leaf(node); + } + + if (!node->schedule || !node->ancestors) + return isl_schedule_node_free(node); + + return node; +} + +/* Replace the subtree that "pos" points to by "tree", updating + * the ancestors to maintain a consistent state. + */ +__isl_give isl_schedule_node *isl_schedule_node_graft_tree( + __isl_take isl_schedule_node *pos, __isl_take isl_schedule_tree *tree) +{ + if (!tree || !pos) + goto error; + if (pos->tree == tree) { + isl_schedule_tree_free(tree); + return pos; + } + + pos = isl_schedule_node_cow(pos); + if (!pos) + goto error; + + isl_schedule_tree_free(pos->tree); + pos->tree = tree; + + return update_ancestors(pos, NULL, NULL); +error: + isl_schedule_node_free(pos); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Make sure we can insert a node between "node" and its parent. + * Return -1 on error, reporting the reason why we cannot insert a node. + */ +static int check_insert(__isl_keep isl_schedule_node *node) +{ + int has_parent; + enum isl_schedule_node_type type; + + has_parent = isl_schedule_node_has_parent(node); + if (has_parent < 0) + return -1; + if (!has_parent) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot insert node outside of root", return -1); + + type = isl_schedule_node_get_parent_type(node); + if (type == isl_schedule_node_error) + return -1; + if (type == isl_schedule_node_set || type == isl_schedule_node_sequence) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot insert node between set or sequence node " + "and its filter children", return -1); + + return 0; +} + +/* Insert a band node with partial schedule "mupa" between "node" and + * its parent. + * Return a pointer to the new band node. + * + * If any of the nodes in the subtree rooted at "node" depend on + * the set of outer band nodes then we refuse to insert the band node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_partial_schedule( + __isl_take isl_schedule_node *node, + __isl_take isl_multi_union_pw_aff *mupa) +{ + int anchored; + isl_schedule_band *band; + isl_schedule_tree *tree; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + goto error; + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot insert band node in anchored subtree", + goto error); + + tree = isl_schedule_node_get_tree(node); + band = isl_schedule_band_from_multi_union_pw_aff(mupa); + tree = isl_schedule_tree_insert_band(tree, band); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +error: + isl_schedule_node_free(node); + isl_multi_union_pw_aff_free(mupa); + return NULL; +} + +/* Insert a context node with context "context" between "node" and its parent. + * Return a pointer to the new context node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_context( + __isl_take isl_schedule_node *node, __isl_take isl_set *context) +{ + isl_schedule_tree *tree; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_insert_context(tree, context); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Insert an expansion node with the given "contraction" and "expansion" + * between "node" and its parent. + * Return a pointer to the new expansion node. + * + * Typically the domain and range spaces of the expansion are different. + * This means that only one of them can refer to the current domain space + * in a consistent tree. It is up to the caller to ensure that the tree + * returns to a consistent state. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_expansion( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion) +{ + isl_schedule_tree *tree; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Insert an extension node with extension "extension" between "node" and + * its parent. + * Return a pointer to the new extension node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_extension( + __isl_take isl_schedule_node *node, + __isl_take isl_union_map *extension) +{ + isl_schedule_tree *tree; + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_insert_extension(tree, extension); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Insert a filter node with filter "filter" between "node" and its parent. + * Return a pointer to the new filter node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_filter( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter) +{ + isl_schedule_tree *tree; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_insert_filter(tree, filter); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Insert a guard node with guard "guard" between "node" and its parent. + * Return a pointer to the new guard node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_guard( + __isl_take isl_schedule_node *node, __isl_take isl_set *guard) +{ + isl_schedule_tree *tree; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_insert_guard(tree, guard); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Insert a mark node with mark identifier "mark" between "node" and + * its parent. + * Return a pointer to the new mark node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_mark( + __isl_take isl_schedule_node *node, __isl_take isl_id *mark) +{ + isl_schedule_tree *tree; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_insert_mark(tree, mark); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Attach the current subtree of "node" to a sequence of filter tree nodes + * with filters described by "filters", attach this sequence + * of filter tree nodes as children to a new tree of type "type" and + * replace the original subtree of "node" by this new tree. + * Each copy of the original subtree is simplified with respect + * to the corresponding filter. + */ +static __isl_give isl_schedule_node *isl_schedule_node_insert_children( + __isl_take isl_schedule_node *node, + enum isl_schedule_node_type type, + __isl_take isl_union_set_list *filters) +{ + int i, n; + isl_ctx *ctx; + isl_schedule_tree *tree; + isl_schedule_tree_list *list; + + if (check_insert(node) < 0) + node = isl_schedule_node_free(node); + + if (!node || !filters) + goto error; + + ctx = isl_schedule_node_get_ctx(node); + n = isl_union_set_list_n_union_set(filters); + list = isl_schedule_tree_list_alloc(ctx, n); + for (i = 0; i < n; ++i) { + isl_schedule_node *node_i; + isl_schedule_tree *tree; + isl_union_set *filter; + + filter = isl_union_set_list_get_union_set(filters, i); + node_i = isl_schedule_node_copy(node); + node_i = isl_schedule_node_gist(node_i, + isl_union_set_copy(filter)); + tree = isl_schedule_node_get_tree(node_i); + isl_schedule_node_free(node_i); + tree = isl_schedule_tree_insert_filter(tree, filter); + list = isl_schedule_tree_list_add(list, tree); + } + tree = isl_schedule_tree_from_children(type, list); + node = isl_schedule_node_graft_tree(node, tree); + + isl_union_set_list_free(filters); + return node; +error: + isl_union_set_list_free(filters); + isl_schedule_node_free(node); + return NULL; +} + +/* Insert a sequence node with child filters "filters" between "node" and + * its parent. That is, the tree that "node" points to is attached + * to each of the child nodes of the filter nodes. + * Return a pointer to the new sequence node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_sequence( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set_list *filters) +{ + return isl_schedule_node_insert_children(node, + isl_schedule_node_sequence, filters); +} + +/* Insert a set node with child filters "filters" between "node" and + * its parent. That is, the tree that "node" points to is attached + * to each of the child nodes of the filter nodes. + * Return a pointer to the new set node. + */ +__isl_give isl_schedule_node *isl_schedule_node_insert_set( + __isl_take isl_schedule_node *node, + __isl_take isl_union_set_list *filters) +{ + return isl_schedule_node_insert_children(node, + isl_schedule_node_set, filters); +} + +/* Remove "node" from its schedule tree and return a pointer + * to the leaf at the same position in the updated schedule tree. + * + * It is not allowed to remove the root of a schedule tree or + * a child of a set or sequence node. + */ +__isl_give isl_schedule_node *isl_schedule_node_cut( + __isl_take isl_schedule_node *node) +{ + isl_schedule_tree *leaf; + enum isl_schedule_node_type parent_type; + + if (!node) + return NULL; + if (!isl_schedule_node_has_parent(node)) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot cut root", return isl_schedule_node_free(node)); + + parent_type = isl_schedule_node_get_parent_type(node); + if (parent_type == isl_schedule_node_set || + parent_type == isl_schedule_node_sequence) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot cut child of set or sequence", + return isl_schedule_node_free(node)); + + leaf = isl_schedule_node_get_leaf(node); + return isl_schedule_node_graft_tree(node, leaf); +} + +/* Remove a single node from the schedule tree, attaching the child + * of "node" directly to its parent. + * Return a pointer to this former child or to the leaf the position + * of the original node if there was no child. + * It is not allowed to remove the root of a schedule tree, + * a set or sequence node, a child of a set or sequence node or + * a band node with an anchored subtree. + */ +__isl_give isl_schedule_node *isl_schedule_node_delete( + __isl_take isl_schedule_node *node) +{ + int n; + isl_schedule_tree *tree; + enum isl_schedule_node_type type; + + if (!node) + return NULL; + + if (isl_schedule_node_get_tree_depth(node) == 0) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot delete root node", + return isl_schedule_node_free(node)); + n = isl_schedule_node_n_children(node); + if (n != 1) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "can only delete node with a single child", + return isl_schedule_node_free(node)); + type = isl_schedule_node_get_parent_type(node); + if (type == isl_schedule_node_sequence || type == isl_schedule_node_set) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "cannot delete child of set or sequence", + return isl_schedule_node_free(node)); + if (isl_schedule_node_get_type(node) == isl_schedule_node_band) { + int anchored; + + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + return isl_schedule_node_free(node); + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), + isl_error_invalid, + "cannot delete band node with anchored subtree", + return isl_schedule_node_free(node)); + } + + tree = isl_schedule_node_get_tree(node); + if (!tree || isl_schedule_tree_has_children(tree)) { + tree = isl_schedule_tree_child(tree, 0); + } else { + isl_schedule_tree_free(tree); + tree = isl_schedule_node_get_leaf(node); + } + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Internal data structure for the group_ancestor callback. + * + * If "finished" is set, then we no longer need to modify + * any further ancestors. + * + * "contraction" and "expansion" represent the expansion + * that reflects the grouping. + * + * "domain" contains the domain elements that reach the position + * where the grouping is performed. That is, it is the range + * of the resulting expansion. + * "domain_universe" is the universe of "domain". + * "group" is the set of group elements, i.e., the domain + * of the resulting expansion. + * "group_universe" is the universe of "group". + * + * "sched" is the schedule for the group elements, in pratice + * an identity mapping on "group_universe". + * "dim" is the dimension of "sched". + */ +struct isl_schedule_group_data { + int finished; + + isl_union_map *expansion; + isl_union_pw_multi_aff *contraction; + + isl_union_set *domain; + isl_union_set *domain_universe; + isl_union_set *group; + isl_union_set *group_universe; + + int dim; + isl_multi_aff *sched; +}; + +/* Is domain covered by data->domain within data->domain_universe? + */ +static int locally_covered_by_domain(__isl_keep isl_union_set *domain, + struct isl_schedule_group_data *data) +{ + int is_subset; + isl_union_set *test; + + test = isl_union_set_copy(domain); + test = isl_union_set_intersect(test, + isl_union_set_copy(data->domain_universe)); + is_subset = isl_union_set_is_subset(test, data->domain); + isl_union_set_free(test); + + return is_subset; +} + +/* Update the band tree root "tree" to refer to the group instances + * in data->group rather than the original domain elements in data->domain. + * "pos" is the position in the original schedule tree where the modified + * "tree" will be attached. + * + * Add the part of the identity schedule on the group instances data->sched + * that corresponds to this band node to the band schedule. + * If the domain elements that reach the node and that are part + * of data->domain_universe are all elements of data->domain (and therefore + * replaced by the group instances) then this data->domain_universe + * is removed from the domain of the band schedule. + */ +static __isl_give isl_schedule_tree *group_band( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos, + struct isl_schedule_group_data *data) +{ + isl_union_set *domain; + isl_multi_aff *ma; + isl_multi_union_pw_aff *mupa, *partial; + int is_covered; + int depth, n, has_id; + + domain = isl_schedule_node_get_domain(pos); + is_covered = locally_covered_by_domain(domain, data); + if (is_covered >= 0 && is_covered) { + domain = isl_union_set_universe(domain); + domain = isl_union_set_subtract(domain, + isl_union_set_copy(data->domain_universe)); + tree = isl_schedule_tree_band_intersect_domain(tree, domain); + } else + isl_union_set_free(domain); + if (is_covered < 0) + return isl_schedule_tree_free(tree); + depth = isl_schedule_node_get_schedule_depth(pos); + n = isl_schedule_tree_band_n_member(tree); + ma = isl_multi_aff_copy(data->sched); + ma = isl_multi_aff_drop_dims(ma, isl_dim_out, 0, depth); + ma = isl_multi_aff_drop_dims(ma, isl_dim_out, n, data->dim - depth - n); + mupa = isl_multi_union_pw_aff_from_multi_aff(ma); + partial = isl_schedule_tree_band_get_partial_schedule(tree); + has_id = isl_multi_union_pw_aff_has_tuple_id(partial, isl_dim_set); + if (has_id < 0) { + partial = isl_multi_union_pw_aff_free(partial); + } else if (has_id) { + isl_id *id; + id = isl_multi_union_pw_aff_get_tuple_id(partial, isl_dim_set); + mupa = isl_multi_union_pw_aff_set_tuple_id(mupa, + isl_dim_set, id); + } + partial = isl_multi_union_pw_aff_union_add(partial, mupa); + tree = isl_schedule_tree_band_set_partial_schedule(tree, partial); + + return tree; +} + +/* Drop the parameters in "uset" that are not also in "space". + * "n" is the number of parameters in "space". + */ +static __isl_give isl_union_set *union_set_drop_extra_params( + __isl_take isl_union_set *uset, __isl_keep isl_space *space, int n) +{ + int n2; + + uset = isl_union_set_align_params(uset, isl_space_copy(space)); + n2 = isl_union_set_dim(uset, isl_dim_param); + uset = isl_union_set_project_out(uset, isl_dim_param, n, n2 - n); + + return uset; +} + +/* Update the context tree root "tree" to refer to the group instances + * in data->group rather than the original domain elements in data->domain. + * "pos" is the position in the original schedule tree where the modified + * "tree" will be attached. + * + * We do not actually need to update "tree" since a context node only + * refers to the schedule space. However, we may need to update "data" + * to not refer to any parameters introduced by the context node. + */ +static __isl_give isl_schedule_tree *group_context( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos, + struct isl_schedule_group_data *data) +{ + isl_space *space; + isl_union_set *domain; + int n1, n2; + int involves; + + if (isl_schedule_node_get_tree_depth(pos) == 1) + return tree; + + domain = isl_schedule_node_get_universe_domain(pos); + space = isl_union_set_get_space(domain); + isl_union_set_free(domain); + + n1 = isl_space_dim(space, isl_dim_param); + data->expansion = isl_union_map_align_params(data->expansion, space); + n2 = isl_union_map_dim(data->expansion, isl_dim_param); + + if (!data->expansion) + return isl_schedule_tree_free(tree); + if (n1 == n2) + return tree; + + involves = isl_union_map_involves_dims(data->expansion, + isl_dim_param, n1, n2 - n1); + if (involves < 0) + return isl_schedule_tree_free(tree); + if (involves) + isl_die(isl_schedule_node_get_ctx(pos), isl_error_invalid, + "grouping cannot only refer to global parameters", + return isl_schedule_tree_free(tree)); + + data->expansion = isl_union_map_project_out(data->expansion, + isl_dim_param, n1, n2 - n1); + space = isl_union_map_get_space(data->expansion); + + data->contraction = isl_union_pw_multi_aff_align_params( + data->contraction, isl_space_copy(space)); + n2 = isl_union_pw_multi_aff_dim(data->contraction, isl_dim_param); + data->contraction = isl_union_pw_multi_aff_drop_dims(data->contraction, + isl_dim_param, n1, n2 - n1); + + data->domain = union_set_drop_extra_params(data->domain, space, n1); + data->domain_universe = + union_set_drop_extra_params(data->domain_universe, space, n1); + data->group = union_set_drop_extra_params(data->group, space, n1); + data->group_universe = + union_set_drop_extra_params(data->group_universe, space, n1); + + data->sched = isl_multi_aff_align_params(data->sched, + isl_space_copy(space)); + n2 = isl_multi_aff_dim(data->sched, isl_dim_param); + data->sched = isl_multi_aff_drop_dims(data->sched, + isl_dim_param, n1, n2 - n1); + + isl_space_free(space); + + return tree; +} + +/* Update the domain tree root "tree" to refer to the group instances + * in data->group rather than the original domain elements in data->domain. + * "pos" is the position in the original schedule tree where the modified + * "tree" will be attached. + * + * We first double-check that all grouped domain elements are actually + * part of the root domain and then replace those elements by the group + * instances. + */ +static __isl_give isl_schedule_tree *group_domain( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos, + struct isl_schedule_group_data *data) +{ + isl_union_set *domain; + int is_subset; + + domain = isl_schedule_tree_domain_get_domain(tree); + is_subset = isl_union_set_is_subset(data->domain, domain); + isl_union_set_free(domain); + if (is_subset < 0) + return isl_schedule_tree_free(tree); + if (!is_subset) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "grouped domain should be part of outer domain", + return isl_schedule_tree_free(tree)); + domain = isl_schedule_tree_domain_get_domain(tree); + domain = isl_union_set_subtract(domain, + isl_union_set_copy(data->domain)); + domain = isl_union_set_union(domain, isl_union_set_copy(data->group)); + tree = isl_schedule_tree_domain_set_domain(tree, domain); + + return tree; +} + +/* Update the expansion tree root "tree" to refer to the group instances + * in data->group rather than the original domain elements in data->domain. + * "pos" is the position in the original schedule tree where the modified + * "tree" will be attached. + * + * Let G_1 -> D_1 be the expansion of "tree" and G_2 -> D_2 the newly + * introduced expansion in a descendant of "tree". + * We first double-check that D_2 is a subset of D_1. + * Then we remove D_2 from the range of G_1 -> D_1 and add the mapping + * G_1 -> D_1 . D_2 -> G_2. + * Simmilarly, we restrict the domain of the contraction to the universe + * of the range of the updated expansion and add G_2 -> D_2 . D_1 -> G_1, + * attempting to remove the domain constraints of this additional part. + */ +static __isl_give isl_schedule_tree *group_expansion( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos, + struct isl_schedule_group_data *data) +{ + isl_union_set *domain; + isl_union_map *expansion, *umap; + isl_union_pw_multi_aff *contraction, *upma; + int is_subset; + + expansion = isl_schedule_tree_expansion_get_expansion(tree); + domain = isl_union_map_range(expansion); + is_subset = isl_union_set_is_subset(data->domain, domain); + isl_union_set_free(domain); + if (is_subset < 0) + return isl_schedule_tree_free(tree); + if (!is_subset) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "grouped domain should be part " + "of outer expansion domain", + return isl_schedule_tree_free(tree)); + expansion = isl_schedule_tree_expansion_get_expansion(tree); + umap = isl_union_map_from_union_pw_multi_aff( + isl_union_pw_multi_aff_copy(data->contraction)); + umap = isl_union_map_apply_range(expansion, umap); + expansion = isl_schedule_tree_expansion_get_expansion(tree); + expansion = isl_union_map_subtract_range(expansion, + isl_union_set_copy(data->domain)); + expansion = isl_union_map_union(expansion, umap); + umap = isl_union_map_universe(isl_union_map_copy(expansion)); + domain = isl_union_map_range(umap); + contraction = isl_schedule_tree_expansion_get_contraction(tree); + umap = isl_union_map_from_union_pw_multi_aff(contraction); + umap = isl_union_map_apply_range(isl_union_map_copy(data->expansion), + umap); + upma = isl_union_pw_multi_aff_from_union_map(umap); + contraction = isl_schedule_tree_expansion_get_contraction(tree); + contraction = isl_union_pw_multi_aff_intersect_domain(contraction, + domain); + domain = isl_union_pw_multi_aff_domain( + isl_union_pw_multi_aff_copy(upma)); + upma = isl_union_pw_multi_aff_gist(upma, domain); + contraction = isl_union_pw_multi_aff_union_add(contraction, upma); + tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree, + contraction, expansion); + + return tree; +} + +/* Update the tree root "tree" to refer to the group instances + * in data->group rather than the original domain elements in data->domain. + * "pos" is the position in the original schedule tree where the modified + * "tree" will be attached. + * + * If we have come across a domain or expansion node before (data->finished + * is set), then we no longer need perform any modifications. + * + * If "tree" is a filter, then we add data->group_universe to the filter. + * We also remove data->domain_universe from the filter if all the domain + * elements in this universe that reach the filter node are part of + * the elements that are being grouped by data->expansion. + * If "tree" is a band, domain or expansion, then it is handled + * in a separate function. + */ +static __isl_give isl_schedule_tree *group_ancestor( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos, + void *user) +{ + struct isl_schedule_group_data *data = user; + isl_union_set *domain; + int is_covered; + + if (!tree || !pos) + return isl_schedule_tree_free(tree); + + if (data->finished) + return tree; + + switch (isl_schedule_tree_get_type(tree)) { + case isl_schedule_node_error: + return isl_schedule_tree_free(tree); + case isl_schedule_node_extension: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported, + "grouping not allowed in extended tree", + return isl_schedule_tree_free(tree)); + case isl_schedule_node_band: + tree = group_band(tree, pos, data); + break; + case isl_schedule_node_context: + tree = group_context(tree, pos, data); + break; + case isl_schedule_node_domain: + tree = group_domain(tree, pos, data); + data->finished = 1; + break; + case isl_schedule_node_filter: + domain = isl_schedule_node_get_domain(pos); + is_covered = locally_covered_by_domain(domain, data); + isl_union_set_free(domain); + if (is_covered < 0) + return isl_schedule_tree_free(tree); + domain = isl_schedule_tree_filter_get_filter(tree); + if (is_covered) + domain = isl_union_set_subtract(domain, + isl_union_set_copy(data->domain_universe)); + domain = isl_union_set_union(domain, + isl_union_set_copy(data->group_universe)); + tree = isl_schedule_tree_filter_set_filter(tree, domain); + break; + case isl_schedule_node_expansion: + tree = group_expansion(tree, pos, data); + data->finished = 1; + break; + case isl_schedule_node_leaf: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + + return tree; +} + +/* Group the domain elements that reach "node" into instances + * of a single statement with identifier "group_id". + * In particular, group the domain elements according to their + * prefix schedule. + * + * That is, introduce an expansion node with as contraction + * the prefix schedule (with the target space replaced by "group_id") + * and as expansion the inverse of this contraction (with its range + * intersected with the domain elements that reach "node"). + * The outer nodes are then modified to refer to the group instances + * instead of the original domain elements. + * + * No instance of "group_id" is allowed to reach "node" prior + * to the grouping. + * No ancestor of "node" is allowed to be an extension node. + * + * Return a pointer to original node in tree, i.e., the child + * of the newly introduced expansion node. + */ +__isl_give isl_schedule_node *isl_schedule_node_group( + __isl_take isl_schedule_node *node, __isl_take isl_id *group_id) +{ + struct isl_schedule_group_data data = { 0 }; + isl_space *space; + isl_union_set *domain; + isl_union_pw_multi_aff *contraction; + isl_union_map *expansion; + int disjoint; + + if (!node || !group_id) + goto error; + if (check_insert(node) < 0) + goto error; + + domain = isl_schedule_node_get_domain(node); + data.domain = isl_union_set_copy(domain); + data.domain_universe = isl_union_set_copy(domain); + data.domain_universe = isl_union_set_universe(data.domain_universe); + + data.dim = isl_schedule_node_get_schedule_depth(node); + if (data.dim == 0) { + isl_ctx *ctx; + isl_set *set; + isl_union_set *group; + isl_union_map *univ; + + ctx = isl_schedule_node_get_ctx(node); + space = isl_space_set_alloc(ctx, 0, 0); + space = isl_space_set_tuple_id(space, isl_dim_set, group_id); + set = isl_set_universe(isl_space_copy(space)); + group = isl_union_set_from_set(set); + expansion = isl_union_map_from_domain_and_range(domain, group); + univ = isl_union_map_universe(isl_union_map_copy(expansion)); + contraction = isl_union_pw_multi_aff_from_union_map(univ); + expansion = isl_union_map_reverse(expansion); + } else { + isl_multi_union_pw_aff *prefix; + isl_union_set *univ; + + prefix = + isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node); + prefix = isl_multi_union_pw_aff_set_tuple_id(prefix, + isl_dim_set, group_id); + space = isl_multi_union_pw_aff_get_space(prefix); + contraction = isl_union_pw_multi_aff_from_multi_union_pw_aff( + prefix); + univ = isl_union_set_universe(isl_union_set_copy(domain)); + contraction = + isl_union_pw_multi_aff_intersect_domain(contraction, univ); + expansion = isl_union_map_from_union_pw_multi_aff( + isl_union_pw_multi_aff_copy(contraction)); + expansion = isl_union_map_reverse(expansion); + expansion = isl_union_map_intersect_range(expansion, domain); + } + space = isl_space_map_from_set(space); + data.sched = isl_multi_aff_identity(space); + data.group = isl_union_map_domain(isl_union_map_copy(expansion)); + data.group = isl_union_set_coalesce(data.group); + data.group_universe = isl_union_set_copy(data.group); + data.group_universe = isl_union_set_universe(data.group_universe); + data.expansion = isl_union_map_copy(expansion); + data.contraction = isl_union_pw_multi_aff_copy(contraction); + node = isl_schedule_node_insert_expansion(node, contraction, expansion); + + disjoint = isl_union_set_is_disjoint(data.domain_universe, + data.group_universe); + + node = update_ancestors(node, &group_ancestor, &data); + + isl_union_set_free(data.domain); + isl_union_set_free(data.domain_universe); + isl_union_set_free(data.group); + isl_union_set_free(data.group_universe); + isl_multi_aff_free(data.sched); + isl_union_map_free(data.expansion); + isl_union_pw_multi_aff_free(data.contraction); + + node = isl_schedule_node_child(node, 0); + + if (!node || disjoint < 0) + return isl_schedule_node_free(node); + if (!disjoint) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "group instances already reach node", + isl_schedule_node_free(node)); + + return node; +error: + isl_schedule_node_free(node); + isl_id_free(group_id); + return NULL; +} + +/* Compute the gist of the given band node with respect to "context". + */ +__isl_give isl_schedule_node *isl_schedule_node_band_gist( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *context) +{ + isl_schedule_tree *tree; + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_band_gist(tree, context); + return isl_schedule_node_graft_tree(node, tree); +} + +/* Internal data structure for isl_schedule_node_gist. + * "n_expansion" is the number of outer expansion nodes + * with respect to the current position + * "filters" contains an element for each outer filter, expansion or + * extension node with respect to the current position, each representing + * the intersection of the previous element and the filter on the filter node + * or the expansion/extension of the previous element. + * The first element in the original context passed to isl_schedule_node_gist. + */ +struct isl_node_gist_data { + int n_expansion; + isl_union_set_list *filters; +}; + +/* Enter the expansion node "node" during a isl_schedule_node_gist traversal. + * + * In particular, add an extra element to data->filters containing + * the expansion of the previous element and replace the expansion + * and contraction on "node" by the gist with respect to these filters. + * Also keep track of the fact that we have entered another expansion. + */ +static __isl_give isl_schedule_node *gist_enter_expansion( + __isl_take isl_schedule_node *node, struct isl_node_gist_data *data) +{ + int n; + isl_union_set *inner; + isl_union_map *expansion; + isl_union_pw_multi_aff *contraction; + + data->n_expansion++; + + n = isl_union_set_list_n_union_set(data->filters); + inner = isl_union_set_list_get_union_set(data->filters, n - 1); + expansion = isl_schedule_node_expansion_get_expansion(node); + inner = isl_union_set_apply(inner, expansion); + + contraction = isl_schedule_node_expansion_get_contraction(node); + contraction = isl_union_pw_multi_aff_gist(contraction, + isl_union_set_copy(inner)); + + data->filters = isl_union_set_list_add(data->filters, inner); + + inner = isl_union_set_list_get_union_set(data->filters, n - 1); + expansion = isl_schedule_node_expansion_get_expansion(node); + expansion = isl_union_map_gist_domain(expansion, inner); + node = isl_schedule_node_expansion_set_contraction_and_expansion(node, + contraction, expansion); + + return node; +} + +/* Leave the expansion node "node" during a isl_schedule_node_gist traversal. + * + * In particular, remove the element in data->filters that was added by + * gist_enter_expansion and decrement the number of outer expansions. + * + * The expansion has already been simplified in gist_enter_expansion. + * If this simplification results in an identity expansion, then + * it is removed here. + */ +static __isl_give isl_schedule_node *gist_leave_expansion( + __isl_take isl_schedule_node *node, struct isl_node_gist_data *data) +{ + int n; + isl_bool identity; + isl_union_map *expansion; + + expansion = isl_schedule_node_expansion_get_expansion(node); + identity = isl_union_map_is_identity(expansion); + isl_union_map_free(expansion); + + if (identity < 0) + node = isl_schedule_node_free(node); + else if (identity) + node = isl_schedule_node_delete(node); + + n = isl_union_set_list_n_union_set(data->filters); + data->filters = isl_union_set_list_drop(data->filters, n - 1, 1); + + data->n_expansion--; + + return node; +} + +/* Enter the extension node "node" during a isl_schedule_node_gist traversal. + * + * In particular, add an extra element to data->filters containing + * the union of the previous element with the additional domain elements + * introduced by the extension. + */ +static __isl_give isl_schedule_node *gist_enter_extension( + __isl_take isl_schedule_node *node, struct isl_node_gist_data *data) +{ + int n; + isl_union_set *inner, *extra; + isl_union_map *extension; + + n = isl_union_set_list_n_union_set(data->filters); + inner = isl_union_set_list_get_union_set(data->filters, n - 1); + extension = isl_schedule_node_extension_get_extension(node); + extra = isl_union_map_range(extension); + inner = isl_union_set_union(inner, extra); + + data->filters = isl_union_set_list_add(data->filters, inner); + + return node; +} + +/* Can we finish gisting at this node? + * That is, is the filter on the current filter node a subset of + * the original context passed to isl_schedule_node_gist? + * If we have gone through any expansions, then we cannot perform + * this test since the current domain elements are incomparable + * to the domain elements in the original context. + */ +static int gist_done(__isl_keep isl_schedule_node *node, + struct isl_node_gist_data *data) +{ + isl_union_set *filter, *outer; + int subset; + + if (data->n_expansion != 0) + return 0; + + filter = isl_schedule_node_filter_get_filter(node); + outer = isl_union_set_list_get_union_set(data->filters, 0); + subset = isl_union_set_is_subset(filter, outer); + isl_union_set_free(outer); + isl_union_set_free(filter); + + return subset; +} + +/* Callback for "traverse" to enter a node and to move + * to the deepest initial subtree that should be traversed + * by isl_schedule_node_gist. + * + * The "filters" list is extended by one element each time + * we come across a filter node by the result of intersecting + * the last element in the list with the filter on the filter node. + * + * If the filter on the current filter node is a subset of + * the original context passed to isl_schedule_node_gist, + * then there is no need to go into its subtree since it cannot + * be further simplified by the context. The "filters" list is + * still extended for consistency, but the actual value of the + * added element is immaterial since it will not be used. + * + * Otherwise, the filter on the current filter node is replaced by + * the gist of the original filter with respect to the intersection + * of the original context with the intermediate filters. + * + * If the new element in the "filters" list is empty, then no elements + * can reach the descendants of the current filter node. The subtree + * underneath the filter node is therefore removed. + * + * Each expansion node we come across is handled by + * gist_enter_expansion. + * + * Each extension node we come across is handled by + * gist_enter_extension. + */ +static __isl_give isl_schedule_node *gist_enter( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_node_gist_data *data = user; + + do { + isl_union_set *filter, *inner; + int done, empty; + int n; + + switch (isl_schedule_node_get_type(node)) { + case isl_schedule_node_error: + return isl_schedule_node_free(node); + case isl_schedule_node_expansion: + node = gist_enter_expansion(node, data); + continue; + case isl_schedule_node_extension: + node = gist_enter_extension(node, data); + continue; + case isl_schedule_node_band: + case isl_schedule_node_context: + case isl_schedule_node_domain: + case isl_schedule_node_guard: + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + continue; + case isl_schedule_node_filter: + break; + } + done = gist_done(node, data); + filter = isl_schedule_node_filter_get_filter(node); + if (done < 0 || done) { + data->filters = isl_union_set_list_add(data->filters, + filter); + if (done < 0) + return isl_schedule_node_free(node); + return node; + } + n = isl_union_set_list_n_union_set(data->filters); + inner = isl_union_set_list_get_union_set(data->filters, n - 1); + filter = isl_union_set_gist(filter, isl_union_set_copy(inner)); + node = isl_schedule_node_filter_set_filter(node, + isl_union_set_copy(filter)); + filter = isl_union_set_intersect(filter, inner); + empty = isl_union_set_is_empty(filter); + data->filters = isl_union_set_list_add(data->filters, filter); + if (empty < 0) + return isl_schedule_node_free(node); + if (!empty) + continue; + node = isl_schedule_node_child(node, 0); + node = isl_schedule_node_cut(node); + node = isl_schedule_node_parent(node); + return node; + } while (isl_schedule_node_has_children(node) && + (node = isl_schedule_node_first_child(node)) != NULL); + + return node; +} + +/* Callback for "traverse" to leave a node for isl_schedule_node_gist. + * + * In particular, if the current node is a filter node, then we remove + * the element on the "filters" list that was added when we entered + * the node. There is no need to compute any gist here, since we + * already did that when we entered the node. + * + * Expansion nodes are handled by gist_leave_expansion. + * + * If the current node is an extension, then remove the element + * in data->filters that was added by gist_enter_extension. + * + * If the current node is a band node, then we compute the gist of + * the band node with respect to the intersection of the original context + * and the intermediate filters. + * + * If the current node is a sequence or set node, then some of + * the filter children may have become empty and so they are removed. + * If only one child is left, then the set or sequence node along with + * the single remaining child filter is removed. The filter can be + * removed because the filters on a sequence or set node are supposed + * to partition the incoming domain instances. + * In principle, it should then be impossible for there to be zero + * remaining children, but should this happen, we replace the entire + * subtree with an empty filter. + */ +static __isl_give isl_schedule_node *gist_leave( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_node_gist_data *data = user; + isl_schedule_tree *tree; + int i, n; + isl_union_set *filter; + + switch (isl_schedule_node_get_type(node)) { + case isl_schedule_node_error: + return isl_schedule_node_free(node); + case isl_schedule_node_expansion: + node = gist_leave_expansion(node, data); + break; + case isl_schedule_node_extension: + case isl_schedule_node_filter: + n = isl_union_set_list_n_union_set(data->filters); + data->filters = isl_union_set_list_drop(data->filters, + n - 1, 1); + break; + case isl_schedule_node_band: + n = isl_union_set_list_n_union_set(data->filters); + filter = isl_union_set_list_get_union_set(data->filters, n - 1); + node = isl_schedule_node_band_gist(node, filter); + break; + case isl_schedule_node_set: + case isl_schedule_node_sequence: + tree = isl_schedule_node_get_tree(node); + n = isl_schedule_tree_n_children(tree); + for (i = n - 1; i >= 0; --i) { + isl_schedule_tree *child; + isl_union_set *filter; + int empty; + + child = isl_schedule_tree_get_child(tree, i); + filter = isl_schedule_tree_filter_get_filter(child); + empty = isl_union_set_is_empty(filter); + isl_union_set_free(filter); + isl_schedule_tree_free(child); + if (empty < 0) + tree = isl_schedule_tree_free(tree); + else if (empty) + tree = isl_schedule_tree_drop_child(tree, i); + } + n = isl_schedule_tree_n_children(tree); + node = isl_schedule_node_graft_tree(node, tree); + if (n == 1) { + node = isl_schedule_node_delete(node); + node = isl_schedule_node_delete(node); + } else if (n == 0) { + isl_space *space; + + filter = + isl_union_set_list_get_union_set(data->filters, 0); + space = isl_union_set_get_space(filter); + isl_union_set_free(filter); + filter = isl_union_set_empty(space); + node = isl_schedule_node_cut(node); + node = isl_schedule_node_insert_filter(node, filter); + } + break; + case isl_schedule_node_context: + case isl_schedule_node_domain: + case isl_schedule_node_guard: + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + break; + } + + return node; +} + +/* Compute the gist of the subtree at "node" with respect to + * the reaching domain elements in "context". + * In particular, compute the gist of all band and filter nodes + * in the subtree with respect to "context". Children of set or sequence + * nodes that end up with an empty filter are removed completely. + * + * We keep track of the intersection of "context" with all outer filters + * of the current node within the subtree in the final element of "filters". + * Initially, this list contains the single element "context" and it is + * extended or shortened each time we enter or leave a filter node. + */ +__isl_give isl_schedule_node *isl_schedule_node_gist( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *context) +{ + struct isl_node_gist_data data; + + data.n_expansion = 0; + data.filters = isl_union_set_list_from_union_set(context); + node = traverse(node, &gist_enter, &gist_leave, &data); + isl_union_set_list_free(data.filters); + return node; +} + +/* Intersect the domain of domain node "node" with "domain". + * + * If the domain of "node" is already a subset of "domain", + * then nothing needs to be changed. + * + * Otherwise, we replace the domain of the domain node by the intersection + * and simplify the subtree rooted at "node" with respect to this intersection. + */ +__isl_give isl_schedule_node *isl_schedule_node_domain_intersect_domain( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain) +{ + isl_schedule_tree *tree; + isl_union_set *uset; + int is_subset; + + if (!node || !domain) + goto error; + + uset = isl_schedule_tree_domain_get_domain(node->tree); + is_subset = isl_union_set_is_subset(uset, domain); + isl_union_set_free(uset); + if (is_subset < 0) + goto error; + if (is_subset) { + isl_union_set_free(domain); + return node; + } + + tree = isl_schedule_tree_copy(node->tree); + uset = isl_schedule_tree_domain_get_domain(tree); + uset = isl_union_set_intersect(uset, domain); + tree = isl_schedule_tree_domain_set_domain(tree, + isl_union_set_copy(uset)); + node = isl_schedule_node_graft_tree(node, tree); + + node = isl_schedule_node_child(node, 0); + node = isl_schedule_node_gist(node, uset); + node = isl_schedule_node_parent(node); + + return node; +error: + isl_schedule_node_free(node); + isl_union_set_free(domain); + return NULL; +} + +/* Replace the domain of domain node "node" with the gist + * of the original domain with respect to the parameter domain "context". + */ +__isl_give isl_schedule_node *isl_schedule_node_domain_gist_params( + __isl_take isl_schedule_node *node, __isl_take isl_set *context) +{ + isl_union_set *domain; + isl_schedule_tree *tree; + + if (!node || !context) + goto error; + + tree = isl_schedule_tree_copy(node->tree); + domain = isl_schedule_tree_domain_get_domain(node->tree); + domain = isl_union_set_gist_params(domain, context); + tree = isl_schedule_tree_domain_set_domain(tree, domain); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +error: + isl_schedule_node_free(node); + isl_set_free(context); + return NULL; +} + +/* Internal data structure for isl_schedule_node_get_subtree_expansion. + * "expansions" contains a list of accumulated expansions + * for each outer expansion, set or sequence node. The first element + * in the list is an identity mapping on the reaching domain elements. + * "res" collects the results. + */ +struct isl_subtree_expansion_data { + isl_union_map_list *expansions; + isl_union_map *res; +}; + +/* Callback for "traverse" to enter a node and to move + * to the deepest initial subtree that should be traversed + * by isl_schedule_node_get_subtree_expansion. + * + * Whenever we come across an expansion node, the last element + * of data->expansions is combined with the expansion + * on the expansion node. + * + * Whenever we come across a filter node that is the child + * of a set or sequence node, data->expansions is extended + * with a new element that restricts the previous element + * to the elements selected by the filter. + * The previous element can then be reused while backtracking. + */ +static __isl_give isl_schedule_node *subtree_expansion_enter( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_subtree_expansion_data *data = user; + + do { + enum isl_schedule_node_type type; + isl_union_set *filter; + isl_union_map *inner, *expansion; + int n; + + switch (isl_schedule_node_get_type(node)) { + case isl_schedule_node_error: + return isl_schedule_node_free(node); + case isl_schedule_node_filter: + type = isl_schedule_node_get_parent_type(node); + if (type != isl_schedule_node_set && + type != isl_schedule_node_sequence) + break; + filter = isl_schedule_node_filter_get_filter(node); + n = isl_union_map_list_n_union_map(data->expansions); + inner = + isl_union_map_list_get_union_map(data->expansions, + n - 1); + inner = isl_union_map_intersect_range(inner, filter); + data->expansions = + isl_union_map_list_add(data->expansions, inner); + break; + case isl_schedule_node_expansion: + n = isl_union_map_list_n_union_map(data->expansions); + expansion = + isl_schedule_node_expansion_get_expansion(node); + inner = + isl_union_map_list_get_union_map(data->expansions, + n - 1); + inner = isl_union_map_apply_range(inner, expansion); + data->expansions = + isl_union_map_list_set_union_map(data->expansions, + n - 1, inner); + break; + case isl_schedule_node_band: + case isl_schedule_node_context: + case isl_schedule_node_domain: + case isl_schedule_node_extension: + case isl_schedule_node_guard: + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + } while (isl_schedule_node_has_children(node) && + (node = isl_schedule_node_first_child(node)) != NULL); + + return node; +} + +/* Callback for "traverse" to leave a node for + * isl_schedule_node_get_subtree_expansion. + * + * If we come across a filter node that is the child + * of a set or sequence node, then we remove the element + * of data->expansions that was added in subtree_expansion_enter. + * + * If we reach a leaf node, then the accumulated expansion is + * added to data->res. + */ +static __isl_give isl_schedule_node *subtree_expansion_leave( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_subtree_expansion_data *data = user; + int n; + isl_union_map *inner; + enum isl_schedule_node_type type; + + switch (isl_schedule_node_get_type(node)) { + case isl_schedule_node_error: + return isl_schedule_node_free(node); + case isl_schedule_node_filter: + type = isl_schedule_node_get_parent_type(node); + if (type != isl_schedule_node_set && + type != isl_schedule_node_sequence) + break; + n = isl_union_map_list_n_union_map(data->expansions); + data->expansions = isl_union_map_list_drop(data->expansions, + n - 1, 1); + break; + case isl_schedule_node_leaf: + n = isl_union_map_list_n_union_map(data->expansions); + inner = isl_union_map_list_get_union_map(data->expansions, + n - 1); + data->res = isl_union_map_union(data->res, inner); + break; + case isl_schedule_node_band: + case isl_schedule_node_context: + case isl_schedule_node_domain: + case isl_schedule_node_expansion: + case isl_schedule_node_extension: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + + return node; +} + +/* Return a mapping from the domain elements that reach "node" + * to the corresponding domain elements in the leaves of the subtree + * rooted at "node" obtained by composing the intermediate expansions. + * + * We start out with an identity mapping between the domain elements + * that reach "node" and compose it with all the expansions + * on a path from "node" to a leaf while traversing the subtree. + * Within the children of an a sequence or set node, the + * accumulated expansion is restricted to the elements selected + * by the filter child. + */ +__isl_give isl_union_map *isl_schedule_node_get_subtree_expansion( + __isl_keep isl_schedule_node *node) +{ + struct isl_subtree_expansion_data data; + isl_space *space; + isl_union_set *domain; + isl_union_map *expansion; + + if (!node) + return NULL; + + domain = isl_schedule_node_get_universe_domain(node); + space = isl_union_set_get_space(domain); + expansion = isl_union_set_identity(domain); + data.res = isl_union_map_empty(space); + data.expansions = isl_union_map_list_from_union_map(expansion); + + node = isl_schedule_node_copy(node); + node = traverse(node, &subtree_expansion_enter, + &subtree_expansion_leave, &data); + if (!node) + data.res = isl_union_map_free(data.res); + isl_schedule_node_free(node); + + isl_union_map_list_free(data.expansions); + + return data.res; +} + +/* Internal data structure for isl_schedule_node_get_subtree_contraction. + * "contractions" contains a list of accumulated contractions + * for each outer expansion, set or sequence node. The first element + * in the list is an identity mapping on the reaching domain elements. + * "res" collects the results. + */ +struct isl_subtree_contraction_data { + isl_union_pw_multi_aff_list *contractions; + isl_union_pw_multi_aff *res; +}; + +/* Callback for "traverse" to enter a node and to move + * to the deepest initial subtree that should be traversed + * by isl_schedule_node_get_subtree_contraction. + * + * Whenever we come across an expansion node, the last element + * of data->contractions is combined with the contraction + * on the expansion node. + * + * Whenever we come across a filter node that is the child + * of a set or sequence node, data->contractions is extended + * with a new element that restricts the previous element + * to the elements selected by the filter. + * The previous element can then be reused while backtracking. + */ +static __isl_give isl_schedule_node *subtree_contraction_enter( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_subtree_contraction_data *data = user; + + do { + enum isl_schedule_node_type type; + isl_union_set *filter; + isl_union_pw_multi_aff *inner, *contraction; + int n; + + switch (isl_schedule_node_get_type(node)) { + case isl_schedule_node_error: + return isl_schedule_node_free(node); + case isl_schedule_node_filter: + type = isl_schedule_node_get_parent_type(node); + if (type != isl_schedule_node_set && + type != isl_schedule_node_sequence) + break; + filter = isl_schedule_node_filter_get_filter(node); + n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff( + data->contractions); + inner = + isl_union_pw_multi_aff_list_get_union_pw_multi_aff( + data->contractions, n - 1); + inner = isl_union_pw_multi_aff_intersect_domain(inner, + filter); + data->contractions = + isl_union_pw_multi_aff_list_add(data->contractions, + inner); + break; + case isl_schedule_node_expansion: + n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff( + data->contractions); + contraction = + isl_schedule_node_expansion_get_contraction(node); + inner = + isl_union_pw_multi_aff_list_get_union_pw_multi_aff( + data->contractions, n - 1); + inner = + isl_union_pw_multi_aff_pullback_union_pw_multi_aff( + inner, contraction); + data->contractions = + isl_union_pw_multi_aff_list_set_union_pw_multi_aff( + data->contractions, n - 1, inner); + break; + case isl_schedule_node_band: + case isl_schedule_node_context: + case isl_schedule_node_domain: + case isl_schedule_node_extension: + case isl_schedule_node_guard: + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + } while (isl_schedule_node_has_children(node) && + (node = isl_schedule_node_first_child(node)) != NULL); + + return node; +} + +/* Callback for "traverse" to leave a node for + * isl_schedule_node_get_subtree_contraction. + * + * If we come across a filter node that is the child + * of a set or sequence node, then we remove the element + * of data->contractions that was added in subtree_contraction_enter. + * + * If we reach a leaf node, then the accumulated contraction is + * added to data->res. + */ +static __isl_give isl_schedule_node *subtree_contraction_leave( + __isl_take isl_schedule_node *node, void *user) +{ + struct isl_subtree_contraction_data *data = user; + int n; + isl_union_pw_multi_aff *inner; + enum isl_schedule_node_type type; + + switch (isl_schedule_node_get_type(node)) { + case isl_schedule_node_error: + return isl_schedule_node_free(node); + case isl_schedule_node_filter: + type = isl_schedule_node_get_parent_type(node); + if (type != isl_schedule_node_set && + type != isl_schedule_node_sequence) + break; + n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff( + data->contractions); + data->contractions = + isl_union_pw_multi_aff_list_drop(data->contractions, + n - 1, 1); + break; + case isl_schedule_node_leaf: + n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff( + data->contractions); + inner = isl_union_pw_multi_aff_list_get_union_pw_multi_aff( + data->contractions, n - 1); + data->res = isl_union_pw_multi_aff_union_add(data->res, inner); + break; + case isl_schedule_node_band: + case isl_schedule_node_context: + case isl_schedule_node_domain: + case isl_schedule_node_expansion: + case isl_schedule_node_extension: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + + return node; +} + +/* Return a mapping from the domain elements in the leaves of the subtree + * rooted at "node" to the corresponding domain elements that reach "node" + * obtained by composing the intermediate contractions. + * + * We start out with an identity mapping between the domain elements + * that reach "node" and compose it with all the contractions + * on a path from "node" to a leaf while traversing the subtree. + * Within the children of an a sequence or set node, the + * accumulated contraction is restricted to the elements selected + * by the filter child. + */ +__isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction( + __isl_keep isl_schedule_node *node) +{ + struct isl_subtree_contraction_data data; + isl_space *space; + isl_union_set *domain; + isl_union_pw_multi_aff *contraction; + + if (!node) + return NULL; + + domain = isl_schedule_node_get_universe_domain(node); + space = isl_union_set_get_space(domain); + contraction = isl_union_set_identity_union_pw_multi_aff(domain); + data.res = isl_union_pw_multi_aff_empty(space); + data.contractions = + isl_union_pw_multi_aff_list_from_union_pw_multi_aff(contraction); + + node = isl_schedule_node_copy(node); + node = traverse(node, &subtree_contraction_enter, + &subtree_contraction_leave, &data); + if (!node) + data.res = isl_union_pw_multi_aff_free(data.res); + isl_schedule_node_free(node); + + isl_union_pw_multi_aff_list_free(data.contractions); + + return data.res; +} + +/* Do the nearest "n" ancestors of "node" have the types given in "types" + * (starting at the parent of "node")? + */ +static int has_ancestors(__isl_keep isl_schedule_node *node, + int n, enum isl_schedule_node_type *types) +{ + int i, n_ancestor; + + if (!node) + return -1; + + n_ancestor = isl_schedule_tree_list_n_schedule_tree(node->ancestors); + if (n_ancestor < n) + return 0; + + for (i = 0; i < n; ++i) { + isl_schedule_tree *tree; + int correct_type; + + tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, + n_ancestor - 1 - i); + if (!tree) + return -1; + correct_type = isl_schedule_tree_get_type(tree) == types[i]; + isl_schedule_tree_free(tree); + if (!correct_type) + return 0; + } + + return 1; +} + +/* Given a node "node" that appears in an extension (i.e., it is the child + * of a filter in a sequence inside an extension node), are the spaces + * of the extension specified by "extension" disjoint from those + * of both the original extension and the domain elements that reach + * that original extension? + */ +static int is_disjoint_extension(__isl_keep isl_schedule_node *node, + __isl_keep isl_union_map *extension) +{ + isl_union_map *old; + isl_union_set *domain; + int empty; + + node = isl_schedule_node_copy(node); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + old = isl_schedule_node_extension_get_extension(node); + domain = isl_schedule_node_get_universe_domain(node); + isl_schedule_node_free(node); + old = isl_union_map_universe(old); + domain = isl_union_set_union(domain, isl_union_map_range(old)); + extension = isl_union_map_copy(extension); + extension = isl_union_map_intersect_range(extension, domain); + empty = isl_union_map_is_empty(extension); + isl_union_map_free(extension); + + return empty; +} + +/* Given a node "node" that is governed by an extension node, extend + * that extension node with "extension". + * + * In particular, "node" is the child of a filter in a sequence that + * is in turn a child of an extension node. Extend that extension node + * with "extension". + * + * Return a pointer to the parent of the original node (i.e., a filter). + */ +static __isl_give isl_schedule_node *extend_extension( + __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension) +{ + int pos; + int disjoint; + isl_union_map *node_extension; + + node = isl_schedule_node_parent(node); + pos = isl_schedule_node_get_child_position(node); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + node_extension = isl_schedule_node_extension_get_extension(node); + disjoint = isl_union_map_is_disjoint(extension, node_extension); + extension = isl_union_map_union(extension, node_extension); + node = isl_schedule_node_extension_set_extension(node, extension); + node = isl_schedule_node_child(node, 0); + node = isl_schedule_node_child(node, pos); + + if (disjoint < 0) + return isl_schedule_node_free(node); + if (!node) + return NULL; + if (!disjoint) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "extension domain should be disjoint from earlier " + "extensions", return isl_schedule_node_free(node)); + + return node; +} + +/* Return the universe of "uset" if this universe is disjoint from "ref". + * Otherwise, return "uset". + * + * Also check if "uset" itself is disjoint from "ref", reporting + * an error if it is not. + */ +static __isl_give isl_union_set *replace_by_universe_if_disjoint( + __isl_take isl_union_set *uset, __isl_keep isl_union_set *ref) +{ + int disjoint; + isl_union_set *universe; + + disjoint = isl_union_set_is_disjoint(uset, ref); + if (disjoint < 0) + return isl_union_set_free(uset); + if (!disjoint) + isl_die(isl_union_set_get_ctx(uset), isl_error_invalid, + "extension domain should be disjoint from " + "current domain", return isl_union_set_free(uset)); + + universe = isl_union_set_universe(isl_union_set_copy(uset)); + disjoint = isl_union_set_is_disjoint(universe, ref); + if (disjoint >= 0 && disjoint) { + isl_union_set_free(uset); + return universe; + } + isl_union_set_free(universe); + + if (disjoint < 0) + return isl_union_set_free(uset); + return uset; +} + +/* Insert an extension node on top of "node" with extension "extension". + * In addition, insert a filter that separates node from the extension + * between the extension node and "node". + * Return a pointer to the inserted filter node. + * + * If "node" already appears in an extension (i.e., if it is the child + * of a filter in a sequence inside an extension node), then extend that + * extension with "extension" instead. + * In this case, a pointer to the original filter node is returned. + * Note that if some of the elements in the new extension live in the + * same space as those of the original extension or the domain elements + * reaching the original extension, then we insert a new extension anyway. + * Otherwise, we would have to adjust the filters in the sequence child + * of the extension to ensure that the elements in the new extension + * are filtered out. + */ +static __isl_give isl_schedule_node *insert_extension( + __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension) +{ + enum isl_schedule_node_type ancestors[] = + { isl_schedule_node_filter, isl_schedule_node_sequence, + isl_schedule_node_extension }; + isl_union_set *domain; + isl_union_set *filter; + int in_ext; + + in_ext = has_ancestors(node, 3, ancestors); + if (in_ext < 0) + goto error; + if (in_ext) { + int disjoint; + + disjoint = is_disjoint_extension(node, extension); + if (disjoint < 0) + goto error; + if (disjoint) + return extend_extension(node, extension); + } + + filter = isl_schedule_node_get_domain(node); + domain = isl_union_map_range(isl_union_map_copy(extension)); + filter = replace_by_universe_if_disjoint(filter, domain); + isl_union_set_free(domain); + + node = isl_schedule_node_insert_filter(node, filter); + node = isl_schedule_node_insert_extension(node, extension); + node = isl_schedule_node_child(node, 0); + return node; +error: + isl_schedule_node_free(node); + isl_union_map_free(extension); + return NULL; +} + +/* Replace the subtree that "node" points to by "tree" (which has + * a sequence root with two children), except if the parent of "node" + * is a sequence as well, in which case "tree" is spliced at the position + * of "node" in its parent. + * Return a pointer to the child of the "tree_pos" (filter) child of "tree" + * in the updated schedule tree. + */ +static __isl_give isl_schedule_node *graft_or_splice( + __isl_take isl_schedule_node *node, __isl_take isl_schedule_tree *tree, + int tree_pos) +{ + int pos; + + if (isl_schedule_node_get_parent_type(node) == + isl_schedule_node_sequence) { + pos = isl_schedule_node_get_child_position(node); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_sequence_splice(node, pos, tree); + } else { + pos = 0; + node = isl_schedule_node_graft_tree(node, tree); + } + node = isl_schedule_node_child(node, pos + tree_pos); + node = isl_schedule_node_child(node, 0); + + return node; +} + +/* Insert a node "graft" into the schedule tree of "node" such that it + * is executed before (if "before" is set) or after (if "before" is not set) + * the node that "node" points to. + * The root of "graft" is an extension node. + * Return a pointer to the node that "node" pointed to. + * + * We first insert an extension node on top of "node" (or extend + * the extension node if there already is one), with a filter on "node" + * separating it from the extension. + * We then insert a filter in the graft to separate it from the original + * domain elements and combine the original and new tree in a sequence. + * If we have extended an extension node, then the children of this + * sequence are spliced in the sequence of the extended extension + * at the position where "node" appears in the original extension. + * Otherwise, the sequence pair is attached to the new extension node. + */ +static __isl_give isl_schedule_node *graft_extension( + __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft, + int before) +{ + isl_union_map *extension; + isl_union_set *graft_domain; + isl_union_set *node_domain; + isl_schedule_tree *tree, *tree_graft; + + extension = isl_schedule_node_extension_get_extension(graft); + graft_domain = isl_union_map_range(isl_union_map_copy(extension)); + node_domain = isl_schedule_node_get_universe_domain(node); + node = insert_extension(node, extension); + + graft_domain = replace_by_universe_if_disjoint(graft_domain, + node_domain); + isl_union_set_free(node_domain); + + tree = isl_schedule_node_get_tree(node); + if (!isl_schedule_node_has_children(graft)) { + tree_graft = isl_schedule_tree_from_filter(graft_domain); + } else { + graft = isl_schedule_node_child(graft, 0); + tree_graft = isl_schedule_node_get_tree(graft); + tree_graft = isl_schedule_tree_insert_filter(tree_graft, + graft_domain); + } + if (before) + tree = isl_schedule_tree_sequence_pair(tree_graft, tree); + else + tree = isl_schedule_tree_sequence_pair(tree, tree_graft); + node = graft_or_splice(node, tree, before); + + isl_schedule_node_free(graft); + + return node; +} + +/* Replace the root domain node of "node" by an extension node suitable + * for insertion at "pos". + * That is, create an extension node that maps the outer band nodes + * at "pos" to the domain of the root node of "node" and attach + * the child of this root node to the extension node. + */ +static __isl_give isl_schedule_node *extension_from_domain( + __isl_take isl_schedule_node *node, __isl_keep isl_schedule_node *pos) +{ + isl_union_set *universe; + isl_union_set *domain; + isl_union_map *ext; + int depth; + int anchored; + isl_space *space; + isl_schedule_node *res; + isl_schedule_tree *tree; + + anchored = isl_schedule_node_is_subtree_anchored(node); + if (anchored < 0) + return isl_schedule_node_free(node); + if (anchored) + isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported, + "cannot graft anchored tree with domain root", + return isl_schedule_node_free(node)); + + depth = isl_schedule_node_get_schedule_depth(pos); + domain = isl_schedule_node_domain_get_domain(node); + space = isl_union_set_get_space(domain); + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, depth); + universe = isl_union_set_from_set(isl_set_universe(space)); + ext = isl_union_map_from_domain_and_range(universe, domain); + res = isl_schedule_node_from_extension(ext); + node = isl_schedule_node_child(node, 0); + if (!node) + return isl_schedule_node_free(res); + if (!isl_schedule_tree_is_leaf(node->tree)) { + tree = isl_schedule_node_get_tree(node); + res = isl_schedule_node_child(res, 0); + res = isl_schedule_node_graft_tree(res, tree); + res = isl_schedule_node_parent(res); + } + isl_schedule_node_free(node); + + return res; +} + +/* Insert a node "graft" into the schedule tree of "node" such that it + * is executed before (if "before" is set) or after (if "before" is not set) + * the node that "node" points to. + * The root of "graft" may be either a domain or an extension node. + * In the latter case, the domain of the extension needs to correspond + * to the outer band nodes of "node". + * The elements of the domain or the range of the extension may not + * intersect with the domain elements that reach "node". + * The schedule tree of "graft" may not be anchored. + * + * The schedule tree of "node" is modified to include an extension node + * corresponding to the root node of "graft" as a child of the original + * parent of "node". The original node that "node" points to and the + * child of the root node of "graft" are attached to this extension node + * through a sequence, with appropriate filters and with the child + * of "graft" appearing before or after the original "node". + * + * If "node" already appears inside a sequence that is the child of + * an extension node and if the spaces of the new domain elements + * do not overlap with those of the original domain elements, + * then that extension node is extended with the new extension + * rather than introducing a new segment of extension and sequence nodes. + * + * Return a pointer to the same node in the modified tree that + * "node" pointed to in the original tree. + */ +static __isl_give isl_schedule_node *isl_schedule_node_graft_before_or_after( + __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft, + int before) +{ + if (!node || !graft) + goto error; + if (check_insert(node) < 0) + goto error; + + if (isl_schedule_node_get_type(graft) == isl_schedule_node_domain) + graft = extension_from_domain(graft, node); + + if (isl_schedule_node_get_type(graft) != isl_schedule_node_extension) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "expecting domain or extension as root of graft", + goto error); + + return graft_extension(node, graft, before); +error: + isl_schedule_node_free(node); + isl_schedule_node_free(graft); + return NULL; +} + +/* Insert a node "graft" into the schedule tree of "node" such that it + * is executed before the node that "node" points to. + * The root of "graft" may be either a domain or an extension node. + * In the latter case, the domain of the extension needs to correspond + * to the outer band nodes of "node". + * The elements of the domain or the range of the extension may not + * intersect with the domain elements that reach "node". + * The schedule tree of "graft" may not be anchored. + * + * Return a pointer to the same node in the modified tree that + * "node" pointed to in the original tree. + */ +__isl_give isl_schedule_node *isl_schedule_node_graft_before( + __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft) +{ + return isl_schedule_node_graft_before_or_after(node, graft, 1); +} + +/* Insert a node "graft" into the schedule tree of "node" such that it + * is executed after the node that "node" points to. + * The root of "graft" may be either a domain or an extension node. + * In the latter case, the domain of the extension needs to correspond + * to the outer band nodes of "node". + * The elements of the domain or the range of the extension may not + * intersect with the domain elements that reach "node". + * The schedule tree of "graft" may not be anchored. + * + * Return a pointer to the same node in the modified tree that + * "node" pointed to in the original tree. + */ +__isl_give isl_schedule_node *isl_schedule_node_graft_after( + __isl_take isl_schedule_node *node, + __isl_take isl_schedule_node *graft) +{ + return isl_schedule_node_graft_before_or_after(node, graft, 0); +} + +/* Split the domain elements that reach "node" into those that satisfy + * "filter" and those that do not. Arrange for the first subset to be + * executed before or after the second subset, depending on the value + * of "before". + * Return a pointer to the tree corresponding to the second subset, + * except when this subset is empty in which case the original pointer + * is returned. + * If both subsets are non-empty, then a sequence node is introduced + * to impose the order. If the grandparent of the original node was + * itself a sequence, then the original child is replaced by two children + * in this sequence instead. + * The children in the sequence are copies of the original subtree, + * simplified with respect to their filters. + */ +static __isl_give isl_schedule_node *isl_schedule_node_order_before_or_after( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter, + int before) +{ + enum isl_schedule_node_type ancestors[] = + { isl_schedule_node_filter, isl_schedule_node_sequence }; + isl_union_set *node_domain, *node_filter = NULL, *parent_filter; + isl_schedule_node *node2; + isl_schedule_tree *tree1, *tree2; + int empty1, empty2; + int in_seq; + + if (!node || !filter) + goto error; + if (check_insert(node) < 0) + goto error; + + in_seq = has_ancestors(node, 2, ancestors); + if (in_seq < 0) + goto error; + node_domain = isl_schedule_node_get_domain(node); + filter = isl_union_set_gist(filter, isl_union_set_copy(node_domain)); + node_filter = isl_union_set_copy(node_domain); + node_filter = isl_union_set_subtract(node_filter, + isl_union_set_copy(filter)); + node_filter = isl_union_set_gist(node_filter, node_domain); + empty1 = isl_union_set_is_empty(filter); + empty2 = isl_union_set_is_empty(node_filter); + if (empty1 < 0 || empty2 < 0) + goto error; + if (empty1 || empty2) { + isl_union_set_free(filter); + isl_union_set_free(node_filter); + return node; + } + + if (in_seq) { + node = isl_schedule_node_parent(node); + parent_filter = isl_schedule_node_filter_get_filter(node); + node_filter = isl_union_set_intersect(node_filter, + isl_union_set_copy(parent_filter)); + filter = isl_union_set_intersect(filter, parent_filter); + } + + node2 = isl_schedule_node_copy(node); + node = isl_schedule_node_gist(node, isl_union_set_copy(node_filter)); + node2 = isl_schedule_node_gist(node2, isl_union_set_copy(filter)); + tree1 = isl_schedule_node_get_tree(node); + tree2 = isl_schedule_node_get_tree(node2); + tree1 = isl_schedule_tree_insert_filter(tree1, node_filter); + tree2 = isl_schedule_tree_insert_filter(tree2, filter); + isl_schedule_node_free(node2); + + if (before) { + tree1 = isl_schedule_tree_sequence_pair(tree2, tree1); + node = graft_or_splice(node, tree1, 1); + } else { + tree1 = isl_schedule_tree_sequence_pair(tree1, tree2); + node = graft_or_splice(node, tree1, 0); + } + + return node; +error: + isl_schedule_node_free(node); + isl_union_set_free(filter); + isl_union_set_free(node_filter); + return NULL; +} + +/* Split the domain elements that reach "node" into those that satisfy + * "filter" and those that do not. Arrange for the first subset to be + * executed before the second subset. + * Return a pointer to the tree corresponding to the second subset, + * except when this subset is empty in which case the original pointer + * is returned. + */ +__isl_give isl_schedule_node *isl_schedule_node_order_before( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter) +{ + return isl_schedule_node_order_before_or_after(node, filter, 1); +} + +/* Split the domain elements that reach "node" into those that satisfy + * "filter" and those that do not. Arrange for the first subset to be + * executed after the second subset. + * Return a pointer to the tree corresponding to the second subset, + * except when this subset is empty in which case the original pointer + * is returned. + */ +__isl_give isl_schedule_node *isl_schedule_node_order_after( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter) +{ + return isl_schedule_node_order_before_or_after(node, filter, 0); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * in the schedule node "node". + */ +__isl_give isl_schedule_node *isl_schedule_node_reset_user( + __isl_take isl_schedule_node *node) +{ + isl_schedule_tree *tree; + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_reset_user(tree); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Align the parameters of the schedule node "node" to those of "space". + */ +__isl_give isl_schedule_node *isl_schedule_node_align_params( + __isl_take isl_schedule_node *node, __isl_take isl_space *space) +{ + isl_schedule_tree *tree; + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_align_params(tree, space); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Compute the pullback of schedule node "node" + * by the function represented by "upma". + * In other words, plug in "upma" in the iteration domains + * of schedule node "node". + * We currently do not handle expansion nodes. + * + * Note that this is only a helper function for + * isl_schedule_pullback_union_pw_multi_aff. In order to maintain consistency, + * this function should not be called on a single node without also + * calling it on all the other nodes. + */ +__isl_give isl_schedule_node *isl_schedule_node_pullback_union_pw_multi_aff( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *upma) +{ + isl_schedule_tree *tree; + + tree = isl_schedule_node_get_tree(node); + tree = isl_schedule_tree_pullback_union_pw_multi_aff(tree, upma); + node = isl_schedule_node_graft_tree(node, tree); + + return node; +} + +/* Internal data structure for isl_schedule_node_expand. + * "tree" is the tree that needs to be plugged in in all the leaves. + * "domain" is the set of domain elements in the original leaves + * to which the tree applies. + */ +struct isl_schedule_expand_data { + isl_schedule_tree *tree; + isl_union_set *domain; +}; + +/* If "node" is a leaf, then plug in data->tree, simplifying it + * within its new context. + * + * If there are any domain elements at the leaf where the tree + * should not be plugged in (i.e., there are elements not in data->domain) + * then first extend the tree to only apply to the elements in data->domain + * by constructing a set node that selects data->tree for elements + * in data->domain and a leaf for the other elements. + */ +static __isl_give isl_schedule_node *expand(__isl_take isl_schedule_node *node, + void *user) +{ + struct isl_schedule_expand_data *data = user; + isl_schedule_tree *tree, *leaf; + isl_union_set *domain, *left; + isl_bool empty; + + if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf) + return node; + + domain = isl_schedule_node_get_domain(node); + tree = isl_schedule_tree_copy(data->tree); + + left = isl_union_set_copy(domain); + left = isl_union_set_subtract(left, isl_union_set_copy(data->domain)); + empty = isl_union_set_is_empty(left); + if (empty >= 0 && !empty) { + leaf = isl_schedule_node_get_leaf(node); + leaf = isl_schedule_tree_insert_filter(leaf, left); + left = isl_union_set_copy(data->domain); + tree = isl_schedule_tree_insert_filter(tree, left); + tree = isl_schedule_tree_set_pair(tree, leaf); + } else { + if (empty < 0) + node = isl_schedule_node_free(node); + isl_union_set_free(left); + } + + node = isl_schedule_node_graft_tree(node, tree); + node = isl_schedule_node_gist(node, domain); + + return node; +} + +/* Expand the tree rooted at "node" by extending all leaves + * with an expansion node with as child "tree". + * The expansion is determined by "contraction" and "domain". + * That is, the elements of "domain" are contracted according + * to "contraction". The expansion relation is then the inverse + * of "contraction" with its range intersected with "domain". + * + * Insert the appropriate expansion node on top of "tree" and + * then plug in the result in all leaves of "node". + */ +__isl_give isl_schedule_node *isl_schedule_node_expand( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_set *domain, + __isl_take isl_schedule_tree *tree) +{ + struct isl_schedule_expand_data data; + isl_union_map *expansion; + isl_union_pw_multi_aff *copy; + + if (!node || !contraction || !tree) + node = isl_schedule_node_free(node); + + copy = isl_union_pw_multi_aff_copy(contraction); + expansion = isl_union_map_from_union_pw_multi_aff(copy); + expansion = isl_union_map_reverse(expansion); + expansion = isl_union_map_intersect_range(expansion, domain); + data.domain = isl_union_map_domain(isl_union_map_copy(expansion)); + + tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion); + data.tree = tree; + + node = isl_schedule_node_map_descendant_bottom_up(node, &expand, &data); + isl_union_set_free(data.domain); + isl_schedule_tree_free(data.tree); + return node; +} + +/* Return the position of the subtree containing "node" among the children + * of "ancestor". "node" is assumed to be a descendant of "ancestor". + * In particular, both nodes should point to the same schedule tree. + * + * Return -1 on error. + */ +int isl_schedule_node_get_ancestor_child_position( + __isl_keep isl_schedule_node *node, + __isl_keep isl_schedule_node *ancestor) +{ + int n1, n2; + isl_schedule_tree *tree; + + if (!node || !ancestor) + return -1; + + if (node->schedule != ancestor->schedule) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a descendant", return -1); + + n1 = isl_schedule_node_get_tree_depth(ancestor); + n2 = isl_schedule_node_get_tree_depth(node); + + if (n1 >= n2) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a descendant", return -1); + tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, n1); + isl_schedule_tree_free(tree); + if (tree != ancestor->tree) + isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid, + "not a descendant", return -1); + + return node->child_pos[n1]; +} + +/* Given two nodes that point to the same schedule tree, return their + * closest shared ancestor. + * + * Since the two nodes point to the same schedule, they share at least + * one ancestor, the root of the schedule. We move down from the root + * to the first ancestor where the respective children have a different + * child position. This is the requested ancestor. + * If there is no ancestor where the children have a different position, + * then one node is an ancestor of the other and then this node is + * the requested ancestor. + */ +__isl_give isl_schedule_node *isl_schedule_node_get_shared_ancestor( + __isl_keep isl_schedule_node *node1, + __isl_keep isl_schedule_node *node2) +{ + int i, n1, n2; + + if (!node1 || !node2) + return NULL; + if (node1->schedule != node2->schedule) + isl_die(isl_schedule_node_get_ctx(node1), isl_error_invalid, + "not part of same schedule", return NULL); + n1 = isl_schedule_node_get_tree_depth(node1); + n2 = isl_schedule_node_get_tree_depth(node2); + if (n2 < n1) + return isl_schedule_node_get_shared_ancestor(node2, node1); + if (n1 == 0) + return isl_schedule_node_copy(node1); + if (isl_schedule_node_is_equal(node1, node2)) + return isl_schedule_node_copy(node1); + + for (i = 0; i < n1; ++i) + if (node1->child_pos[i] != node2->child_pos[i]) + break; + + node1 = isl_schedule_node_copy(node1); + return isl_schedule_node_ancestor(node1, n1 - i); +} + +/* Print "node" to "p". + */ +__isl_give isl_printer *isl_printer_print_schedule_node( + __isl_take isl_printer *p, __isl_keep isl_schedule_node *node) +{ + if (!node) + return isl_printer_free(p); + return isl_printer_print_schedule_tree_mark(p, node->schedule->root, + isl_schedule_tree_list_n_schedule_tree(node->ancestors), + node->child_pos); +} + +void isl_schedule_node_dump(__isl_keep isl_schedule_node *node) +{ + isl_ctx *ctx; + isl_printer *printer; + + if (!node) + return; + + ctx = isl_schedule_node_get_ctx(node); + printer = isl_printer_to_file(ctx, stderr); + printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK); + printer = isl_printer_print_schedule_node(printer, node); + + isl_printer_free(printer); +} + +/* Return a string representation of "node". + * Print the schedule node in block format as it would otherwise + * look identical to the entire schedule. + */ +__isl_give char *isl_schedule_node_to_str(__isl_keep isl_schedule_node *node) +{ + isl_printer *printer; + char *s; + + if (!node) + return NULL; + + printer = isl_printer_to_str(isl_schedule_node_get_ctx(node)); + printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK); + printer = isl_printer_print_schedule_node(printer, node); + s = isl_printer_get_str(printer); + isl_printer_free(printer); + + return s; +} Index: lib/Analysis/isl/isl_schedule_node_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_node_private.h @@ -0,0 +1,68 @@ +#ifndef ISL_SCHEDLUE_NODE_PRIVATE_H +#define ISL_SCHEDLUE_NODE_PRIVATE_H + +#include +#include +#include + +/* An isl_schedule_node points to a particular location in a schedule tree. + * + * "schedule" is the schedule that the node is pointing to. + * "ancestors" is a list of the n ancestors of the node + * that is being pointed to. + * The first ancestor is the root of "schedule", while the last ancestor + * is the parent of the specified location. + * "child_pos" is an array of child positions of the same length as "ancestors", + * where ancestor i (i > 0) appears in child_pos[i - 1] of ancestor i - 1 and + * "tree" appears in child_pos[n - 1] of ancestor n - 1. + * "tree" is the subtree at the specified location. + * + * Note that the same isl_schedule_tree object may appear several times + * in a schedule tree and therefore does not uniquely identify a position + * in the schedule tree. + */ +struct isl_schedule_node { + int ref; + + isl_schedule *schedule; + isl_schedule_tree_list *ancestors; + int *child_pos; + isl_schedule_tree *tree; +}; + +__isl_give isl_schedule_node *isl_schedule_node_alloc( + __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree, + __isl_take isl_schedule_tree_list *ancestors, int *child_pos); +__isl_give isl_schedule_node *isl_schedule_node_graft_tree( + __isl_take isl_schedule_node *pos, __isl_take isl_schedule_tree *tree); + +__isl_give isl_schedule_tree *isl_schedule_node_get_tree( + __isl_keep isl_schedule_node *node); + +__isl_give isl_schedule_node *isl_schedule_node_pullback_union_pw_multi_aff( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_schedule_node *isl_schedule_node_expand( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_set *domain, + __isl_take isl_schedule_tree *tree); + +__isl_give isl_schedule_node *isl_schedule_node_gist( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *context); + +__isl_give isl_schedule_node *isl_schedule_node_domain_intersect_domain( + __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain); +__isl_give isl_schedule_node *isl_schedule_node_domain_gist_params( + __isl_take isl_schedule_node *node, __isl_take isl_set *context); + +__isl_give isl_schedule_node *isl_schedule_node_insert_expansion( + __isl_take isl_schedule_node *node, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion); +__isl_give isl_schedule_node *isl_schedule_node_insert_extension( + __isl_take isl_schedule_node *node, + __isl_take isl_union_map *extension); + +#endif Index: lib/Analysis/isl/isl_schedule_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_private.h @@ -0,0 +1,41 @@ +#ifndef ISL_SCHEDLUE_PRIVATE_H +#define ISL_SCHEDLUE_PRIVATE_H + +#include +#include +#include + +/* A complete schedule tree. + * + * band_forest points to a band forest representation of the schedule + * and may be NULL if the forest hasn't been created yet. + * + * "root" is the root of the schedule tree and may be NULL if we + * have created a band forest corresponding to the schedule. + * + * "leaf" may be used to represent a leaf of the schedule. + * It should not appear as a child to any other isl_schedule_tree objects, + * but an isl_schedule_node may have "leaf" as its tree if it refers to + * a leaf of this schedule tree. + */ +struct isl_schedule { + int ref; + + isl_band_list *band_forest; + isl_schedule_tree *root; + + struct isl_schedule_tree *leaf; +}; + +__isl_give isl_schedule *isl_schedule_from_schedule_tree(isl_ctx *ctx, + __isl_take isl_schedule_tree *tree); +__isl_give isl_schedule *isl_schedule_set_root( + __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree); +__isl_give isl_space *isl_schedule_get_space( + __isl_keep isl_schedule *schedule); +__isl_give isl_union_set *isl_schedule_get_domain( + __isl_keep isl_schedule *schedule); +__isl_keep isl_schedule_tree *isl_schedule_peek_leaf( + __isl_keep isl_schedule *schedule); + +#endif Index: lib/Analysis/isl/isl_schedule_read.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_read.c @@ -0,0 +1,820 @@ +#include + +#include +#include +#include +#include + +/* An enumeration of the various keys that may appear in a YAML mapping + * of a schedule. + */ +enum isl_schedule_key { + isl_schedule_key_error = -1, + isl_schedule_key_child, + isl_schedule_key_coincident, + isl_schedule_key_context, + isl_schedule_key_contraction, + isl_schedule_key_domain, + isl_schedule_key_expansion, + isl_schedule_key_extension, + isl_schedule_key_filter, + isl_schedule_key_guard, + isl_schedule_key_leaf, + isl_schedule_key_mark, + isl_schedule_key_options, + isl_schedule_key_permutable, + isl_schedule_key_schedule, + isl_schedule_key_sequence, + isl_schedule_key_set +}; + +/* Extract a mapping key from the token "tok". + * Return isl_schedule_key_error on error, i.e., if "tok" does not + * correspond to any known key. + */ +static enum isl_schedule_key extract_key(__isl_keep isl_stream *s, + struct isl_token *tok) +{ + int type; + char *name; + enum isl_schedule_key key; + isl_ctx *ctx; + + ctx = isl_stream_get_ctx(s); + type = isl_token_get_type(tok); + if (type != ISL_TOKEN_IDENT && type != ISL_TOKEN_STRING) { + isl_stream_error(s, tok, "expecting key"); + return isl_schedule_key_error; + } + name = isl_token_get_str(ctx, tok); + if (!strcmp(name, "child")) + key = isl_schedule_key_child; + else if (!strcmp(name, "coincident")) + key = isl_schedule_key_coincident; + else if (!strcmp(name, "context")) + key = isl_schedule_key_context; + else if (!strcmp(name, "contraction")) + key = isl_schedule_key_contraction; + else if (!strcmp(name, "domain")) + key = isl_schedule_key_domain; + else if (!strcmp(name, "expansion")) + key = isl_schedule_key_expansion; + else if (!strcmp(name, "extension")) + key = isl_schedule_key_extension; + else if (!strcmp(name, "filter")) + key = isl_schedule_key_filter; + else if (!strcmp(name, "guard")) + key = isl_schedule_key_guard; + else if (!strcmp(name, "leaf")) + key = isl_schedule_key_leaf; + else if (!strcmp(name, "mark")) + key = isl_schedule_key_mark; + else if (!strcmp(name, "options")) + key = isl_schedule_key_options; + else if (!strcmp(name, "schedule")) + key = isl_schedule_key_schedule; + else if (!strcmp(name, "sequence")) + key = isl_schedule_key_sequence; + else if (!strcmp(name, "set")) + key = isl_schedule_key_set; + else if (!strcmp(name, "permutable")) + key = isl_schedule_key_permutable; + else + isl_die(ctx, isl_error_invalid, "unknown key", + key = isl_schedule_key_error); + free(name); + return key; +} + +/* Read a key from "s" and return the corresponding enum. + * Return isl_schedule_key_error on error, i.e., if the first token + * on the stream does not correspond to any known key. + */ +static enum isl_schedule_key get_key(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + enum isl_schedule_key key; + + tok = isl_stream_next_token(s); + key = extract_key(s, tok); + isl_token_free(tok); + + return key; +} + +static __isl_give isl_schedule_tree *isl_stream_read_schedule_tree( + __isl_keep isl_stream *s); + +/* Read a subtree with context root node from "s". + */ +static __isl_give isl_schedule_tree *read_context(__isl_keep isl_stream *s) +{ + isl_set *context = NULL; + isl_schedule_tree *tree; + isl_ctx *ctx; + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + int more; + + ctx = isl_stream_get_ctx(s); + + key = get_key(s); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + str = isl_token_get_str(ctx, tok); + context = isl_set_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + + more = isl_stream_yaml_next(s); + if (more < 0) + goto error; + if (!more) { + tree = isl_schedule_tree_from_context(context); + } else { + key = get_key(s); + if (key != isl_schedule_key_child) + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + if (isl_stream_yaml_next(s) < 0) + goto error; + tree = isl_stream_read_schedule_tree(s); + tree = isl_schedule_tree_insert_context(tree, context); + } + + return tree; +error: + isl_set_free(context); + return NULL; +} + +/* Read a subtree with domain root node from "s". + */ +static __isl_give isl_schedule_tree *read_domain(__isl_keep isl_stream *s) +{ + isl_union_set *domain = NULL; + isl_schedule_tree *tree; + isl_ctx *ctx; + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + int more; + + ctx = isl_stream_get_ctx(s); + + key = get_key(s); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + str = isl_token_get_str(ctx, tok); + domain = isl_union_set_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + + more = isl_stream_yaml_next(s); + if (more < 0) + goto error; + if (!more) { + tree = isl_schedule_tree_from_domain(domain); + } else { + key = get_key(s); + if (key != isl_schedule_key_child) + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + if (isl_stream_yaml_next(s) < 0) + goto error; + tree = isl_stream_read_schedule_tree(s); + tree = isl_schedule_tree_insert_domain(tree, domain); + } + + return tree; +error: + isl_union_set_free(domain); + return NULL; +} + +/* Read a subtree with expansion root node from "s". + */ +static __isl_give isl_schedule_tree *read_expansion(isl_stream *s) +{ + isl_ctx *ctx; + isl_union_pw_multi_aff *contraction = NULL; + isl_union_map *expansion = NULL; + isl_schedule_tree *tree = NULL; + int more; + + ctx = isl_stream_get_ctx(s); + + do { + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + + key = get_key(s); + if (isl_stream_yaml_next(s) < 0) + goto error; + + switch (key) { + case isl_schedule_key_contraction: + isl_union_pw_multi_aff_free(contraction); + tok = isl_stream_next_token(s); + str = isl_token_get_str(ctx, tok); + contraction = isl_union_pw_multi_aff_read_from_str(ctx, + str); + free(str); + isl_token_free(tok); + if (!contraction) + goto error; + break; + case isl_schedule_key_expansion: + isl_union_map_free(expansion); + tok = isl_stream_next_token(s); + str = isl_token_get_str(ctx, tok); + expansion = isl_union_map_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + if (!expansion) + goto error; + break; + case isl_schedule_key_child: + isl_schedule_tree_free(tree); + tree = isl_stream_read_schedule_tree(s); + if (!tree) + goto error; + break; + default: + isl_die(ctx, isl_error_invalid, "unexpected key", + goto error); + } + } while ((more = isl_stream_yaml_next(s)) > 0); + + if (more < 0) + goto error; + + if (!contraction) + isl_die(ctx, isl_error_invalid, "missing contraction", + goto error); + if (!expansion) + isl_die(ctx, isl_error_invalid, "missing expansion", + goto error); + + if (!tree) + return isl_schedule_tree_from_expansion(contraction, expansion); + return isl_schedule_tree_insert_expansion(tree, contraction, expansion); +error: + isl_schedule_tree_free(tree); + isl_union_pw_multi_aff_free(contraction); + isl_union_map_free(expansion); + return NULL; +} + +/* Read a subtree with extension root node from "s". + */ +static __isl_give isl_schedule_tree *read_extension(isl_stream *s) +{ + isl_union_map *extension = NULL; + isl_schedule_tree *tree; + isl_ctx *ctx; + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + int more; + + ctx = isl_stream_get_ctx(s); + + key = get_key(s); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + str = isl_token_get_str(ctx, tok); + extension = isl_union_map_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + + more = isl_stream_yaml_next(s); + if (more < 0) + goto error; + if (!more) { + tree = isl_schedule_tree_from_extension(extension); + } else { + key = get_key(s); + if (key != isl_schedule_key_child) + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + if (isl_stream_yaml_next(s) < 0) + goto error; + tree = isl_stream_read_schedule_tree(s); + tree = isl_schedule_tree_insert_extension(tree, extension); + } + + return tree; +error: + isl_union_map_free(extension); + return NULL; +} + +/* Read a subtree with filter root node from "s". + */ +static __isl_give isl_schedule_tree *read_filter(__isl_keep isl_stream *s) +{ + isl_union_set *filter = NULL; + isl_schedule_tree *tree; + isl_ctx *ctx; + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + int more; + + ctx = isl_stream_get_ctx(s); + + key = get_key(s); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + str = isl_token_get_str(ctx, tok); + filter = isl_union_set_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + + more = isl_stream_yaml_next(s); + if (more < 0) + goto error; + if (!more) { + tree = isl_schedule_tree_from_filter(filter); + } else { + key = get_key(s); + if (key != isl_schedule_key_child) + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + if (isl_stream_yaml_next(s) < 0) + goto error; + tree = isl_stream_read_schedule_tree(s); + tree = isl_schedule_tree_insert_filter(tree, filter); + } + + return tree; +error: + isl_union_set_free(filter); + return NULL; +} + +/* Read a subtree with guard root node from "s". + */ +static __isl_give isl_schedule_tree *read_guard(isl_stream *s) +{ + isl_set *guard = NULL; + isl_schedule_tree *tree; + isl_ctx *ctx; + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + int more; + + ctx = isl_stream_get_ctx(s); + + key = get_key(s); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + str = isl_token_get_str(ctx, tok); + guard = isl_set_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + + more = isl_stream_yaml_next(s); + if (more < 0) + goto error; + if (!more) { + tree = isl_schedule_tree_from_guard(guard); + } else { + key = get_key(s); + if (key != isl_schedule_key_child) + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + if (isl_stream_yaml_next(s) < 0) + goto error; + tree = isl_stream_read_schedule_tree(s); + tree = isl_schedule_tree_insert_guard(tree, guard); + } + + return tree; +error: + isl_set_free(guard); + return NULL; +} + +/* Read a subtree with mark root node from "s". + */ +static __isl_give isl_schedule_tree *read_mark(isl_stream *s) +{ + isl_id *mark; + isl_schedule_tree *tree; + isl_ctx *ctx; + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + int more; + + ctx = isl_stream_get_ctx(s); + + key = get_key(s); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + return NULL; + } + str = isl_token_get_str(ctx, tok); + mark = isl_id_alloc(ctx, str, NULL); + free(str); + isl_token_free(tok); + + more = isl_stream_yaml_next(s); + if (more < 0) + goto error; + if (!more) { + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + } else { + key = get_key(s); + if (key != isl_schedule_key_child) + isl_die(ctx, isl_error_invalid, "expecting child", + goto error); + if (isl_stream_yaml_next(s) < 0) + goto error; + tree = isl_stream_read_schedule_tree(s); + tree = isl_schedule_tree_insert_mark(tree, mark); + } + + return tree; +error: + isl_id_free(mark); + return NULL; +} + +/* Read a sequence of integers from "s" (representing the coincident + * property of a band node). + */ +static __isl_give isl_val_list *read_coincident(__isl_keep isl_stream *s) +{ + isl_ctx *ctx; + isl_val_list *list; + int more; + + ctx = isl_stream_get_ctx(s); + + if (isl_stream_yaml_read_start_sequence(s) < 0) + return NULL; + + list = isl_val_list_alloc(ctx, 0); + while ((more = isl_stream_yaml_next(s)) > 0) { + isl_val *val; + + val = isl_stream_read_val(s); + list = isl_val_list_add(list, val); + } + + if (more < 0 || isl_stream_yaml_read_end_sequence(s)) + list = isl_val_list_free(list); + + return list; +} + +/* Set the (initial) coincident properties of "band" according to + * the (initial) elements of "coincident". + */ +static __isl_give isl_schedule_band *set_coincident( + __isl_take isl_schedule_band *band, __isl_take isl_val_list *coincident) +{ + int i; + int n, m; + + n = isl_schedule_band_n_member(band); + m = isl_val_list_n_val(coincident); + + for (i = 0; i < n && i < m; ++i) { + isl_val *v; + + v = isl_val_list_get_val(coincident, i); + if (!v) + band = isl_schedule_band_free(band); + band = isl_schedule_band_member_set_coincident(band, i, + !isl_val_is_zero(v)); + isl_val_free(v); + } + isl_val_list_free(coincident); + return band; +} + +/* Read a subtree with band root node from "s". + */ +static __isl_give isl_schedule_tree *read_band(isl_stream *s) +{ + isl_multi_union_pw_aff *schedule = NULL; + isl_schedule_tree *tree = NULL; + isl_val_list *coincident = NULL; + isl_union_set *options = NULL; + isl_ctx *ctx; + isl_schedule_band *band; + int permutable = 0; + int more; + + ctx = isl_stream_get_ctx(s); + + do { + struct isl_token *tok; + enum isl_schedule_key key; + char *str; + isl_val *v; + + key = get_key(s); + if (isl_stream_yaml_next(s) < 0) + goto error; + + switch (key) { + case isl_schedule_key_schedule: + isl_multi_union_pw_aff_free(schedule); + tok = isl_stream_next_token(s); + if (!tok) { + isl_stream_error(s, NULL, "unexpected EOF"); + goto error; + } + str = isl_token_get_str(ctx, tok); + schedule = isl_multi_union_pw_aff_read_from_str(ctx, + str); + free(str); + isl_token_free(tok); + if (!schedule) + goto error; + break; + case isl_schedule_key_coincident: + coincident = read_coincident(s); + if (!coincident) + goto error; + break; + case isl_schedule_key_permutable: + v = isl_stream_read_val(s); + permutable = !isl_val_is_zero(v); + isl_val_free(v); + break; + case isl_schedule_key_options: + isl_union_set_free(options); + tok = isl_stream_next_token(s); + str = isl_token_get_str(ctx, tok); + options = isl_union_set_read_from_str(ctx, str); + free(str); + isl_token_free(tok); + if (!options) + goto error; + break; + case isl_schedule_key_child: + isl_schedule_tree_free(tree); + tree = isl_stream_read_schedule_tree(s); + if (!tree) + goto error; + break; + default: + isl_die(ctx, isl_error_invalid, "unexpected key", + goto error); + } + } while ((more = isl_stream_yaml_next(s)) > 0); + + if (more < 0) + goto error; + + if (!schedule) + isl_die(ctx, isl_error_invalid, "missing schedule", goto error); + + band = isl_schedule_band_from_multi_union_pw_aff(schedule); + band = isl_schedule_band_set_permutable(band, permutable); + if (coincident) + band = set_coincident(band, coincident); + if (options) + band = isl_schedule_band_set_ast_build_options(band, options); + if (tree) + tree = isl_schedule_tree_insert_band(tree, band); + else + tree = isl_schedule_tree_from_band(band); + + return tree; +error: + isl_val_list_free(coincident); + isl_union_set_free(options); + isl_schedule_tree_free(tree); + isl_multi_union_pw_aff_free(schedule); + return NULL; +} + +/* Read a subtree with root node of type "type" from "s". + * The node is represented by a sequence of children. + */ +static __isl_give isl_schedule_tree *read_children(isl_stream *s, + enum isl_schedule_node_type type) +{ + isl_ctx *ctx; + isl_schedule_tree_list *list; + int more; + + ctx = isl_stream_get_ctx(s); + + isl_token_free(isl_stream_next_token(s)); + + if (isl_stream_yaml_next(s) < 0) + return NULL; + + if (isl_stream_yaml_read_start_sequence(s)) + return NULL; + + list = isl_schedule_tree_list_alloc(ctx, 0); + while ((more = isl_stream_yaml_next(s)) > 0) { + isl_schedule_tree *tree; + + tree = isl_stream_read_schedule_tree(s); + list = isl_schedule_tree_list_add(list, tree); + } + + if (more < 0 || isl_stream_yaml_read_end_sequence(s)) + list = isl_schedule_tree_list_free(list); + + return isl_schedule_tree_from_children(type, list); +} + +/* Read a subtree with sequence root node from "s". + */ +static __isl_give isl_schedule_tree *read_sequence(isl_stream *s) +{ + return read_children(s, isl_schedule_node_sequence); +} + +/* Read a subtree with set root node from "s". + */ +static __isl_give isl_schedule_tree *read_set(isl_stream *s) +{ + return read_children(s, isl_schedule_node_set); +} + +/* Read a schedule (sub)tree from "s". + * + * We first determine the type of the root node based on the first + * mapping key and then hand over to a function tailored to reading + * nodes of this type. + */ +static __isl_give isl_schedule_tree *isl_stream_read_schedule_tree( + struct isl_stream *s) +{ + enum isl_schedule_key key; + struct isl_token *tok; + isl_schedule_tree *tree = NULL; + int more; + + if (isl_stream_yaml_read_start_mapping(s)) + return NULL; + more = isl_stream_yaml_next(s); + if (more < 0) + return NULL; + if (!more) { + isl_stream_error(s, NULL, "missing key"); + return NULL; + } + + tok = isl_stream_next_token(s); + key = extract_key(s, tok); + isl_stream_push_token(s, tok); + if (key < 0) + return NULL; + switch (key) { + case isl_schedule_key_context: + tree = read_context(s); + break; + case isl_schedule_key_domain: + tree = read_domain(s); + break; + case isl_schedule_key_contraction: + case isl_schedule_key_expansion: + tree = read_expansion(s); + break; + case isl_schedule_key_extension: + tree = read_extension(s); + break; + case isl_schedule_key_filter: + tree = read_filter(s); + break; + case isl_schedule_key_guard: + tree = read_guard(s); + break; + case isl_schedule_key_leaf: + isl_token_free(isl_stream_next_token(s)); + tree = isl_schedule_tree_leaf(isl_stream_get_ctx(s)); + break; + case isl_schedule_key_mark: + tree = read_mark(s); + break; + case isl_schedule_key_sequence: + tree = read_sequence(s); + break; + case isl_schedule_key_set: + tree = read_set(s); + break; + case isl_schedule_key_schedule: + case isl_schedule_key_coincident: + case isl_schedule_key_options: + case isl_schedule_key_permutable: + tree = read_band(s); + break; + case isl_schedule_key_child: + isl_die(isl_stream_get_ctx(s), isl_error_unsupported, + "cannot identity node type", return NULL); + case isl_schedule_key_error: + return NULL; + } + + if (isl_stream_yaml_read_end_mapping(s) < 0) { + isl_stream_error(s, NULL, "unexpected extra elements"); + return isl_schedule_tree_free(tree); + } + + return tree; +} + +/* Read an isl_schedule from "s". + */ +__isl_give isl_schedule *isl_stream_read_schedule(isl_stream *s) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!s) + return NULL; + + ctx = isl_stream_get_ctx(s); + tree = isl_stream_read_schedule_tree(s); + return isl_schedule_from_schedule_tree(ctx, tree); +} + +/* Read an isl_schedule from "input". + */ +__isl_give isl_schedule *isl_schedule_read_from_file(isl_ctx *ctx, FILE *input) +{ + struct isl_stream *s; + isl_schedule *schedule; + + s = isl_stream_new_file(ctx, input); + if (!s) + return NULL; + schedule = isl_stream_read_schedule(s); + isl_stream_free(s); + + return schedule; +} + +/* Read an isl_schedule from "str". + */ +__isl_give isl_schedule *isl_schedule_read_from_str(isl_ctx *ctx, + const char *str) +{ + struct isl_stream *s; + isl_schedule *schedule; + + s = isl_stream_new_str(ctx, str); + if (!s) + return NULL; + schedule = isl_stream_read_schedule(s); + isl_stream_free(s); + + return schedule; +} Index: lib/Analysis/isl/isl_schedule_tree.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_tree.h @@ -0,0 +1,266 @@ +#ifndef ISL_SCHEDLUE_TREE_H +#define ISL_SCHEDLUE_TREE_H + +#include +#include +#include +#include + +struct isl_schedule_tree; +typedef struct isl_schedule_tree isl_schedule_tree; + +ISL_DECLARE_LIST(schedule_tree) + +/* A schedule (sub)tree. + * + * The leaves of a tree are not explicitly represented inside + * the isl_schedule_tree, except when the tree consists of only a leaf. + * + * The "band" field is valid when type is isl_schedule_node_band. + * The "context" field is valid when type is isl_schedule_node_context + * and represents constraints on the flat product of the outer band nodes, + * possibly introducing additional parameters. + * The "domain" field is valid when type is isl_schedule_node_domain + * and introduces the statement instances scheduled by the tree. + * + * The "contraction" and "expansion" fields are valid when type + * is isl_schedule_node_expansion. + * "expansion" expands the reaching domain elements to one or more + * domain elements for the subtree. + * "contraction" maps these elements back to the corresponding + * reaching domain element. It does not involve any domain constraints. + * + * The "extension" field is valid when the is isl_schedule_node_extension + * maps outer schedule dimenions (the flat product of the outer band nodes) + * to additional iteration domains. + * + * The "filter" field is valid when type is isl_schedule_node_filter + * and represents the statement instances selected by the node. + * + * The "guard" field is valid when type is isl_schedule_node_guard + * and represents constraints on the flat product of the outer band nodes + * that need to be enforced by the outer nodes in the generated AST. + * + * The "mark" field is valid when type is isl_schedule_node_mark and + * identifies the mark. + * + * The "children" field is valid for all types except + * isl_schedule_node_leaf. This field is NULL if there are + * no children (except for the implicit leaves). + * + * anchored is set if the node or any of its descendants depends + * on its position in the schedule tree. + */ +struct isl_schedule_tree { + int ref; + isl_ctx *ctx; + int anchored; + enum isl_schedule_node_type type; + union { + isl_schedule_band *band; + isl_set *context; + isl_union_set *domain; + struct { + isl_union_pw_multi_aff *contraction; + isl_union_map *expansion; + }; + isl_union_map *extension; + isl_union_set *filter; + isl_set *guard; + isl_id *mark; + }; + isl_schedule_tree_list *children; +}; + +isl_ctx *isl_schedule_tree_get_ctx(__isl_keep isl_schedule_tree *tree); +enum isl_schedule_node_type isl_schedule_tree_get_type( + __isl_keep isl_schedule_tree *tree); + +__isl_give isl_schedule_tree *isl_schedule_tree_leaf(isl_ctx *ctx); +int isl_schedule_tree_is_leaf(__isl_keep isl_schedule_tree *tree); + +isl_bool isl_schedule_tree_plain_is_equal(__isl_keep isl_schedule_tree *tree1, + __isl_keep isl_schedule_tree *tree2); + +__isl_give isl_schedule_tree *isl_schedule_tree_copy( + __isl_keep isl_schedule_tree *tree); +__isl_null isl_schedule_tree *isl_schedule_tree_free( + __isl_take isl_schedule_tree *tree); + +__isl_give isl_schedule_tree *isl_schedule_tree_from_band( + __isl_take isl_schedule_band *band); +__isl_give isl_schedule_tree *isl_schedule_tree_from_context( + __isl_take isl_set *context); +__isl_give isl_schedule_tree *isl_schedule_tree_from_domain( + __isl_take isl_union_set *domain); +__isl_give isl_schedule_tree *isl_schedule_tree_from_expansion( + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion); +__isl_give isl_schedule_tree *isl_schedule_tree_from_extension( + __isl_take isl_union_map *extension); +__isl_give isl_schedule_tree *isl_schedule_tree_from_filter( + __isl_take isl_union_set *filter); +__isl_give isl_schedule_tree *isl_schedule_tree_from_guard( + __isl_take isl_set *guard); +__isl_give isl_schedule_tree *isl_schedule_tree_from_children( + enum isl_schedule_node_type type, + __isl_take isl_schedule_tree_list *list); +__isl_give isl_schedule_tree *isl_schedule_tree_from_pair( + enum isl_schedule_node_type type, __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2); +__isl_give isl_schedule_tree *isl_schedule_tree_sequence_pair( + __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2); +__isl_give isl_schedule_tree *isl_schedule_tree_set_pair( + __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2); + +isl_bool isl_schedule_tree_is_subtree_anchored( + __isl_keep isl_schedule_tree *tree); + +__isl_give isl_space *isl_schedule_tree_band_get_space( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_band_intersect_domain( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain); +__isl_give isl_multi_union_pw_aff *isl_schedule_tree_band_get_partial_schedule( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_band_set_partial_schedule( + __isl_take isl_schedule_tree *tree, + __isl_take isl_multi_union_pw_aff *schedule); +enum isl_ast_loop_type isl_schedule_tree_band_member_get_ast_loop_type( + __isl_keep isl_schedule_tree *tree, int pos); +__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_ast_loop_type( + __isl_take isl_schedule_tree *tree, int pos, + enum isl_ast_loop_type type); +enum isl_ast_loop_type isl_schedule_tree_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_tree *tree, int pos); +__isl_give isl_schedule_tree * +isl_schedule_tree_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_tree *tree, int pos, + enum isl_ast_loop_type type); +__isl_give isl_union_set *isl_schedule_tree_band_get_ast_build_options( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_band_set_ast_build_options( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *options); +__isl_give isl_set *isl_schedule_tree_band_get_ast_isolate_option( + __isl_keep isl_schedule_tree *tree, int depth); +__isl_give isl_set *isl_schedule_tree_context_get_context( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_union_set *isl_schedule_tree_domain_get_domain( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_domain_set_domain( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain); +__isl_give isl_union_pw_multi_aff *isl_schedule_tree_expansion_get_contraction( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_union_map *isl_schedule_tree_expansion_get_expansion( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree * +isl_schedule_tree_expansion_set_contraction_and_expansion( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion); +__isl_give isl_union_map *isl_schedule_tree_extension_get_extension( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_extension_set_extension( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_map *extension); +__isl_give isl_union_set *isl_schedule_tree_filter_get_filter( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_filter_set_filter( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter); +__isl_give isl_set *isl_schedule_tree_guard_get_guard( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_id *isl_schedule_tree_mark_get_id( + __isl_keep isl_schedule_tree *tree); + +__isl_give isl_schedule_tree *isl_schedule_tree_first_schedule_descendant( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_tree *leaf); +__isl_give isl_union_map *isl_schedule_tree_get_subtree_schedule_union_map( + __isl_keep isl_schedule_tree *tree); + +unsigned isl_schedule_tree_band_n_member(__isl_keep isl_schedule_tree *tree); + +isl_bool isl_schedule_tree_band_member_get_coincident( + __isl_keep isl_schedule_tree *tree, int pos); +__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_coincident( + __isl_take isl_schedule_tree *tree, int pos, int coincident); +isl_bool isl_schedule_tree_band_get_permutable( + __isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_band_set_permutable( + __isl_take isl_schedule_tree *tree, int permutable); + +int isl_schedule_tree_has_children(__isl_keep isl_schedule_tree *tree); +int isl_schedule_tree_n_children(__isl_keep isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_get_child( + __isl_keep isl_schedule_tree *tree, int pos); + +__isl_give isl_schedule_tree *isl_schedule_tree_insert_band( + __isl_take isl_schedule_tree *tree, __isl_take isl_schedule_band *band); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_context( + __isl_take isl_schedule_tree *tree, __isl_take isl_set *context); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_domain( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_expansion( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_extension( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_map *extension); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_filter( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter); +__isl_give isl_schedule_tree *isl_schedule_tree_children_insert_filter( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_guard( + __isl_take isl_schedule_tree *tree, __isl_take isl_set *guard); +__isl_give isl_schedule_tree *isl_schedule_tree_insert_mark( + __isl_take isl_schedule_tree *tree, __isl_take isl_id *mark); + +__isl_give isl_schedule_tree *isl_schedule_tree_append_to_leaves( + __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2); + +__isl_give isl_schedule_tree *isl_schedule_tree_band_scale( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_tree *isl_schedule_tree_band_scale_down( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_tree *isl_schedule_tree_band_mod( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv); +__isl_give isl_schedule_tree *isl_schedule_tree_band_tile( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *sizes); +__isl_give isl_schedule_tree *isl_schedule_tree_band_shift( + __isl_take isl_schedule_tree *tree, + __isl_take isl_multi_union_pw_aff *shift); +__isl_give isl_schedule_tree *isl_schedule_tree_band_split( + __isl_take isl_schedule_tree *tree, int pos, int depth); +__isl_give isl_schedule_tree *isl_schedule_tree_band_gist( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *context); + +__isl_give isl_schedule_tree *isl_schedule_tree_child( + __isl_take isl_schedule_tree *tree, int pos); +__isl_give isl_schedule_tree *isl_schedule_tree_reset_children( + __isl_take isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_drop_child( + __isl_take isl_schedule_tree *tree, int pos); +__isl_give isl_schedule_tree *isl_schedule_tree_replace_child( + __isl_take isl_schedule_tree *tree, int pos, + __isl_take isl_schedule_tree *new_child); +__isl_give isl_schedule_tree *isl_schedule_tree_sequence_splice( + __isl_take isl_schedule_tree *tree, int pos, + __isl_take isl_schedule_tree *child); + +__isl_give isl_schedule_tree *isl_schedule_tree_reset_user( + __isl_take isl_schedule_tree *tree); +__isl_give isl_schedule_tree *isl_schedule_tree_align_params( + __isl_take isl_schedule_tree *tree, __isl_take isl_space *space); +__isl_give isl_schedule_tree *isl_schedule_tree_pullback_union_pw_multi_aff( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_pw_multi_aff *upma); + +__isl_give isl_printer *isl_printer_print_schedule_tree( + __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree); +__isl_give isl_printer *isl_printer_print_schedule_tree_mark( + __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree, + int n_ancestor, int *child_pos); + +#endif Index: lib/Analysis/isl/isl_schedule_tree.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_schedule_tree.c @@ -0,0 +1,2853 @@ +/* + * Copyright 2013-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include + +#undef EL +#define EL isl_schedule_tree + +#include + +#undef BASE +#define BASE schedule_tree + +#include + +/* Is "tree" the leaf of a schedule tree? + */ +int isl_schedule_tree_is_leaf(__isl_keep isl_schedule_tree *tree) +{ + return isl_schedule_tree_get_type(tree) == isl_schedule_node_leaf; +} + +/* Create a new schedule tree of type "type". + * The caller is responsible for filling in the type specific fields and + * the children. + * + * By default, the single node tree does not have any anchored nodes. + * The caller is responsible for updating the anchored field if needed. + */ +static __isl_give isl_schedule_tree *isl_schedule_tree_alloc(isl_ctx *ctx, + enum isl_schedule_node_type type) +{ + isl_schedule_tree *tree; + + if (type == isl_schedule_node_error) + return NULL; + + tree = isl_calloc_type(ctx, isl_schedule_tree); + if (!tree) + return NULL; + + tree->ref = 1; + tree->ctx = ctx; + isl_ctx_ref(ctx); + tree->type = type; + tree->anchored = 0; + + return tree; +} + +/* Return a fresh copy of "tree". + */ +__isl_take isl_schedule_tree *isl_schedule_tree_dup( + __isl_keep isl_schedule_tree *tree) +{ + isl_ctx *ctx; + isl_schedule_tree *dup; + + if (!tree) + return NULL; + + ctx = isl_schedule_tree_get_ctx(tree); + dup = isl_schedule_tree_alloc(ctx, tree->type); + if (!dup) + return NULL; + + switch (tree->type) { + case isl_schedule_node_error: + isl_die(ctx, isl_error_internal, + "allocation should have failed", + isl_schedule_tree_free(dup)); + case isl_schedule_node_band: + dup->band = isl_schedule_band_copy(tree->band); + if (!dup->band) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_context: + dup->context = isl_set_copy(tree->context); + if (!dup->context) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_domain: + dup->domain = isl_union_set_copy(tree->domain); + if (!dup->domain) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_expansion: + dup->contraction = + isl_union_pw_multi_aff_copy(tree->contraction); + dup->expansion = isl_union_map_copy(tree->expansion); + if (!dup->contraction || !dup->expansion) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_extension: + dup->extension = isl_union_map_copy(tree->extension); + if (!dup->extension) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_filter: + dup->filter = isl_union_set_copy(tree->filter); + if (!dup->filter) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_guard: + dup->guard = isl_set_copy(tree->guard); + if (!dup->guard) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_mark: + dup->mark = isl_id_copy(tree->mark); + if (!dup->mark) + return isl_schedule_tree_free(dup); + break; + case isl_schedule_node_leaf: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + + if (tree->children) { + dup->children = isl_schedule_tree_list_copy(tree->children); + if (!dup->children) + return isl_schedule_tree_free(dup); + } + dup->anchored = tree->anchored; + + return dup; +} + +/* Return an isl_schedule_tree that is equal to "tree" and that has only + * a single reference. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_cow( + __isl_take isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->ref == 1) + return tree; + tree->ref--; + return isl_schedule_tree_dup(tree); +} + +/* Return a new reference to "tree". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_copy( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + tree->ref++; + return tree; +} + +/* Free "tree" and return NULL. + */ +__isl_null isl_schedule_tree *isl_schedule_tree_free( + __isl_take isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + if (--tree->ref > 0) + return NULL; + + switch (tree->type) { + case isl_schedule_node_band: + isl_schedule_band_free(tree->band); + break; + case isl_schedule_node_context: + isl_set_free(tree->context); + break; + case isl_schedule_node_domain: + isl_union_set_free(tree->domain); + break; + case isl_schedule_node_expansion: + isl_union_pw_multi_aff_free(tree->contraction); + isl_union_map_free(tree->expansion); + break; + case isl_schedule_node_extension: + isl_union_map_free(tree->extension); + break; + case isl_schedule_node_filter: + isl_union_set_free(tree->filter); + break; + case isl_schedule_node_guard: + isl_set_free(tree->guard); + break; + case isl_schedule_node_mark: + isl_id_free(tree->mark); + break; + case isl_schedule_node_sequence: + case isl_schedule_node_set: + case isl_schedule_node_error: + case isl_schedule_node_leaf: + break; + } + isl_schedule_tree_list_free(tree->children); + isl_ctx_deref(tree->ctx); + free(tree); + + return NULL; +} + +/* Create and return a new leaf schedule tree. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_leaf(isl_ctx *ctx) +{ + return isl_schedule_tree_alloc(ctx, isl_schedule_node_leaf); +} + +/* Create a new band schedule tree referring to "band" + * with no children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_band( + __isl_take isl_schedule_band *band) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!band) + return NULL; + + ctx = isl_schedule_band_get_ctx(band); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_band); + if (!tree) + goto error; + + tree->band = band; + tree->anchored = isl_schedule_band_is_anchored(band); + + return tree; +error: + isl_schedule_band_free(band); + return NULL; +} + +/* Create a new context schedule tree with the given context and no children. + * Since the context references the outer schedule dimension, + * the tree is anchored. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_context( + __isl_take isl_set *context) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!context) + return NULL; + + ctx = isl_set_get_ctx(context); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_context); + if (!tree) + goto error; + + tree->context = context; + tree->anchored = 1; + + return tree; +error: + isl_set_free(context); + return NULL; +} + +/* Create a new domain schedule tree with the given domain and no children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_domain( + __isl_take isl_union_set *domain) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!domain) + return NULL; + + ctx = isl_union_set_get_ctx(domain); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_domain); + if (!tree) + goto error; + + tree->domain = domain; + + return tree; +error: + isl_union_set_free(domain); + return NULL; +} + +/* Create a new expansion schedule tree with the given contraction and + * expansion and no children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_expansion( + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!contraction || !expansion) + goto error; + + ctx = isl_union_map_get_ctx(expansion); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_expansion); + if (!tree) + goto error; + + tree->contraction = contraction; + tree->expansion = expansion; + + return tree; +error: + isl_union_pw_multi_aff_free(contraction); + isl_union_map_free(expansion); + return NULL; +} + +/* Create a new extension schedule tree with the given extension and + * no children. + * Since the domain of the extension refers to the outer schedule dimension, + * the tree is anchored. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_extension( + __isl_take isl_union_map *extension) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!extension) + return NULL; + + ctx = isl_union_map_get_ctx(extension); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_extension); + if (!tree) + goto error; + + tree->extension = extension; + tree->anchored = 1; + + return tree; +error: + isl_union_map_free(extension); + return NULL; +} + +/* Create a new filter schedule tree with the given filter and no children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_filter( + __isl_take isl_union_set *filter) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!filter) + return NULL; + + ctx = isl_union_set_get_ctx(filter); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_filter); + if (!tree) + goto error; + + tree->filter = filter; + + return tree; +error: + isl_union_set_free(filter); + return NULL; +} + +/* Create a new guard schedule tree with the given guard and no children. + * Since the guard references the outer schedule dimension, + * the tree is anchored. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_guard( + __isl_take isl_set *guard) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!guard) + return NULL; + + ctx = isl_set_get_ctx(guard); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_guard); + if (!tree) + goto error; + + tree->guard = guard; + tree->anchored = 1; + + return tree; +error: + isl_set_free(guard); + return NULL; +} + +/* Create a new mark schedule tree with the given mark identifier and + * no children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_mark( + __isl_take isl_id *mark) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!mark) + return NULL; + + ctx = isl_id_get_ctx(mark); + tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_mark); + if (!tree) + goto error; + + tree->mark = mark; + + return tree; +error: + isl_id_free(mark); + return NULL; +} + +/* Does "tree" have any node that depends on its position + * in the complete schedule tree? + */ +isl_bool isl_schedule_tree_is_subtree_anchored( + __isl_keep isl_schedule_tree *tree) +{ + return tree ? tree->anchored : isl_bool_error; +} + +/* Does the root node of "tree" depend on its position in the complete + * schedule tree? + * Band nodes may be anchored depending on the associated AST build options. + * Context, extension and guard nodes are always anchored. + */ +int isl_schedule_tree_is_anchored(__isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return -1; + + switch (isl_schedule_tree_get_type(tree)) { + case isl_schedule_node_error: + return -1; + case isl_schedule_node_band: + return isl_schedule_band_is_anchored(tree->band); + case isl_schedule_node_context: + case isl_schedule_node_extension: + case isl_schedule_node_guard: + return 1; + case isl_schedule_node_domain: + case isl_schedule_node_expansion: + case isl_schedule_node_filter: + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + return 0; + } + + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "unhandled case", return -1); +} + +/* Update the anchored field of "tree" based on whether the root node + * itself in anchored and the anchored fields of the children. + * + * This function should be called whenever the children of a tree node + * are changed or the anchoredness of the tree root itself changes. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_update_anchored( + __isl_take isl_schedule_tree *tree) +{ + int i, n; + int anchored; + + if (!tree) + return NULL; + + anchored = isl_schedule_tree_is_anchored(tree); + if (anchored < 0) + return isl_schedule_tree_free(tree); + + n = isl_schedule_tree_list_n_schedule_tree(tree->children); + for (i = 0; !anchored && i < n; ++i) { + isl_schedule_tree *child; + + child = isl_schedule_tree_get_child(tree, i); + if (!child) + return isl_schedule_tree_free(tree); + anchored = child->anchored; + isl_schedule_tree_free(child); + } + + if (anchored == tree->anchored) + return tree; + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + tree->anchored = anchored; + return tree; +} + +/* Create a new tree of the given type (isl_schedule_node_sequence or + * isl_schedule_node_set) with the given children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_children( + enum isl_schedule_node_type type, + __isl_take isl_schedule_tree_list *list) +{ + isl_ctx *ctx; + isl_schedule_tree *tree; + + if (!list) + return NULL; + + ctx = isl_schedule_tree_list_get_ctx(list); + tree = isl_schedule_tree_alloc(ctx, type); + if (!tree) + goto error; + + tree->children = list; + tree = isl_schedule_tree_update_anchored(tree); + + return tree; +error: + isl_schedule_tree_list_free(list); + return NULL; +} + +/* Construct a tree with a root node of type "type" and as children + * "tree1" and "tree2". + * If the root of one (or both) of the input trees is itself of type "type", + * then the tree is replaced by its children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_from_pair( + enum isl_schedule_node_type type, __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2) +{ + isl_ctx *ctx; + isl_schedule_tree_list *list; + + if (!tree1 || !tree2) + goto error; + + ctx = isl_schedule_tree_get_ctx(tree1); + if (isl_schedule_tree_get_type(tree1) == type) { + list = isl_schedule_tree_list_copy(tree1->children); + isl_schedule_tree_free(tree1); + } else { + list = isl_schedule_tree_list_alloc(ctx, 2); + list = isl_schedule_tree_list_add(list, tree1); + } + if (isl_schedule_tree_get_type(tree2) == type) { + isl_schedule_tree_list *children; + + children = isl_schedule_tree_list_copy(tree2->children); + list = isl_schedule_tree_list_concat(list, children); + isl_schedule_tree_free(tree2); + } else { + list = isl_schedule_tree_list_add(list, tree2); + } + + return isl_schedule_tree_from_children(type, list); +error: + isl_schedule_tree_free(tree1); + isl_schedule_tree_free(tree2); + return NULL; +} + +/* Construct a tree with a sequence root node and as children + * "tree1" and "tree2". + * If the root of one (or both) of the input trees is itself a sequence, + * then the tree is replaced by its children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_sequence_pair( + __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2) +{ + return isl_schedule_tree_from_pair(isl_schedule_node_sequence, + tree1, tree2); +} + +/* Construct a tree with a set root node and as children + * "tree1" and "tree2". + * If the root of one (or both) of the input trees is itself a set, + * then the tree is replaced by its children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_set_pair( + __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2) +{ + return isl_schedule_tree_from_pair(isl_schedule_node_set, tree1, tree2); +} + +/* Return the isl_ctx to which "tree" belongs. + */ +isl_ctx *isl_schedule_tree_get_ctx(__isl_keep isl_schedule_tree *tree) +{ + return tree ? tree->ctx : NULL; +} + +/* Return the type of the root of the tree or isl_schedule_node_error + * on error. + */ +enum isl_schedule_node_type isl_schedule_tree_get_type( + __isl_keep isl_schedule_tree *tree) +{ + return tree ? tree->type : isl_schedule_node_error; +} + +/* Are "tree1" and "tree2" obviously equal to each other? + */ +isl_bool isl_schedule_tree_plain_is_equal(__isl_keep isl_schedule_tree *tree1, + __isl_keep isl_schedule_tree *tree2) +{ + isl_bool equal; + int i, n; + + if (!tree1 || !tree2) + return isl_bool_error; + if (tree1 == tree2) + return isl_bool_true; + if (tree1->type != tree2->type) + return isl_bool_false; + + switch (tree1->type) { + case isl_schedule_node_band: + equal = isl_schedule_band_plain_is_equal(tree1->band, + tree2->band); + break; + case isl_schedule_node_context: + equal = isl_set_is_equal(tree1->context, tree2->context); + break; + case isl_schedule_node_domain: + equal = isl_union_set_is_equal(tree1->domain, tree2->domain); + break; + case isl_schedule_node_expansion: + equal = isl_union_map_is_equal(tree1->expansion, + tree2->expansion); + if (equal >= 0 && equal) + equal = isl_union_pw_multi_aff_plain_is_equal( + tree1->contraction, tree2->contraction); + break; + case isl_schedule_node_extension: + equal = isl_union_map_is_equal(tree1->extension, + tree2->extension); + break; + case isl_schedule_node_filter: + equal = isl_union_set_is_equal(tree1->filter, tree2->filter); + break; + case isl_schedule_node_guard: + equal = isl_set_is_equal(tree1->guard, tree2->guard); + break; + case isl_schedule_node_mark: + equal = tree1->mark == tree2->mark; + break; + case isl_schedule_node_leaf: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + equal = isl_bool_true; + break; + case isl_schedule_node_error: + equal = isl_bool_error; + break; + } + + if (equal < 0 || !equal) + return equal; + + n = isl_schedule_tree_n_children(tree1); + if (n != isl_schedule_tree_n_children(tree2)) + return isl_bool_false; + for (i = 0; i < n; ++i) { + isl_schedule_tree *child1, *child2; + + child1 = isl_schedule_tree_get_child(tree1, i); + child2 = isl_schedule_tree_get_child(tree2, i); + equal = isl_schedule_tree_plain_is_equal(child1, child2); + isl_schedule_tree_free(child1); + isl_schedule_tree_free(child2); + + if (equal < 0 || !equal) + return equal; + } + + return isl_bool_true; +} + +/* Does "tree" have any children, other than an implicit leaf. + */ +int isl_schedule_tree_has_children(__isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return -1; + + return tree->children != NULL; +} + +/* Return the number of children of "tree", excluding implicit leaves. + */ +int isl_schedule_tree_n_children(__isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return -1; + + return isl_schedule_tree_list_n_schedule_tree(tree->children); +} + +/* Return a copy of the (explicit) child at position "pos" of "tree". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_get_child( + __isl_keep isl_schedule_tree *tree, int pos) +{ + if (!tree) + return NULL; + if (!tree->children) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "schedule tree has no explicit children", return NULL); + return isl_schedule_tree_list_get_schedule_tree(tree->children, pos); +} + +/* Return a copy of the (explicit) child at position "pos" of "tree" and + * free "tree". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_child( + __isl_take isl_schedule_tree *tree, int pos) +{ + isl_schedule_tree *child; + + child = isl_schedule_tree_get_child(tree, pos); + isl_schedule_tree_free(tree); + return child; +} + +/* Remove all (explicit) children from "tree". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_reset_children( + __isl_take isl_schedule_tree *tree) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + tree->children = isl_schedule_tree_list_free(tree->children); + return tree; +} + +/* Remove the child at position "pos" from the children of "tree". + * If there was only one child to begin with, then remove all children. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_drop_child( + __isl_take isl_schedule_tree *tree, int pos) +{ + int n; + + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + + if (!isl_schedule_tree_has_children(tree)) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "tree does not have any explicit children", + return isl_schedule_tree_free(tree)); + n = isl_schedule_tree_list_n_schedule_tree(tree->children); + if (pos < 0 || pos >= n) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "position out of bounds", + return isl_schedule_tree_free(tree)); + if (n == 1) + return isl_schedule_tree_reset_children(tree); + + tree->children = isl_schedule_tree_list_drop(tree->children, pos, 1); + if (!tree->children) + return isl_schedule_tree_free(tree); + + return tree; +} + +/* Replace the child at position "pos" of "tree" by "child". + * + * If the new child is a leaf, then it is not explicitly + * recorded in the list of children. Instead, the list of children + * (which is assumed to have only one element) is removed. + * Note that the children of set and sequence nodes are always + * filters, so they cannot be replaced by empty trees. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_replace_child( + __isl_take isl_schedule_tree *tree, int pos, + __isl_take isl_schedule_tree *child) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !child) + goto error; + + if (isl_schedule_tree_is_leaf(child)) { + isl_schedule_tree_free(child); + if (!tree->children && pos == 0) + return tree; + if (isl_schedule_tree_n_children(tree) != 1) + isl_die(isl_schedule_tree_get_ctx(tree), + isl_error_internal, + "can only replace single child by leaf", + goto error); + return isl_schedule_tree_reset_children(tree); + } + + if (!tree->children && pos == 0) + tree->children = + isl_schedule_tree_list_from_schedule_tree(child); + else + tree->children = isl_schedule_tree_list_set_schedule_tree( + tree->children, pos, child); + + if (!tree->children) + return isl_schedule_tree_free(tree); + tree = isl_schedule_tree_update_anchored(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_schedule_tree_free(child); + return NULL; +} + +/* Replace the (explicit) children of "tree" by "children"? + */ +__isl_give isl_schedule_tree *isl_schedule_tree_set_children( + __isl_take isl_schedule_tree *tree, + __isl_take isl_schedule_tree_list *children) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !children) + goto error; + isl_schedule_tree_list_free(tree->children); + tree->children = children; + return tree; +error: + isl_schedule_tree_free(tree); + isl_schedule_tree_list_free(children); + return NULL; +} + +/* Create a new band schedule tree referring to "band" + * with "tree" as single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_band( + __isl_take isl_schedule_tree *tree, __isl_take isl_schedule_band *band) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_band(band); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Create a new context schedule tree with the given context and + * with "tree" as single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_context( + __isl_take isl_schedule_tree *tree, __isl_take isl_set *context) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_context(context); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Create a new domain schedule tree with the given domain and + * with "tree" as single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_domain( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_domain(domain); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Create a new expansion schedule tree with the given contraction and + * expansion and with "tree" as single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_expansion( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_expansion(contraction, expansion); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Create a new extension schedule tree with the given extension and + * with "tree" as single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_extension( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_map *extension) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_extension(extension); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Create a new filter schedule tree with the given filter and single child. + * + * If the root of "tree" is itself a filter node, then the two + * filter nodes are merged into one node. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_filter( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter) +{ + isl_schedule_tree *res; + + if (isl_schedule_tree_get_type(tree) == isl_schedule_node_filter) { + isl_union_set *tree_filter; + + tree_filter = isl_schedule_tree_filter_get_filter(tree); + tree_filter = isl_union_set_intersect(tree_filter, filter); + tree = isl_schedule_tree_filter_set_filter(tree, tree_filter); + return tree; + } + + res = isl_schedule_tree_from_filter(filter); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Insert a filter node with filter set "filter" + * in each of the children of "tree". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_children_insert_filter( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter) +{ + int i, n; + + if (!tree || !filter) + goto error; + + n = isl_schedule_tree_n_children(tree); + for (i = 0; i < n; ++i) { + isl_schedule_tree *child; + + child = isl_schedule_tree_get_child(tree, i); + child = isl_schedule_tree_insert_filter(child, + isl_union_set_copy(filter)); + tree = isl_schedule_tree_replace_child(tree, i, child); + } + + isl_union_set_free(filter); + return tree; +error: + isl_union_set_free(filter); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Create a new guard schedule tree with the given guard and + * with "tree" as single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_guard( + __isl_take isl_schedule_tree *tree, __isl_take isl_set *guard) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_guard(guard); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Create a new mark schedule tree with the given mark identifier and + * single child. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_insert_mark( + __isl_take isl_schedule_tree *tree, __isl_take isl_id *mark) +{ + isl_schedule_tree *res; + + res = isl_schedule_tree_from_mark(mark); + return isl_schedule_tree_replace_child(res, 0, tree); +} + +/* Return the number of members in the band tree root. + */ +unsigned isl_schedule_tree_band_n_member(__isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return 0; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return 0); + + return isl_schedule_band_n_member(tree->band); +} + +/* Is the band member at position "pos" of the band tree root + * marked coincident? + */ +isl_bool isl_schedule_tree_band_member_get_coincident( + __isl_keep isl_schedule_tree *tree, int pos) +{ + if (!tree) + return isl_bool_error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_bool_error); + + return isl_schedule_band_member_get_coincident(tree->band, pos); +} + +/* Mark the given band member as being coincident or not + * according to "coincident". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_coincident( + __isl_take isl_schedule_tree *tree, int pos, int coincident) +{ + if (!tree) + return NULL; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_schedule_tree_free(tree)); + if (isl_schedule_tree_band_member_get_coincident(tree, pos) == + coincident) + return tree; + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + + tree->band = isl_schedule_band_member_set_coincident(tree->band, pos, + coincident); + if (!tree->band) + return isl_schedule_tree_free(tree); + return tree; +} + +/* Is the band tree root marked permutable? + */ +isl_bool isl_schedule_tree_band_get_permutable( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return isl_bool_error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_bool_error); + + return isl_schedule_band_get_permutable(tree->band); +} + +/* Mark the band tree root permutable or not according to "permutable"? + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_set_permutable( + __isl_take isl_schedule_tree *tree, int permutable) +{ + if (!tree) + return NULL; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_schedule_tree_free(tree)); + if (isl_schedule_tree_band_get_permutable(tree) == permutable) + return tree; + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + + tree->band = isl_schedule_band_set_permutable(tree->band, permutable); + if (!tree->band) + return isl_schedule_tree_free(tree); + return tree; +} + +/* Return the schedule space of the band tree root. + */ +__isl_give isl_space *isl_schedule_tree_band_get_space( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return NULL); + + return isl_schedule_band_get_space(tree->band); +} + +/* Intersect the domain of the band schedule of the band tree root + * with "domain". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_intersect_domain( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain) +{ + if (!tree || !domain) + goto error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + tree->band = isl_schedule_band_intersect_domain(tree->band, domain); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_union_set_free(domain); + return NULL; +} + +/* Return the schedule of the band tree root in isolation. + */ +__isl_give isl_multi_union_pw_aff *isl_schedule_tree_band_get_partial_schedule( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return NULL); + + return isl_schedule_band_get_partial_schedule(tree->band); +} + +/* Replace the schedule of the band tree root by "schedule". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_set_partial_schedule( + __isl_take isl_schedule_tree *tree, + __isl_take isl_multi_union_pw_aff *schedule) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !schedule) + goto error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return NULL); + tree->band = isl_schedule_band_set_partial_schedule(tree->band, + schedule); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_multi_union_pw_aff_free(schedule); + return NULL; +} + +/* Return the loop AST generation type for the band member + * of the band tree root at position "pos". + */ +enum isl_ast_loop_type isl_schedule_tree_band_member_get_ast_loop_type( + __isl_keep isl_schedule_tree *tree, int pos) +{ + if (!tree) + return isl_ast_loop_error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_ast_loop_error); + + return isl_schedule_band_member_get_ast_loop_type(tree->band, pos); +} + +/* Set the loop AST generation type for the band member of the band tree root + * at position "pos" to "type". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_ast_loop_type( + __isl_take isl_schedule_tree *tree, int pos, + enum isl_ast_loop_type type) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_schedule_tree_free(tree)); + + tree->band = isl_schedule_band_member_set_ast_loop_type(tree->band, + pos, type); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +} + +/* Return the loop AST generation type for the band member + * of the band tree root at position "pos" for the isolated part. + */ +enum isl_ast_loop_type isl_schedule_tree_band_member_get_isolate_ast_loop_type( + __isl_keep isl_schedule_tree *tree, int pos) +{ + if (!tree) + return isl_ast_loop_error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_ast_loop_error); + + return isl_schedule_band_member_get_isolate_ast_loop_type(tree->band, + pos); +} + +/* Set the loop AST generation type for the band member of the band tree root + * at position "pos" for the isolated part to "type". + */ +__isl_give isl_schedule_tree * +isl_schedule_tree_band_member_set_isolate_ast_loop_type( + __isl_take isl_schedule_tree *tree, int pos, + enum isl_ast_loop_type type) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_schedule_tree_free(tree)); + + tree->band = isl_schedule_band_member_set_isolate_ast_loop_type( + tree->band, pos, type); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +} + +/* Return the AST build options associated to the band tree root. + */ +__isl_give isl_union_set *isl_schedule_tree_band_get_ast_build_options( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return NULL); + + return isl_schedule_band_get_ast_build_options(tree->band); +} + +/* Replace the AST build options associated to band tree root by "options". + * Updated the anchored field if the anchoredness of the root node itself + * changes. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_set_ast_build_options( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *options) +{ + int was_anchored; + + tree = isl_schedule_tree_cow(tree); + if (!tree || !options) + goto error; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + was_anchored = isl_schedule_tree_is_anchored(tree); + tree->band = isl_schedule_band_set_ast_build_options(tree->band, + options); + if (!tree->band) + return isl_schedule_tree_free(tree); + if (isl_schedule_tree_is_anchored(tree) != was_anchored) + tree = isl_schedule_tree_update_anchored(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_union_set_free(options); + return NULL; +} + +/* Return the "isolate" option associated to the band tree root of "tree", + * which is assumed to appear at schedule depth "depth". + */ +__isl_give isl_set *isl_schedule_tree_band_get_ast_isolate_option( + __isl_keep isl_schedule_tree *tree, int depth) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return NULL); + + return isl_schedule_band_get_ast_isolate_option(tree->band, depth); +} + +/* Return the context of the context tree root. + */ +__isl_give isl_set *isl_schedule_tree_context_get_context( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_context) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a context node", return NULL); + + return isl_set_copy(tree->context); +} + +/* Return the domain of the domain tree root. + */ +__isl_give isl_union_set *isl_schedule_tree_domain_get_domain( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_domain) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a domain node", return NULL); + + return isl_union_set_copy(tree->domain); +} + +/* Replace the domain of domain tree root "tree" by "domain". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_domain_set_domain( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !domain) + goto error; + + if (tree->type != isl_schedule_node_domain) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a domain node", goto error); + + isl_union_set_free(tree->domain); + tree->domain = domain; + + return tree; +error: + isl_schedule_tree_free(tree); + isl_union_set_free(domain); + return NULL; +} + +/* Return the contraction of the expansion tree root. + */ +__isl_give isl_union_pw_multi_aff *isl_schedule_tree_expansion_get_contraction( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_expansion) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not an expansion node", return NULL); + + return isl_union_pw_multi_aff_copy(tree->contraction); +} + +/* Return the expansion of the expansion tree root. + */ +__isl_give isl_union_map *isl_schedule_tree_expansion_get_expansion( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_expansion) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not an expansion node", return NULL); + + return isl_union_map_copy(tree->expansion); +} + +/* Replace the contraction and the expansion of the expansion tree root "tree" + * by "contraction" and "expansion". + */ +__isl_give isl_schedule_tree * +isl_schedule_tree_expansion_set_contraction_and_expansion( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_pw_multi_aff *contraction, + __isl_take isl_union_map *expansion) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !contraction || !expansion) + goto error; + + if (tree->type != isl_schedule_node_expansion) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not an expansion node", return NULL); + + isl_union_pw_multi_aff_free(tree->contraction); + tree->contraction = contraction; + isl_union_map_free(tree->expansion); + tree->expansion = expansion; + + return tree; +error: + isl_schedule_tree_free(tree); + isl_union_pw_multi_aff_free(contraction); + isl_union_map_free(expansion); + return NULL; +} + +/* Return the extension of the extension tree root. + */ +__isl_give isl_union_map *isl_schedule_tree_extension_get_extension( + __isl_take isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_extension) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not an extension node", return NULL); + + return isl_union_map_copy(tree->extension); +} + +/* Replace the extension of extension tree root "tree" by "extension". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_extension_set_extension( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_map *extension) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !extension) + goto error; + + if (tree->type != isl_schedule_node_extension) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not an extension node", return NULL); + isl_union_map_free(tree->extension); + tree->extension = extension; + + return tree; +error: + isl_schedule_tree_free(tree); + isl_union_map_free(extension); + return NULL; +} + +/* Return the filter of the filter tree root. + */ +__isl_give isl_union_set *isl_schedule_tree_filter_get_filter( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_filter) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a filter node", return NULL); + + return isl_union_set_copy(tree->filter); +} + +/* Replace the filter of the filter tree root by "filter". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_filter_set_filter( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter) +{ + tree = isl_schedule_tree_cow(tree); + if (!tree || !filter) + goto error; + + if (tree->type != isl_schedule_node_filter) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a filter node", return NULL); + + isl_union_set_free(tree->filter); + tree->filter = filter; + + return tree; +error: + isl_schedule_tree_free(tree); + isl_union_set_free(filter); + return NULL; +} + +/* Return the guard of the guard tree root. + */ +__isl_give isl_set *isl_schedule_tree_guard_get_guard( + __isl_take isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_guard) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a guard node", return NULL); + + return isl_set_copy(tree->guard); +} + +/* Return the mark identifier of the mark tree root "tree". + */ +__isl_give isl_id *isl_schedule_tree_mark_get_id( + __isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return NULL; + + if (tree->type != isl_schedule_node_mark) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a mark node", return NULL); + + return isl_id_copy(tree->mark); +} + +/* Set dim to the range dimension of "map" and abort the search. + */ +static isl_stat set_range_dim(__isl_take isl_map *map, void *user) +{ + int *dim = user; + + *dim = isl_map_dim(map, isl_dim_out); + isl_map_free(map); + + return isl_stat_error; +} + +/* Return the dimension of the range of "umap". + * "umap" is assumed not to be empty and + * all maps inside "umap" are assumed to have the same range. + * + * We extract the range dimension from the first map in "umap". + */ +static int range_dim(__isl_keep isl_union_map *umap) +{ + int dim = -1; + + if (!umap) + return -1; + if (isl_union_map_n_map(umap) == 0) + isl_die(isl_union_map_get_ctx(umap), isl_error_internal, + "unexpected empty input", return -1); + + isl_union_map_foreach_map(umap, &set_range_dim, &dim); + + return dim; +} + +/* Append an "extra" number of zeros to the range of "umap" and + * return the result. + */ +static __isl_give isl_union_map *append_range(__isl_take isl_union_map *umap, + int extra) +{ + isl_union_set *dom; + isl_space *space; + isl_multi_val *mv; + isl_union_pw_multi_aff *suffix; + isl_union_map *universe; + isl_union_map *suffix_umap; + + universe = isl_union_map_universe(isl_union_map_copy(umap)); + dom = isl_union_map_domain(universe); + space = isl_union_set_get_space(dom); + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, extra); + mv = isl_multi_val_zero(space); + + suffix = isl_union_pw_multi_aff_multi_val_on_domain(dom, mv); + suffix_umap = isl_union_map_from_union_pw_multi_aff(suffix); + umap = isl_union_map_flat_range_product(umap, suffix_umap); + + return umap; +} + +/* Should we skip the root of "tree" while looking for the first + * descendant with schedule information? + * That is, is it impossible to derive any information about + * the iteration domain from this node? + * + * We do not want to skip leaf or error nodes because there is + * no point in looking any deeper from these nodes. + * We can only extract partial iteration domain information + * from an extension node, but extension nodes are not supported + * by the caller and it will error out on them. + */ +static int domain_less(__isl_keep isl_schedule_tree *tree) +{ + enum isl_schedule_node_type type; + + type = isl_schedule_tree_get_type(tree); + switch (type) { + case isl_schedule_node_band: + return isl_schedule_tree_band_n_member(tree) == 0; + case isl_schedule_node_context: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + return 1; + case isl_schedule_node_leaf: + case isl_schedule_node_error: + case isl_schedule_node_domain: + case isl_schedule_node_expansion: + case isl_schedule_node_extension: + case isl_schedule_node_filter: + case isl_schedule_node_set: + case isl_schedule_node_sequence: + return 0; + } + + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "unhandled case", return 0); +} + +/* Move down to the first descendant of "tree" that contains any schedule + * information or return "leaf" if there is no such descendant. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_first_schedule_descendant( + __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_tree *leaf) +{ + while (domain_less(tree)) { + if (!isl_schedule_tree_has_children(tree)) { + isl_schedule_tree_free(tree); + return isl_schedule_tree_copy(leaf); + } + tree = isl_schedule_tree_child(tree, 0); + } + + return tree; +} + +static __isl_give isl_union_map *subtree_schedule_extend( + __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer); + +/* Extend the schedule map "outer" with the subtree schedule + * of the (single) child of "tree", if any. + * + * If "tree" does not have any descendants (apart from those that + * do not carry any schedule information), then we simply return "outer". + * Otherwise, we extend the schedule map "outer" with the subtree schedule + * of the single child. + */ +static __isl_give isl_union_map *subtree_schedule_extend_child( + __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer) +{ + isl_schedule_tree *child; + isl_union_map *res; + + if (!tree) + return isl_union_map_free(outer); + if (!isl_schedule_tree_has_children(tree)) + return outer; + child = isl_schedule_tree_get_child(tree, 0); + if (!child) + return isl_union_map_free(outer); + res = subtree_schedule_extend(child, outer); + isl_schedule_tree_free(child); + return res; +} + +/* Extract the parameter space from one of the children of "tree", + * which are assumed to be filters. + */ +static __isl_give isl_space *extract_space_from_filter_child( + __isl_keep isl_schedule_tree *tree) +{ + isl_space *space; + isl_union_set *dom; + isl_schedule_tree *child; + + child = isl_schedule_tree_list_get_schedule_tree(tree->children, 0); + dom = isl_schedule_tree_filter_get_filter(child); + space = isl_union_set_get_space(dom); + isl_union_set_free(dom); + isl_schedule_tree_free(child); + + return space; +} + +/* Extend the schedule map "outer" with the subtree schedule + * of a set or sequence node. + * + * The schedule for the set or sequence node itself is composed of + * pieces of the form + * + * filter -> [] + * + * or + * + * filter -> [index] + * + * The first form is used if there is only a single child or + * if the current node is a set node and the schedule_separate_components + * option is not set. + * + * Each of the pieces above is extended with the subtree schedule of + * the child of the corresponding filter, if any, padded with zeros + * to ensure that all pieces have the same range dimension. + */ +static __isl_give isl_union_map *subtree_schedule_extend_from_children( + __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer) +{ + int i, n; + int dim; + int separate; + isl_ctx *ctx; + isl_val *v = NULL; + isl_multi_val *mv; + isl_space *space; + isl_union_map *umap; + + if (!tree) + return NULL; + + ctx = isl_schedule_tree_get_ctx(tree); + if (!tree->children) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "missing children", return NULL); + n = isl_schedule_tree_list_n_schedule_tree(tree->children); + if (n == 0) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "missing children", return NULL); + + separate = n > 1 && (tree->type == isl_schedule_node_sequence || + isl_options_get_schedule_separate_components(ctx)); + + space = extract_space_from_filter_child(tree); + + umap = isl_union_map_empty(isl_space_copy(space)); + space = isl_space_set_from_params(space); + if (separate) { + space = isl_space_add_dims(space, isl_dim_set, 1); + v = isl_val_zero(ctx); + } + mv = isl_multi_val_zero(space); + + dim = isl_multi_val_dim(mv, isl_dim_set); + for (i = 0; i < n; ++i) { + isl_union_pw_multi_aff *upma; + isl_union_map *umap_i; + isl_union_set *dom; + isl_schedule_tree *child; + int dim_i; + int empty; + + child = isl_schedule_tree_list_get_schedule_tree( + tree->children, i); + dom = isl_schedule_tree_filter_get_filter(child); + + if (separate) { + mv = isl_multi_val_set_val(mv, 0, isl_val_copy(v)); + v = isl_val_add_ui(v, 1); + } + upma = isl_union_pw_multi_aff_multi_val_on_domain(dom, + isl_multi_val_copy(mv)); + umap_i = isl_union_map_from_union_pw_multi_aff(upma); + umap_i = isl_union_map_flat_range_product( + isl_union_map_copy(outer), umap_i); + umap_i = subtree_schedule_extend_child(child, umap_i); + isl_schedule_tree_free(child); + + empty = isl_union_map_is_empty(umap_i); + if (empty < 0) + umap_i = isl_union_map_free(umap_i); + else if (empty) { + isl_union_map_free(umap_i); + continue; + } + + dim_i = range_dim(umap_i); + if (dim_i < 0) { + umap = isl_union_map_free(umap); + } else if (dim < dim_i) { + umap = append_range(umap, dim_i - dim); + dim = dim_i; + } else if (dim_i < dim) { + umap_i = append_range(umap_i, dim - dim_i); + } + umap = isl_union_map_union(umap, umap_i); + } + + isl_val_free(v); + isl_multi_val_free(mv); + isl_union_map_free(outer); + + return umap; +} + +/* Extend the schedule map "outer" with the subtree schedule of "tree". + * + * If the root of the tree is a set or a sequence, then we extend + * the schedule map in subtree_schedule_extend_from_children. + * Otherwise, we extend the schedule map with the partial schedule + * corresponding to the root of the tree and then continue with + * the single child of this root. + * In the special case of an expansion, the schedule map is "extended" + * by applying the expansion to the domain of the schedule map. + */ +static __isl_give isl_union_map *subtree_schedule_extend( + __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer) +{ + isl_multi_union_pw_aff *mupa; + isl_union_map *umap; + isl_union_set *domain; + + if (!tree) + return NULL; + + switch (tree->type) { + case isl_schedule_node_error: + return isl_union_map_free(outer); + case isl_schedule_node_extension: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "cannot construct subtree schedule of tree " + "with extension nodes", + return isl_union_map_free(outer)); + case isl_schedule_node_context: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + return subtree_schedule_extend_child(tree, outer); + case isl_schedule_node_band: + if (isl_schedule_tree_band_n_member(tree) == 0) + return subtree_schedule_extend_child(tree, outer); + mupa = isl_schedule_band_get_partial_schedule(tree->band); + umap = isl_union_map_from_multi_union_pw_aff(mupa); + outer = isl_union_map_flat_range_product(outer, umap); + umap = subtree_schedule_extend_child(tree, outer); + break; + case isl_schedule_node_domain: + domain = isl_schedule_tree_domain_get_domain(tree); + umap = isl_union_map_from_domain(domain); + outer = isl_union_map_flat_range_product(outer, umap); + umap = subtree_schedule_extend_child(tree, outer); + break; + case isl_schedule_node_expansion: + umap = isl_schedule_tree_expansion_get_expansion(tree); + outer = isl_union_map_apply_domain(outer, umap); + umap = subtree_schedule_extend_child(tree, outer); + break; + case isl_schedule_node_filter: + domain = isl_schedule_tree_filter_get_filter(tree); + umap = isl_union_map_from_domain(domain); + outer = isl_union_map_flat_range_product(outer, umap); + umap = subtree_schedule_extend_child(tree, outer); + break; + case isl_schedule_node_leaf: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "leaf node should be handled by caller", return NULL); + case isl_schedule_node_set: + case isl_schedule_node_sequence: + umap = subtree_schedule_extend_from_children(tree, outer); + break; + } + + return umap; +} + +static __isl_give isl_union_set *initial_domain( + __isl_keep isl_schedule_tree *tree); + +/* Extract a universe domain from the children of the tree root "tree", + * which is a set or sequence, meaning that its children are filters. + * In particular, return the union of the universes of the filters. + */ +static __isl_give isl_union_set *initial_domain_from_children( + __isl_keep isl_schedule_tree *tree) +{ + int i, n; + isl_space *space; + isl_union_set *domain; + + if (!tree->children) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "missing children", return NULL); + n = isl_schedule_tree_list_n_schedule_tree(tree->children); + if (n == 0) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "missing children", return NULL); + + space = extract_space_from_filter_child(tree); + domain = isl_union_set_empty(space); + + for (i = 0; i < n; ++i) { + isl_schedule_tree *child; + isl_union_set *domain_i; + + child = isl_schedule_tree_get_child(tree, i); + domain_i = initial_domain(child); + domain = isl_union_set_union(domain, domain_i); + isl_schedule_tree_free(child); + } + + return domain; +} + +/* Extract a universe domain from the tree root "tree". + * The caller is responsible for making sure that this node + * would not be skipped by isl_schedule_tree_first_schedule_descendant + * and that it is not a leaf node. + */ +static __isl_give isl_union_set *initial_domain( + __isl_keep isl_schedule_tree *tree) +{ + isl_multi_union_pw_aff *mupa; + isl_union_set *domain; + isl_union_map *exp; + + if (!tree) + return NULL; + + switch (tree->type) { + case isl_schedule_node_error: + return NULL; + case isl_schedule_node_context: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "context node should be handled by caller", + return NULL); + case isl_schedule_node_guard: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "guard node should be handled by caller", + return NULL); + case isl_schedule_node_mark: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "mark node should be handled by caller", + return NULL); + case isl_schedule_node_extension: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "cannot construct subtree schedule of tree " + "with extension nodes", return NULL); + case isl_schedule_node_band: + if (isl_schedule_tree_band_n_member(tree) == 0) + isl_die(isl_schedule_tree_get_ctx(tree), + isl_error_internal, + "0D band should be handled by caller", + return NULL); + mupa = isl_schedule_band_get_partial_schedule(tree->band); + domain = isl_multi_union_pw_aff_domain(mupa); + domain = isl_union_set_universe(domain); + break; + case isl_schedule_node_domain: + domain = isl_schedule_tree_domain_get_domain(tree); + domain = isl_union_set_universe(domain); + break; + case isl_schedule_node_expansion: + exp = isl_schedule_tree_expansion_get_expansion(tree); + exp = isl_union_map_universe(exp); + domain = isl_union_map_domain(exp); + break; + case isl_schedule_node_filter: + domain = isl_schedule_tree_filter_get_filter(tree); + domain = isl_union_set_universe(domain); + break; + case isl_schedule_node_leaf: + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "leaf node should be handled by caller", return NULL); + case isl_schedule_node_set: + case isl_schedule_node_sequence: + domain = initial_domain_from_children(tree); + break; + } + + return domain; +} + +/* Return the subtree schedule of a node that contains some schedule + * information, i.e., a node that would not be skipped by + * isl_schedule_tree_first_schedule_descendant and that is not a leaf. + * + * If the tree contains any expansions, then the returned subtree + * schedule is formulated in terms of the expanded domains. + * The tree is not allowed to contain any extension nodes. + * + * We start with an initial zero-dimensional subtree schedule based + * on the domain information in the root node and then extend it + * based on the schedule information in the root node and its descendants. + */ +__isl_give isl_union_map *isl_schedule_tree_get_subtree_schedule_union_map( + __isl_keep isl_schedule_tree *tree) +{ + isl_union_set *domain; + isl_union_map *umap; + + domain = initial_domain(tree); + umap = isl_union_map_from_domain(domain); + return subtree_schedule_extend(tree, umap); +} + +/* Multiply the partial schedule of the band root node of "tree" + * with the factors in "mv". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_scale( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv) +{ + if (!tree || !mv) + goto error; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + tree->band = isl_schedule_band_scale(tree->band, mv); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_multi_val_free(mv); + return NULL; +} + +/* Divide the partial schedule of the band root node of "tree" + * by the factors in "mv". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_scale_down( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv) +{ + if (!tree || !mv) + goto error; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + tree->band = isl_schedule_band_scale_down(tree->band, mv); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_multi_val_free(mv); + return NULL; +} + +/* Reduce the partial schedule of the band root node of "tree" + * modulo the factors in "mv". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_mod( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv) +{ + if (!tree || !mv) + goto error; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + tree->band = isl_schedule_band_mod(tree->band, mv); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_multi_val_free(mv); + return NULL; +} + +/* Shift the partial schedule of the band root node of "tree" by "shift". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_shift( + __isl_take isl_schedule_tree *tree, + __isl_take isl_multi_union_pw_aff *shift) +{ + if (!tree || !shift) + goto error; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + tree->band = isl_schedule_band_shift(tree->band, shift); + if (!tree->band) + return isl_schedule_tree_free(tree); + + return tree; +error: + isl_schedule_tree_free(tree); + isl_multi_union_pw_aff_free(shift); + return NULL; +} + +/* Given two trees with sequence roots, replace the child at position + * "pos" of "tree" with the children of "child". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_sequence_splice( + __isl_take isl_schedule_tree *tree, int pos, + __isl_take isl_schedule_tree *child) +{ + int n; + isl_schedule_tree_list *list1, *list2; + + tree = isl_schedule_tree_cow(tree); + if (!tree || !child) + goto error; + if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a sequence node", goto error); + n = isl_schedule_tree_n_children(tree); + if (pos < 0 || pos >= n) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "position out of bounds", goto error); + if (isl_schedule_tree_get_type(child) != isl_schedule_node_sequence) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a sequence node", goto error); + + list1 = isl_schedule_tree_list_copy(tree->children); + list1 = isl_schedule_tree_list_drop(list1, pos, n - pos); + list2 = isl_schedule_tree_list_copy(tree->children); + list2 = isl_schedule_tree_list_drop(list2, 0, pos + 1); + list1 = isl_schedule_tree_list_concat(list1, + isl_schedule_tree_list_copy(child->children)); + list1 = isl_schedule_tree_list_concat(list1, list2); + + isl_schedule_tree_free(tree); + isl_schedule_tree_free(child); + return isl_schedule_tree_from_children(isl_schedule_node_sequence, + list1); +error: + isl_schedule_tree_free(tree); + isl_schedule_tree_free(child); + return NULL; +} + +/* Tile the band root node of "tree" with tile sizes "sizes". + * + * We duplicate the band node, change the schedule of one of them + * to the tile schedule and the other to the point schedule and then + * attach the point band as a child to the tile band. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_tile( + __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *sizes) +{ + isl_schedule_tree *child = NULL; + + if (!tree || !sizes) + goto error; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + + child = isl_schedule_tree_copy(tree); + tree = isl_schedule_tree_cow(tree); + child = isl_schedule_tree_cow(child); + if (!tree || !child) + goto error; + + tree->band = isl_schedule_band_tile(tree->band, + isl_multi_val_copy(sizes)); + if (!tree->band) + goto error; + child->band = isl_schedule_band_point(child->band, tree->band, sizes); + if (!child->band) + child = isl_schedule_tree_free(child); + + tree = isl_schedule_tree_replace_child(tree, 0, child); + + return tree; +error: + isl_schedule_tree_free(child); + isl_schedule_tree_free(tree); + isl_multi_val_free(sizes); + return NULL; +} + +/* Given an isolate AST generation option "isolate" for a band of size pos + n, + * return the corresponding option for a band covering the first "pos" + * members. + * + * The input isolate option is of the form + * + * isolate[[flattened outer bands] -> [pos; n]] + * + * The output isolate option is of the form + * + * isolate[[flattened outer bands] -> [pos]] + */ +static __isl_give isl_set *isolate_initial(__isl_keep isl_set *isolate, + int pos, int n) +{ + isl_id *id; + isl_map *map; + + isolate = isl_set_copy(isolate); + id = isl_set_get_tuple_id(isolate); + map = isl_set_unwrap(isolate); + map = isl_map_project_out(map, isl_dim_out, pos, n); + isolate = isl_map_wrap(map); + isolate = isl_set_set_tuple_id(isolate, id); + + return isolate; +} + +/* Given an isolate AST generation option "isolate" for a band of size pos + n, + * return the corresponding option for a band covering the final "n" + * members within a band covering the first "pos" members. + * + * The input isolate option is of the form + * + * isolate[[flattened outer bands] -> [pos; n]] + * + * The output isolate option is of the form + * + * isolate[[flattened outer bands; pos] -> [n]] + * + * + * The range is first split into + * + * isolate[[flattened outer bands] -> [[pos] -> [n]]] + * + * and then the first pos members are moved to the domain + * + * isolate[[[flattened outer bands] -> [pos]] -> [n]] + * + * after which the domain is flattened to obtain the desired output. + */ +static __isl_give isl_set *isolate_final(__isl_keep isl_set *isolate, + int pos, int n) +{ + isl_id *id; + isl_space *space; + isl_multi_aff *ma1, *ma2; + isl_map *map; + + isolate = isl_set_copy(isolate); + id = isl_set_get_tuple_id(isolate); + map = isl_set_unwrap(isolate); + space = isl_space_range(isl_map_get_space(map)); + ma1 = isl_multi_aff_project_out_map(isl_space_copy(space), + isl_dim_set, pos, n); + ma2 = isl_multi_aff_project_out_map(space, isl_dim_set, 0, pos); + ma1 = isl_multi_aff_range_product(ma1, ma2); + map = isl_map_apply_range(map, isl_map_from_multi_aff(ma1)); + map = isl_map_uncurry(map); + map = isl_map_flatten_domain(map); + isolate = isl_map_wrap(map); + isolate = isl_set_set_tuple_id(isolate, id); + + return isolate; +} + +/* Split the band root node of "tree" into two nested band nodes, + * one with the first "pos" dimensions and + * one with the remaining dimensions. + * The tree is itself positioned at schedule depth "depth". + * + * The loop AST generation type options and the isolate option + * are split over the the two band nodes. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_split( + __isl_take isl_schedule_tree *tree, int pos, int depth) +{ + int n; + isl_set *isolate, *tree_isolate, *child_isolate; + isl_schedule_tree *child; + + if (!tree) + return NULL; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", return isl_schedule_tree_free(tree)); + + n = isl_schedule_tree_band_n_member(tree); + if (pos < 0 || pos > n) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "position out of bounds", + return isl_schedule_tree_free(tree)); + + child = isl_schedule_tree_copy(tree); + tree = isl_schedule_tree_cow(tree); + child = isl_schedule_tree_cow(child); + if (!tree || !child) + goto error; + + isolate = isl_schedule_tree_band_get_ast_isolate_option(tree, depth); + tree_isolate = isolate_initial(isolate, pos, n - pos); + child_isolate = isolate_final(isolate, pos, n - pos); + child->band = isl_schedule_band_drop(child->band, 0, pos); + child->band = isl_schedule_band_replace_ast_build_option(child->band, + isl_set_copy(isolate), child_isolate); + tree->band = isl_schedule_band_drop(tree->band, pos, n - pos); + tree->band = isl_schedule_band_replace_ast_build_option(tree->band, + isl_set_copy(isolate), tree_isolate); + isl_set_free(isolate); + if (!child->band || !tree->band) + goto error; + + tree = isl_schedule_tree_replace_child(tree, 0, child); + + return tree; +error: + isl_schedule_tree_free(child); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Attach "tree2" at each of the leaves of "tree1". + * + * If "tree1" does not have any explicit children, then make "tree2" + * its single child. Otherwise, attach "tree2" to the leaves of + * each of the children of "tree1". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_append_to_leaves( + __isl_take isl_schedule_tree *tree1, + __isl_take isl_schedule_tree *tree2) +{ + int i, n; + + if (!tree1 || !tree2) + goto error; + n = isl_schedule_tree_n_children(tree1); + if (n == 0) { + isl_schedule_tree_list *list; + list = isl_schedule_tree_list_from_schedule_tree(tree2); + tree1 = isl_schedule_tree_set_children(tree1, list); + return tree1; + } + for (i = 0; i < n; ++i) { + isl_schedule_tree *child; + + child = isl_schedule_tree_get_child(tree1, i); + child = isl_schedule_tree_append_to_leaves(child, + isl_schedule_tree_copy(tree2)); + tree1 = isl_schedule_tree_replace_child(tree1, i, child); + } + + isl_schedule_tree_free(tree2); + return tree1; +error: + isl_schedule_tree_free(tree1); + isl_schedule_tree_free(tree2); + return NULL; +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * in the root of "tree". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_reset_user( + __isl_take isl_schedule_tree *tree) +{ + if (isl_schedule_tree_is_leaf(tree)) + return tree; + + tree = isl_schedule_tree_cow(tree); + if (!tree) + return NULL; + + switch (tree->type) { + case isl_schedule_node_error: + return isl_schedule_tree_free(tree); + case isl_schedule_node_band: + tree->band = isl_schedule_band_reset_user(tree->band); + if (!tree->band) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_context: + tree->context = isl_set_reset_user(tree->context); + if (!tree->context) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_domain: + tree->domain = isl_union_set_reset_user(tree->domain); + if (!tree->domain) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_expansion: + tree->contraction = + isl_union_pw_multi_aff_reset_user(tree->contraction); + tree->expansion = isl_union_map_reset_user(tree->expansion); + if (!tree->contraction || !tree->expansion) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_extension: + tree->extension = isl_union_map_reset_user(tree->extension); + if (!tree->extension) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_filter: + tree->filter = isl_union_set_reset_user(tree->filter); + if (!tree->filter) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_guard: + tree->guard = isl_set_reset_user(tree->guard); + if (!tree->guard) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + break; + } + + return tree; +} + +/* Align the parameters of the root of "tree" to those of "space". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_align_params( + __isl_take isl_schedule_tree *tree, __isl_take isl_space *space) +{ + if (!space) + goto error; + + if (isl_schedule_tree_is_leaf(tree)) { + isl_space_free(space); + return tree; + } + + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + switch (tree->type) { + case isl_schedule_node_error: + goto error; + case isl_schedule_node_band: + tree->band = isl_schedule_band_align_params(tree->band, space); + if (!tree->band) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_context: + tree->context = isl_set_align_params(tree->context, space); + if (!tree->context) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_domain: + tree->domain = isl_union_set_align_params(tree->domain, space); + if (!tree->domain) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_expansion: + tree->contraction = + isl_union_pw_multi_aff_align_params(tree->contraction, + isl_space_copy(space)); + tree->expansion = isl_union_map_align_params(tree->expansion, + space); + if (!tree->contraction || !tree->expansion) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_extension: + tree->extension = isl_union_map_align_params(tree->extension, + space); + if (!tree->extension) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_filter: + tree->filter = isl_union_set_align_params(tree->filter, space); + if (!tree->filter) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_guard: + tree->guard = isl_set_align_params(tree->guard, space); + if (!tree->guard) + return isl_schedule_tree_free(tree); + break; + case isl_schedule_node_leaf: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + isl_space_free(space); + break; + } + + return tree; +error: + isl_space_free(space); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Does "tree" involve the iteration domain? + * That is, does it need to be modified + * by isl_schedule_tree_pullback_union_pw_multi_aff? + */ +static int involves_iteration_domain(__isl_keep isl_schedule_tree *tree) +{ + if (!tree) + return -1; + + switch (tree->type) { + case isl_schedule_node_error: + return -1; + case isl_schedule_node_band: + case isl_schedule_node_domain: + case isl_schedule_node_expansion: + case isl_schedule_node_extension: + case isl_schedule_node_filter: + return 1; + case isl_schedule_node_context: + case isl_schedule_node_leaf: + case isl_schedule_node_guard: + case isl_schedule_node_mark: + case isl_schedule_node_sequence: + case isl_schedule_node_set: + return 0; + } + + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal, + "unhandled case", return -1); +} + +/* Compute the pullback of the root node of "tree" by the function + * represented by "upma". + * In other words, plug in "upma" in the iteration domains of + * the root node of "tree". + * We currently do not handle expansion nodes. + * + * We first check if the root node involves any iteration domains. + * If so, we handle the specific cases. + */ +__isl_give isl_schedule_tree *isl_schedule_tree_pullback_union_pw_multi_aff( + __isl_take isl_schedule_tree *tree, + __isl_take isl_union_pw_multi_aff *upma) +{ + int involves; + + if (!tree || !upma) + goto error; + + involves = involves_iteration_domain(tree); + if (involves < 0) + goto error; + if (!involves) { + isl_union_pw_multi_aff_free(upma); + return tree; + } + + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + if (tree->type == isl_schedule_node_band) { + tree->band = isl_schedule_band_pullback_union_pw_multi_aff( + tree->band, upma); + if (!tree->band) + return isl_schedule_tree_free(tree); + } else if (tree->type == isl_schedule_node_domain) { + tree->domain = + isl_union_set_preimage_union_pw_multi_aff(tree->domain, + upma); + if (!tree->domain) + return isl_schedule_tree_free(tree); + } else if (tree->type == isl_schedule_node_expansion) { + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported, + "cannot pullback expansion node", goto error); + } else if (tree->type == isl_schedule_node_extension) { + tree->extension = + isl_union_map_preimage_range_union_pw_multi_aff( + tree->extension, upma); + if (!tree->extension) + return isl_schedule_tree_free(tree); + } else if (tree->type == isl_schedule_node_filter) { + tree->filter = + isl_union_set_preimage_union_pw_multi_aff(tree->filter, + upma); + if (!tree->filter) + return isl_schedule_tree_free(tree); + } + + return tree; +error: + isl_union_pw_multi_aff_free(upma); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Compute the gist of the band tree root with respect to "context". + */ +__isl_give isl_schedule_tree *isl_schedule_tree_band_gist( + __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *context) +{ + if (!tree) + return NULL; + if (tree->type != isl_schedule_node_band) + isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid, + "not a band node", goto error); + tree = isl_schedule_tree_cow(tree); + if (!tree) + goto error; + + tree->band = isl_schedule_band_gist(tree->band, context); + if (!tree->band) + return isl_schedule_tree_free(tree); + return tree; +error: + isl_union_set_free(context); + isl_schedule_tree_free(tree); + return NULL; +} + +/* Are any members in "band" marked coincident? + */ +static int any_coincident(__isl_keep isl_schedule_band *band) +{ + int i, n; + + n = isl_schedule_band_n_member(band); + for (i = 0; i < n; ++i) + if (isl_schedule_band_member_get_coincident(band, i)) + return 1; + + return 0; +} + +/* Print the band node "band" to "p". + * + * The permutable and coincident properties are only printed if they + * are different from the defaults. + * The coincident property is always printed in YAML flow style. + */ +static __isl_give isl_printer *print_tree_band(__isl_take isl_printer *p, + __isl_keep isl_schedule_band *band) +{ + isl_union_set *options; + int empty; + + p = isl_printer_print_str(p, "schedule"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_multi_union_pw_aff(p, band->mupa); + p = isl_printer_print_str(p, "\""); + if (isl_schedule_band_get_permutable(band)) { + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "permutable"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_int(p, 1); + } + if (any_coincident(band)) { + int i, n; + int style; + + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "coincident"); + p = isl_printer_yaml_next(p); + style = isl_printer_get_yaml_style(p); + p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW); + p = isl_printer_yaml_start_sequence(p); + n = isl_schedule_band_n_member(band); + for (i = 0; i < n; ++i) { + p = isl_printer_print_int(p, + isl_schedule_band_member_get_coincident(band, i)); + p = isl_printer_yaml_next(p); + } + p = isl_printer_yaml_end_sequence(p); + p = isl_printer_set_yaml_style(p, style); + } + options = isl_schedule_band_get_ast_build_options(band); + empty = isl_union_set_is_empty(options); + if (empty < 0) + p = isl_printer_free(p); + if (!empty) { + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "options"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_set(p, options); + p = isl_printer_print_str(p, "\""); + } + isl_union_set_free(options); + + return p; +} + +/* Print "tree" to "p". + * + * If "n_ancestor" is non-negative, then "child_pos" contains the child + * positions of a descendant of the current node that should be marked + * (by the comment "YOU ARE HERE"). In particular, if "n_ancestor" + * is zero, then the current node should be marked. + * The marking is only printed in YAML block format. + * + * Implicit leaf nodes are not printed, except if they correspond + * to the node that should be marked. + */ +__isl_give isl_printer *isl_printer_print_schedule_tree_mark( + __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree, + int n_ancestor, int *child_pos) +{ + int i, n; + int sequence = 0; + int block; + + block = isl_printer_get_yaml_style(p) == ISL_YAML_STYLE_BLOCK; + + p = isl_printer_yaml_start_mapping(p); + if (n_ancestor == 0 && block) { + p = isl_printer_print_str(p, "# YOU ARE HERE"); + p = isl_printer_end_line(p); + p = isl_printer_start_line(p); + } + switch (tree->type) { + case isl_schedule_node_error: + p = isl_printer_print_str(p, "ERROR"); + break; + case isl_schedule_node_leaf: + p = isl_printer_print_str(p, "leaf"); + break; + case isl_schedule_node_sequence: + p = isl_printer_print_str(p, "sequence"); + sequence = 1; + break; + case isl_schedule_node_set: + p = isl_printer_print_str(p, "set"); + sequence = 1; + break; + case isl_schedule_node_context: + p = isl_printer_print_str(p, "context"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_set(p, tree->context); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_domain: + p = isl_printer_print_str(p, "domain"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_set(p, tree->domain); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_expansion: + p = isl_printer_print_str(p, "contraction"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_pw_multi_aff(p, tree->contraction); + p = isl_printer_print_str(p, "\""); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "expansion"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_map(p, tree->expansion); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_extension: + p = isl_printer_print_str(p, "extension"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_map(p, tree->extension); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_filter: + p = isl_printer_print_str(p, "filter"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_union_set(p, tree->filter); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_guard: + p = isl_printer_print_str(p, "guard"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_set(p, tree->guard); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_mark: + p = isl_printer_print_str(p, "mark"); + p = isl_printer_yaml_next(p); + p = isl_printer_print_str(p, "\""); + p = isl_printer_print_str(p, isl_id_get_name(tree->mark)); + p = isl_printer_print_str(p, "\""); + break; + case isl_schedule_node_band: + p = print_tree_band(p, tree->band); + break; + } + p = isl_printer_yaml_next(p); + + if (!tree->children) { + if (n_ancestor > 0 && block) { + isl_schedule_tree *leaf; + + p = isl_printer_print_str(p, "child"); + p = isl_printer_yaml_next(p); + leaf = isl_schedule_tree_leaf(isl_printer_get_ctx(p)); + p = isl_printer_print_schedule_tree_mark(p, + leaf, 0, NULL); + isl_schedule_tree_free(leaf); + p = isl_printer_yaml_next(p); + } + return isl_printer_yaml_end_mapping(p); + } + + if (sequence) { + p = isl_printer_yaml_start_sequence(p); + } else { + p = isl_printer_print_str(p, "child"); + p = isl_printer_yaml_next(p); + } + + n = isl_schedule_tree_list_n_schedule_tree(tree->children); + for (i = 0; i < n; ++i) { + isl_schedule_tree *t; + + t = isl_schedule_tree_get_child(tree, i); + if (n_ancestor > 0 && child_pos[0] == i) + p = isl_printer_print_schedule_tree_mark(p, t, + n_ancestor - 1, child_pos + 1); + else + p = isl_printer_print_schedule_tree_mark(p, t, + -1, NULL); + isl_schedule_tree_free(t); + + p = isl_printer_yaml_next(p); + } + + if (sequence) + p = isl_printer_yaml_end_sequence(p); + p = isl_printer_yaml_end_mapping(p); + + return p; +} + +/* Print "tree" to "p". + */ +__isl_give isl_printer *isl_printer_print_schedule_tree( + __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree) +{ + return isl_printer_print_schedule_tree_mark(p, tree, -1, NULL); +} + +void isl_schedule_tree_dump(__isl_keep isl_schedule_tree *tree) +{ + isl_ctx *ctx; + isl_printer *printer; + + if (!tree) + return; + + ctx = isl_schedule_tree_get_ctx(tree); + printer = isl_printer_to_file(ctx, stderr); + printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK); + printer = isl_printer_print_schedule_tree(printer, tree); + + isl_printer_free(printer); +} Index: lib/Analysis/isl/isl_scheduler.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_scheduler.c @@ -0,0 +1,6765 @@ +/* + * Copyright 2011 INRIA Saclay + * Copyright 2012-2014 Ecole Normale Superieure + * Copyright 2015-2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The scheduling algorithm implemented in this file was inspired by + * Bondhugula et al., "Automatic Transformations for Communication-Minimized + * Parallelization and Locality Optimization in the Polyhedral Model". + */ + +enum isl_edge_type { + isl_edge_validity = 0, + isl_edge_first = isl_edge_validity, + isl_edge_coincidence, + isl_edge_condition, + isl_edge_conditional_validity, + isl_edge_proximity, + isl_edge_last = isl_edge_proximity, + isl_edge_local +}; + +/* The constraints that need to be satisfied by a schedule on "domain". + * + * "context" specifies extra constraints on the parameters. + * + * "validity" constraints map domain elements i to domain elements + * that should be scheduled after i. (Hard constraint) + * "proximity" constraints map domain elements i to domains elements + * that should be scheduled as early as possible after i (or before i). + * (Soft constraint) + * + * "condition" and "conditional_validity" constraints map possibly "tagged" + * domain elements i -> s to "tagged" domain elements j -> t. + * The elements of the "conditional_validity" constraints, but without the + * tags (i.e., the elements i -> j) are treated as validity constraints, + * except that during the construction of a tilable band, + * the elements of the "conditional_validity" constraints may be violated + * provided that all adjacent elements of the "condition" constraints + * are local within the band. + * A dependence is local within a band if domain and range are mapped + * to the same schedule point by the band. + */ +struct isl_schedule_constraints { + isl_union_set *domain; + isl_set *context; + + isl_union_map *constraint[isl_edge_last + 1]; +}; + +__isl_give isl_schedule_constraints *isl_schedule_constraints_copy( + __isl_keep isl_schedule_constraints *sc) +{ + isl_ctx *ctx; + isl_schedule_constraints *sc_copy; + enum isl_edge_type i; + + ctx = isl_union_set_get_ctx(sc->domain); + sc_copy = isl_calloc_type(ctx, struct isl_schedule_constraints); + if (!sc_copy) + return NULL; + + sc_copy->domain = isl_union_set_copy(sc->domain); + sc_copy->context = isl_set_copy(sc->context); + if (!sc_copy->domain || !sc_copy->context) + return isl_schedule_constraints_free(sc_copy); + + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + sc_copy->constraint[i] = isl_union_map_copy(sc->constraint[i]); + if (!sc_copy->constraint[i]) + return isl_schedule_constraints_free(sc_copy); + } + + return sc_copy; +} + + +/* Construct an isl_schedule_constraints object for computing a schedule + * on "domain". The initial object does not impose any constraints. + */ +__isl_give isl_schedule_constraints *isl_schedule_constraints_on_domain( + __isl_take isl_union_set *domain) +{ + isl_ctx *ctx; + isl_space *space; + isl_schedule_constraints *sc; + isl_union_map *empty; + enum isl_edge_type i; + + if (!domain) + return NULL; + + ctx = isl_union_set_get_ctx(domain); + sc = isl_calloc_type(ctx, struct isl_schedule_constraints); + if (!sc) + goto error; + + space = isl_union_set_get_space(domain); + sc->domain = domain; + sc->context = isl_set_universe(isl_space_copy(space)); + empty = isl_union_map_empty(space); + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + sc->constraint[i] = isl_union_map_copy(empty); + if (!sc->constraint[i]) + sc->domain = isl_union_set_free(sc->domain); + } + isl_union_map_free(empty); + + if (!sc->domain || !sc->context) + return isl_schedule_constraints_free(sc); + + return sc; +error: + isl_union_set_free(domain); + return NULL; +} + +/* Replace the context of "sc" by "context". + */ +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_context( + __isl_take isl_schedule_constraints *sc, __isl_take isl_set *context) +{ + if (!sc || !context) + goto error; + + isl_set_free(sc->context); + sc->context = context; + + return sc; +error: + isl_schedule_constraints_free(sc); + isl_set_free(context); + return NULL; +} + +/* Replace the validity constraints of "sc" by "validity". + */ +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_validity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *validity) +{ + if (!sc || !validity) + goto error; + + isl_union_map_free(sc->constraint[isl_edge_validity]); + sc->constraint[isl_edge_validity] = validity; + + return sc; +error: + isl_schedule_constraints_free(sc); + isl_union_map_free(validity); + return NULL; +} + +/* Replace the coincidence constraints of "sc" by "coincidence". + */ +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_coincidence( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *coincidence) +{ + if (!sc || !coincidence) + goto error; + + isl_union_map_free(sc->constraint[isl_edge_coincidence]); + sc->constraint[isl_edge_coincidence] = coincidence; + + return sc; +error: + isl_schedule_constraints_free(sc); + isl_union_map_free(coincidence); + return NULL; +} + +/* Replace the proximity constraints of "sc" by "proximity". + */ +__isl_give isl_schedule_constraints *isl_schedule_constraints_set_proximity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *proximity) +{ + if (!sc || !proximity) + goto error; + + isl_union_map_free(sc->constraint[isl_edge_proximity]); + sc->constraint[isl_edge_proximity] = proximity; + + return sc; +error: + isl_schedule_constraints_free(sc); + isl_union_map_free(proximity); + return NULL; +} + +/* Replace the conditional validity constraints of "sc" by "condition" + * and "validity". + */ +__isl_give isl_schedule_constraints * +isl_schedule_constraints_set_conditional_validity( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *condition, + __isl_take isl_union_map *validity) +{ + if (!sc || !condition || !validity) + goto error; + + isl_union_map_free(sc->constraint[isl_edge_condition]); + sc->constraint[isl_edge_condition] = condition; + isl_union_map_free(sc->constraint[isl_edge_conditional_validity]); + sc->constraint[isl_edge_conditional_validity] = validity; + + return sc; +error: + isl_schedule_constraints_free(sc); + isl_union_map_free(condition); + isl_union_map_free(validity); + return NULL; +} + +__isl_null isl_schedule_constraints *isl_schedule_constraints_free( + __isl_take isl_schedule_constraints *sc) +{ + enum isl_edge_type i; + + if (!sc) + return NULL; + + isl_union_set_free(sc->domain); + isl_set_free(sc->context); + for (i = isl_edge_first; i <= isl_edge_last; ++i) + isl_union_map_free(sc->constraint[i]); + + free(sc); + + return NULL; +} + +isl_ctx *isl_schedule_constraints_get_ctx( + __isl_keep isl_schedule_constraints *sc) +{ + return sc ? isl_union_set_get_ctx(sc->domain) : NULL; +} + +/* Return the domain of "sc". + */ +__isl_give isl_union_set *isl_schedule_constraints_get_domain( + __isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return NULL; + + return isl_union_set_copy(sc->domain); +} + +/* Return the validity constraints of "sc". + */ +__isl_give isl_union_map *isl_schedule_constraints_get_validity( + __isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return NULL; + + return isl_union_map_copy(sc->constraint[isl_edge_validity]); +} + +/* Return the coincidence constraints of "sc". + */ +__isl_give isl_union_map *isl_schedule_constraints_get_coincidence( + __isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return NULL; + + return isl_union_map_copy(sc->constraint[isl_edge_coincidence]); +} + +/* Return the proximity constraints of "sc". + */ +__isl_give isl_union_map *isl_schedule_constraints_get_proximity( + __isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return NULL; + + return isl_union_map_copy(sc->constraint[isl_edge_proximity]); +} + +/* Return the conditional validity constraints of "sc". + */ +__isl_give isl_union_map *isl_schedule_constraints_get_conditional_validity( + __isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return NULL; + + return + isl_union_map_copy(sc->constraint[isl_edge_conditional_validity]); +} + +/* Return the conditions for the conditional validity constraints of "sc". + */ +__isl_give isl_union_map * +isl_schedule_constraints_get_conditional_validity_condition( + __isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return NULL; + + return isl_union_map_copy(sc->constraint[isl_edge_condition]); +} + +/* Can a schedule constraint of type "type" be tagged? + */ +static int may_be_tagged(enum isl_edge_type type) +{ + if (type == isl_edge_condition || type == isl_edge_conditional_validity) + return 1; + return 0; +} + +/* Apply "umap" to the domains of the wrapped relations + * inside the domain and range of "c". + * + * That is, for each map of the form + * + * [D -> S] -> [E -> T] + * + * in "c", apply "umap" to D and E. + * + * D is exposed by currying the relation to + * + * D -> [S -> [E -> T]] + * + * E is exposed by doing the same to the inverse of "c". + */ +static __isl_give isl_union_map *apply_factor_domain( + __isl_take isl_union_map *c, __isl_keep isl_union_map *umap) +{ + c = isl_union_map_curry(c); + c = isl_union_map_apply_domain(c, isl_union_map_copy(umap)); + c = isl_union_map_uncurry(c); + + c = isl_union_map_reverse(c); + c = isl_union_map_curry(c); + c = isl_union_map_apply_domain(c, isl_union_map_copy(umap)); + c = isl_union_map_uncurry(c); + c = isl_union_map_reverse(c); + + return c; +} + +/* Apply "umap" to domain and range of "c". + * If "tag" is set, then "c" may contain tags and then "umap" + * needs to be applied to the domains of the wrapped relations + * inside the domain and range of "c". + */ +static __isl_give isl_union_map *apply(__isl_take isl_union_map *c, + __isl_keep isl_union_map *umap, int tag) +{ + isl_union_map *t; + + if (tag) + t = isl_union_map_copy(c); + c = isl_union_map_apply_domain(c, isl_union_map_copy(umap)); + c = isl_union_map_apply_range(c, isl_union_map_copy(umap)); + if (!tag) + return c; + t = apply_factor_domain(t, umap); + c = isl_union_map_union(c, t); + return c; +} + +/* Apply "umap" to the domain of the schedule constraints "sc". + * + * The two sides of the various schedule constraints are adjusted + * accordingly. + */ +__isl_give isl_schedule_constraints *isl_schedule_constraints_apply( + __isl_take isl_schedule_constraints *sc, + __isl_take isl_union_map *umap) +{ + enum isl_edge_type i; + + if (!sc || !umap) + goto error; + + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + int tag = may_be_tagged(i); + + sc->constraint[i] = apply(sc->constraint[i], umap, tag); + if (!sc->constraint[i]) + goto error; + } + sc->domain = isl_union_set_apply(sc->domain, umap); + if (!sc->domain) + return isl_schedule_constraints_free(sc); + + return sc; +error: + isl_schedule_constraints_free(sc); + isl_union_map_free(umap); + return NULL; +} + +void isl_schedule_constraints_dump(__isl_keep isl_schedule_constraints *sc) +{ + if (!sc) + return; + + fprintf(stderr, "domain: "); + isl_union_set_dump(sc->domain); + fprintf(stderr, "context: "); + isl_set_dump(sc->context); + fprintf(stderr, "validity: "); + isl_union_map_dump(sc->constraint[isl_edge_validity]); + fprintf(stderr, "proximity: "); + isl_union_map_dump(sc->constraint[isl_edge_proximity]); + fprintf(stderr, "coincidence: "); + isl_union_map_dump(sc->constraint[isl_edge_coincidence]); + fprintf(stderr, "condition: "); + isl_union_map_dump(sc->constraint[isl_edge_condition]); + fprintf(stderr, "conditional_validity: "); + isl_union_map_dump(sc->constraint[isl_edge_conditional_validity]); +} + +/* Align the parameters of the fields of "sc". + */ +static __isl_give isl_schedule_constraints * +isl_schedule_constraints_align_params(__isl_take isl_schedule_constraints *sc) +{ + isl_space *space; + enum isl_edge_type i; + + if (!sc) + return NULL; + + space = isl_union_set_get_space(sc->domain); + space = isl_space_align_params(space, isl_set_get_space(sc->context)); + for (i = isl_edge_first; i <= isl_edge_last; ++i) + space = isl_space_align_params(space, + isl_union_map_get_space(sc->constraint[i])); + + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + sc->constraint[i] = isl_union_map_align_params( + sc->constraint[i], isl_space_copy(space)); + if (!sc->constraint[i]) + space = isl_space_free(space); + } + sc->context = isl_set_align_params(sc->context, isl_space_copy(space)); + sc->domain = isl_union_set_align_params(sc->domain, space); + if (!sc->context || !sc->domain) + return isl_schedule_constraints_free(sc); + + return sc; +} + +/* Return the total number of isl_maps in the constraints of "sc". + */ +static __isl_give int isl_schedule_constraints_n_map( + __isl_keep isl_schedule_constraints *sc) +{ + enum isl_edge_type i; + int n = 0; + + for (i = isl_edge_first; i <= isl_edge_last; ++i) + n += isl_union_map_n_map(sc->constraint[i]); + + return n; +} + +/* Internal information about a node that is used during the construction + * of a schedule. + * space represents the space in which the domain lives + * sched is a matrix representation of the schedule being constructed + * for this node; if compressed is set, then this schedule is + * defined over the compressed domain space + * sched_map is an isl_map representation of the same (partial) schedule + * sched_map may be NULL; if compressed is set, then this map + * is defined over the uncompressed domain space + * rank is the number of linearly independent rows in the linear part + * of sched + * the columns of cmap represent a change of basis for the schedule + * coefficients; the first rank columns span the linear part of + * the schedule rows + * cinv is the inverse of cmap. + * ctrans is the transpose of cmap. + * start is the first variable in the LP problem in the sequences that + * represents the schedule coefficients of this node + * nvar is the dimension of the domain + * nparam is the number of parameters or 0 if we are not constructing + * a parametric schedule + * + * If compressed is set, then hull represents the constraints + * that were used to derive the compression, while compress and + * decompress map the original space to the compressed space and + * vice versa. + * + * scc is the index of SCC (or WCC) this node belongs to + * + * "cluster" is only used inside extract_clusters and identifies + * the cluster of SCCs that the node belongs to. + * + * coincident contains a boolean for each of the rows of the schedule, + * indicating whether the corresponding scheduling dimension satisfies + * the coincidence constraints in the sense that the corresponding + * dependence distances are zero. + * + * If the schedule_treat_coalescing option is set, then + * "sizes" contains the sizes of the (compressed) instance set + * in each direction. If there is no fixed size in a given direction, + * then the corresponding size value is set to infinity. + * If the schedule_treat_coalescing option or the schedule_max_coefficient + * option is set, then "max" contains the maximal values for + * schedule coefficients of the (compressed) variables. If no bound + * needs to be imposed on a particular variable, then the corresponding + * value is negative. + */ +struct isl_sched_node { + isl_space *space; + int compressed; + isl_set *hull; + isl_multi_aff *compress; + isl_multi_aff *decompress; + isl_mat *sched; + isl_map *sched_map; + int rank; + isl_mat *cmap; + isl_mat *cinv; + isl_mat *ctrans; + int start; + int nvar; + int nparam; + + int scc; + int cluster; + + int *coincident; + + isl_multi_val *sizes; + isl_vec *max; +}; + +static int node_has_space(const void *entry, const void *val) +{ + struct isl_sched_node *node = (struct isl_sched_node *)entry; + isl_space *dim = (isl_space *)val; + + return isl_space_is_equal(node->space, dim); +} + +static int node_scc_exactly(struct isl_sched_node *node, int scc) +{ + return node->scc == scc; +} + +static int node_scc_at_most(struct isl_sched_node *node, int scc) +{ + return node->scc <= scc; +} + +static int node_scc_at_least(struct isl_sched_node *node, int scc) +{ + return node->scc >= scc; +} + +/* An edge in the dependence graph. An edge may be used to + * ensure validity of the generated schedule, to minimize the dependence + * distance or both + * + * map is the dependence relation, with i -> j in the map if j depends on i + * tagged_condition and tagged_validity contain the union of all tagged + * condition or conditional validity dependence relations that + * specialize the dependence relation "map"; that is, + * if (i -> a) -> (j -> b) is an element of "tagged_condition" + * or "tagged_validity", then i -> j is an element of "map". + * If these fields are NULL, then they represent the empty relation. + * src is the source node + * dst is the sink node + * + * types is a bit vector containing the types of this edge. + * validity is set if the edge is used to ensure correctness + * coincidence is used to enforce zero dependence distances + * proximity is set if the edge is used to minimize dependence distances + * condition is set if the edge represents a condition + * for a conditional validity schedule constraint + * local can only be set for condition edges and indicates that + * the dependence distance over the edge should be zero + * conditional_validity is set if the edge is used to conditionally + * ensure correctness + * + * For validity edges, start and end mark the sequence of inequality + * constraints in the LP problem that encode the validity constraint + * corresponding to this edge. + * + * During clustering, an edge may be marked "no_merge" if it should + * not be used to merge clusters. + * The weight is also only used during clustering and it is + * an indication of how many schedule dimensions on either side + * of the schedule constraints can be aligned. + * If the weight is negative, then this means that this edge was postponed + * by has_bounded_distances or any_no_merge. The original weight can + * be retrieved by adding 1 + graph->max_weight, with "graph" + * the graph containing this edge. + */ +struct isl_sched_edge { + isl_map *map; + isl_union_map *tagged_condition; + isl_union_map *tagged_validity; + + struct isl_sched_node *src; + struct isl_sched_node *dst; + + unsigned types; + + int start; + int end; + + int no_merge; + int weight; +}; + +/* Is "edge" marked as being of type "type"? + */ +static int is_type(struct isl_sched_edge *edge, enum isl_edge_type type) +{ + return ISL_FL_ISSET(edge->types, 1 << type); +} + +/* Mark "edge" as being of type "type". + */ +static void set_type(struct isl_sched_edge *edge, enum isl_edge_type type) +{ + ISL_FL_SET(edge->types, 1 << type); +} + +/* No longer mark "edge" as being of type "type"? + */ +static void clear_type(struct isl_sched_edge *edge, enum isl_edge_type type) +{ + ISL_FL_CLR(edge->types, 1 << type); +} + +/* Is "edge" marked as a validity edge? + */ +static int is_validity(struct isl_sched_edge *edge) +{ + return is_type(edge, isl_edge_validity); +} + +/* Mark "edge" as a validity edge. + */ +static void set_validity(struct isl_sched_edge *edge) +{ + set_type(edge, isl_edge_validity); +} + +/* Is "edge" marked as a proximity edge? + */ +static int is_proximity(struct isl_sched_edge *edge) +{ + return is_type(edge, isl_edge_proximity); +} + +/* Is "edge" marked as a local edge? + */ +static int is_local(struct isl_sched_edge *edge) +{ + return is_type(edge, isl_edge_local); +} + +/* Mark "edge" as a local edge. + */ +static void set_local(struct isl_sched_edge *edge) +{ + set_type(edge, isl_edge_local); +} + +/* No longer mark "edge" as a local edge. + */ +static void clear_local(struct isl_sched_edge *edge) +{ + clear_type(edge, isl_edge_local); +} + +/* Is "edge" marked as a coincidence edge? + */ +static int is_coincidence(struct isl_sched_edge *edge) +{ + return is_type(edge, isl_edge_coincidence); +} + +/* Is "edge" marked as a condition edge? + */ +static int is_condition(struct isl_sched_edge *edge) +{ + return is_type(edge, isl_edge_condition); +} + +/* Is "edge" marked as a conditional validity edge? + */ +static int is_conditional_validity(struct isl_sched_edge *edge) +{ + return is_type(edge, isl_edge_conditional_validity); +} + +/* Internal information about the dependence graph used during + * the construction of the schedule. + * + * intra_hmap is a cache, mapping dependence relations to their dual, + * for dependences from a node to itself + * inter_hmap is a cache, mapping dependence relations to their dual, + * for dependences between distinct nodes + * if compression is involved then the key for these maps + * is the original, uncompressed dependence relation, while + * the value is the dual of the compressed dependence relation. + * + * n is the number of nodes + * node is the list of nodes + * maxvar is the maximal number of variables over all nodes + * max_row is the allocated number of rows in the schedule + * n_row is the current (maximal) number of linearly independent + * rows in the node schedules + * n_total_row is the current number of rows in the node schedules + * band_start is the starting row in the node schedules of the current band + * root is set if this graph is the original dependence graph, + * without any splitting + * + * sorted contains a list of node indices sorted according to the + * SCC to which a node belongs + * + * n_edge is the number of edges + * edge is the list of edges + * max_edge contains the maximal number of edges of each type; + * in particular, it contains the number of edges in the inital graph. + * edge_table contains pointers into the edge array, hashed on the source + * and sink spaces; there is one such table for each type; + * a given edge may be referenced from more than one table + * if the corresponding relation appears in more than one of the + * sets of dependences; however, for each type there is only + * a single edge between a given pair of source and sink space + * in the entire graph + * + * node_table contains pointers into the node array, hashed on the space + * + * region contains a list of variable sequences that should be non-trivial + * + * lp contains the (I)LP problem used to obtain new schedule rows + * + * src_scc and dst_scc are the source and sink SCCs of an edge with + * conflicting constraints + * + * scc represents the number of components + * weak is set if the components are weakly connected + * + * max_weight is used during clustering and represents the maximal + * weight of the relevant proximity edges. + */ +struct isl_sched_graph { + isl_map_to_basic_set *intra_hmap; + isl_map_to_basic_set *inter_hmap; + + struct isl_sched_node *node; + int n; + int maxvar; + int max_row; + int n_row; + + int *sorted; + + int n_total_row; + int band_start; + + int root; + + struct isl_sched_edge *edge; + int n_edge; + int max_edge[isl_edge_last + 1]; + struct isl_hash_table *edge_table[isl_edge_last + 1]; + + struct isl_hash_table *node_table; + struct isl_region *region; + + isl_basic_set *lp; + + int src_scc; + int dst_scc; + + int scc; + int weak; + + int max_weight; +}; + +/* Initialize node_table based on the list of nodes. + */ +static int graph_init_table(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + int i; + + graph->node_table = isl_hash_table_alloc(ctx, graph->n); + if (!graph->node_table) + return -1; + + for (i = 0; i < graph->n; ++i) { + struct isl_hash_table_entry *entry; + uint32_t hash; + + hash = isl_space_get_hash(graph->node[i].space); + entry = isl_hash_table_find(ctx, graph->node_table, hash, + &node_has_space, + graph->node[i].space, 1); + if (!entry) + return -1; + entry->data = &graph->node[i]; + } + + return 0; +} + +/* Return a pointer to the node that lives within the given space, + * or NULL if there is no such node. + */ +static struct isl_sched_node *graph_find_node(isl_ctx *ctx, + struct isl_sched_graph *graph, __isl_keep isl_space *dim) +{ + struct isl_hash_table_entry *entry; + uint32_t hash; + + hash = isl_space_get_hash(dim); + entry = isl_hash_table_find(ctx, graph->node_table, hash, + &node_has_space, dim, 0); + + return entry ? entry->data : NULL; +} + +static int edge_has_src_and_dst(const void *entry, const void *val) +{ + const struct isl_sched_edge *edge = entry; + const struct isl_sched_edge *temp = val; + + return edge->src == temp->src && edge->dst == temp->dst; +} + +/* Add the given edge to graph->edge_table[type]. + */ +static isl_stat graph_edge_table_add(isl_ctx *ctx, + struct isl_sched_graph *graph, enum isl_edge_type type, + struct isl_sched_edge *edge) +{ + struct isl_hash_table_entry *entry; + uint32_t hash; + + hash = isl_hash_init(); + hash = isl_hash_builtin(hash, edge->src); + hash = isl_hash_builtin(hash, edge->dst); + entry = isl_hash_table_find(ctx, graph->edge_table[type], hash, + &edge_has_src_and_dst, edge, 1); + if (!entry) + return isl_stat_error; + entry->data = edge; + + return isl_stat_ok; +} + +/* Allocate the edge_tables based on the maximal number of edges of + * each type. + */ +static int graph_init_edge_tables(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + int i; + + for (i = 0; i <= isl_edge_last; ++i) { + graph->edge_table[i] = isl_hash_table_alloc(ctx, + graph->max_edge[i]); + if (!graph->edge_table[i]) + return -1; + } + + return 0; +} + +/* If graph->edge_table[type] contains an edge from the given source + * to the given destination, then return the hash table entry of this edge. + * Otherwise, return NULL. + */ +static struct isl_hash_table_entry *graph_find_edge_entry( + struct isl_sched_graph *graph, + enum isl_edge_type type, + struct isl_sched_node *src, struct isl_sched_node *dst) +{ + isl_ctx *ctx = isl_space_get_ctx(src->space); + uint32_t hash; + struct isl_sched_edge temp = { .src = src, .dst = dst }; + + hash = isl_hash_init(); + hash = isl_hash_builtin(hash, temp.src); + hash = isl_hash_builtin(hash, temp.dst); + return isl_hash_table_find(ctx, graph->edge_table[type], hash, + &edge_has_src_and_dst, &temp, 0); +} + + +/* If graph->edge_table[type] contains an edge from the given source + * to the given destination, then return this edge. + * Otherwise, return NULL. + */ +static struct isl_sched_edge *graph_find_edge(struct isl_sched_graph *graph, + enum isl_edge_type type, + struct isl_sched_node *src, struct isl_sched_node *dst) +{ + struct isl_hash_table_entry *entry; + + entry = graph_find_edge_entry(graph, type, src, dst); + if (!entry) + return NULL; + + return entry->data; +} + +/* Check whether the dependence graph has an edge of the given type + * between the given two nodes. + */ +static isl_bool graph_has_edge(struct isl_sched_graph *graph, + enum isl_edge_type type, + struct isl_sched_node *src, struct isl_sched_node *dst) +{ + struct isl_sched_edge *edge; + isl_bool empty; + + edge = graph_find_edge(graph, type, src, dst); + if (!edge) + return 0; + + empty = isl_map_plain_is_empty(edge->map); + if (empty < 0) + return isl_bool_error; + + return !empty; +} + +/* Look for any edge with the same src, dst and map fields as "model". + * + * Return the matching edge if one can be found. + * Return "model" if no matching edge is found. + * Return NULL on error. + */ +static struct isl_sched_edge *graph_find_matching_edge( + struct isl_sched_graph *graph, struct isl_sched_edge *model) +{ + enum isl_edge_type i; + struct isl_sched_edge *edge; + + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + int is_equal; + + edge = graph_find_edge(graph, i, model->src, model->dst); + if (!edge) + continue; + is_equal = isl_map_plain_is_equal(model->map, edge->map); + if (is_equal < 0) + return NULL; + if (is_equal) + return edge; + } + + return model; +} + +/* Remove the given edge from all the edge_tables that refer to it. + */ +static void graph_remove_edge(struct isl_sched_graph *graph, + struct isl_sched_edge *edge) +{ + isl_ctx *ctx = isl_map_get_ctx(edge->map); + enum isl_edge_type i; + + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + struct isl_hash_table_entry *entry; + + entry = graph_find_edge_entry(graph, i, edge->src, edge->dst); + if (!entry) + continue; + if (entry->data != edge) + continue; + isl_hash_table_remove(ctx, graph->edge_table[i], entry); + } +} + +/* Check whether the dependence graph has any edge + * between the given two nodes. + */ +static isl_bool graph_has_any_edge(struct isl_sched_graph *graph, + struct isl_sched_node *src, struct isl_sched_node *dst) +{ + enum isl_edge_type i; + isl_bool r; + + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + r = graph_has_edge(graph, i, src, dst); + if (r < 0 || r) + return r; + } + + return r; +} + +/* Check whether the dependence graph has a validity edge + * between the given two nodes. + * + * Conditional validity edges are essentially validity edges that + * can be ignored if the corresponding condition edges are iteration private. + * Here, we are only checking for the presence of validity + * edges, so we need to consider the conditional validity edges too. + * In particular, this function is used during the detection + * of strongly connected components and we cannot ignore + * conditional validity edges during this detection. + */ +static isl_bool graph_has_validity_edge(struct isl_sched_graph *graph, + struct isl_sched_node *src, struct isl_sched_node *dst) +{ + isl_bool r; + + r = graph_has_edge(graph, isl_edge_validity, src, dst); + if (r < 0 || r) + return r; + + return graph_has_edge(graph, isl_edge_conditional_validity, src, dst); +} + +static int graph_alloc(isl_ctx *ctx, struct isl_sched_graph *graph, + int n_node, int n_edge) +{ + int i; + + graph->n = n_node; + graph->n_edge = n_edge; + graph->node = isl_calloc_array(ctx, struct isl_sched_node, graph->n); + graph->sorted = isl_calloc_array(ctx, int, graph->n); + graph->region = isl_alloc_array(ctx, struct isl_region, graph->n); + graph->edge = isl_calloc_array(ctx, + struct isl_sched_edge, graph->n_edge); + + graph->intra_hmap = isl_map_to_basic_set_alloc(ctx, 2 * n_edge); + graph->inter_hmap = isl_map_to_basic_set_alloc(ctx, 2 * n_edge); + + if (!graph->node || !graph->region || (graph->n_edge && !graph->edge) || + !graph->sorted) + return -1; + + for(i = 0; i < graph->n; ++i) + graph->sorted[i] = i; + + return 0; +} + +static void graph_free(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + int i; + + isl_map_to_basic_set_free(graph->intra_hmap); + isl_map_to_basic_set_free(graph->inter_hmap); + + if (graph->node) + for (i = 0; i < graph->n; ++i) { + isl_space_free(graph->node[i].space); + isl_set_free(graph->node[i].hull); + isl_multi_aff_free(graph->node[i].compress); + isl_multi_aff_free(graph->node[i].decompress); + isl_mat_free(graph->node[i].sched); + isl_map_free(graph->node[i].sched_map); + isl_mat_free(graph->node[i].cmap); + isl_mat_free(graph->node[i].cinv); + isl_mat_free(graph->node[i].ctrans); + if (graph->root) + free(graph->node[i].coincident); + isl_multi_val_free(graph->node[i].sizes); + isl_vec_free(graph->node[i].max); + } + free(graph->node); + free(graph->sorted); + if (graph->edge) + for (i = 0; i < graph->n_edge; ++i) { + isl_map_free(graph->edge[i].map); + isl_union_map_free(graph->edge[i].tagged_condition); + isl_union_map_free(graph->edge[i].tagged_validity); + } + free(graph->edge); + free(graph->region); + for (i = 0; i <= isl_edge_last; ++i) + isl_hash_table_free(ctx, graph->edge_table[i]); + isl_hash_table_free(ctx, graph->node_table); + isl_basic_set_free(graph->lp); +} + +/* For each "set" on which this function is called, increment + * graph->n by one and update graph->maxvar. + */ +static isl_stat init_n_maxvar(__isl_take isl_set *set, void *user) +{ + struct isl_sched_graph *graph = user; + int nvar = isl_set_dim(set, isl_dim_set); + + graph->n++; + if (nvar > graph->maxvar) + graph->maxvar = nvar; + + isl_set_free(set); + + return isl_stat_ok; +} + +/* Add the number of basic maps in "map" to *n. + */ +static isl_stat add_n_basic_map(__isl_take isl_map *map, void *user) +{ + int *n = user; + + *n += isl_map_n_basic_map(map); + isl_map_free(map); + + return isl_stat_ok; +} + +/* Compute the number of rows that should be allocated for the schedule. + * In particular, we need one row for each variable or one row + * for each basic map in the dependences. + * Note that it is practically impossible to exhaust both + * the number of dependences and the number of variables. + */ +static int compute_max_row(struct isl_sched_graph *graph, + __isl_keep isl_schedule_constraints *sc) +{ + enum isl_edge_type i; + int n_edge; + + graph->n = 0; + graph->maxvar = 0; + if (isl_union_set_foreach_set(sc->domain, &init_n_maxvar, graph) < 0) + return -1; + n_edge = 0; + for (i = isl_edge_first; i <= isl_edge_last; ++i) + if (isl_union_map_foreach_map(sc->constraint[i], + &add_n_basic_map, &n_edge) < 0) + return -1; + graph->max_row = n_edge + graph->maxvar; + + return 0; +} + +/* Does "bset" have any defining equalities for its set variables? + */ +static int has_any_defining_equality(__isl_keep isl_basic_set *bset) +{ + int i, n; + + if (!bset) + return -1; + + n = isl_basic_set_dim(bset, isl_dim_set); + for (i = 0; i < n; ++i) { + int has; + + has = isl_basic_set_has_defining_equality(bset, isl_dim_set, i, + NULL); + if (has < 0 || has) + return has; + } + + return 0; +} + +/* Set the entries of node->max to the value of the schedule_max_coefficient + * option, if set. + */ +static isl_stat set_max_coefficient(isl_ctx *ctx, struct isl_sched_node *node) +{ + int max; + + max = isl_options_get_schedule_max_coefficient(ctx); + if (max == -1) + return isl_stat_ok; + + node->max = isl_vec_alloc(ctx, node->nvar); + node->max = isl_vec_set_si(node->max, max); + if (!node->max) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Set the entries of node->max to the minimum of the schedule_max_coefficient + * option (if set) and half of the minimum of the sizes in the other + * dimensions. If the minimum of the sizes is one, half of the size + * is zero and this value is reset to one. + * If the global minimum is unbounded (i.e., if both + * the schedule_max_coefficient is not set and the sizes in the other + * dimensions are unbounded), then store a negative value. + * If the schedule coefficient is close to the size of the instance set + * in another dimension, then the schedule may represent a loop + * coalescing transformation (especially if the coefficient + * in that other dimension is one). Forcing the coefficient to be + * smaller than or equal to half the minimal size should avoid this + * situation. + */ +static isl_stat compute_max_coefficient(isl_ctx *ctx, + struct isl_sched_node *node) +{ + int max; + int i, j; + isl_vec *v; + + max = isl_options_get_schedule_max_coefficient(ctx); + v = isl_vec_alloc(ctx, node->nvar); + if (!v) + return isl_stat_error; + + for (i = 0; i < node->nvar; ++i) { + isl_int_set_si(v->el[i], max); + isl_int_mul_si(v->el[i], v->el[i], 2); + } + + for (i = 0; i < node->nvar; ++i) { + isl_val *size; + + size = isl_multi_val_get_val(node->sizes, i); + if (!size) + goto error; + if (!isl_val_is_int(size)) { + isl_val_free(size); + continue; + } + for (j = 0; j < node->nvar; ++j) { + if (j == i) + continue; + if (isl_int_is_neg(v->el[j]) || + isl_int_gt(v->el[j], size->n)) + isl_int_set(v->el[j], size->n); + } + isl_val_free(size); + } + + for (i = 0; i < node->nvar; ++i) { + isl_int_fdiv_q_ui(v->el[i], v->el[i], 2); + if (isl_int_is_zero(v->el[i])) + isl_int_set_si(v->el[i], 1); + } + + node->max = v; + return isl_stat_ok; +error: + isl_vec_free(v); + return isl_stat_error; +} + +/* Compute and return the size of "set" in dimension "dim". + * The size is taken to be the difference in values for that variable + * for fixed values of the other variables. + * In particular, the variable is first isolated from the other variables + * in the range of a map + * + * [i_0, ..., i_dim-1, i_dim+1, ...] -> [i_dim] + * + * and then duplicated + * + * [i_0, ..., i_dim-1, i_dim+1, ...] -> [[i_dim] -> [i_dim']] + * + * The shared variables are then projected out and the maximal value + * of i_dim' - i_dim is computed. + */ +static __isl_give isl_val *compute_size(__isl_take isl_set *set, int dim) +{ + isl_map *map; + isl_local_space *ls; + isl_aff *obj; + isl_val *v; + + map = isl_set_project_onto_map(set, isl_dim_set, dim, 1); + map = isl_map_project_out(map, isl_dim_in, dim, 1); + map = isl_map_range_product(map, isl_map_copy(map)); + map = isl_set_unwrap(isl_map_range(map)); + set = isl_map_deltas(map); + ls = isl_local_space_from_space(isl_set_get_space(set)); + obj = isl_aff_var_on_domain(ls, isl_dim_set, 0); + v = isl_set_max_val(set, obj); + isl_aff_free(obj); + isl_set_free(set); + + return v; +} + +/* Compute the size of the instance set "set" of "node", after compression, + * as well as bounds on the corresponding coefficients, if needed. + * + * The sizes are needed when the schedule_treat_coalescing option is set. + * The bounds are needed when the schedule_treat_coalescing option or + * the schedule_max_coefficient option is set. + * + * If the schedule_treat_coalescing option is not set, then at most + * the bounds need to be set and this is done in set_max_coefficient. + * Otherwise, compress the domain if needed, compute the size + * in each direction and store the results in node->size. + * Finally, set the bounds on the coefficients based on the sizes + * and the schedule_max_coefficient option in compute_max_coefficient. + */ +static isl_stat compute_sizes_and_max(isl_ctx *ctx, struct isl_sched_node *node, + __isl_take isl_set *set) +{ + int j, n; + isl_multi_val *mv; + + if (!isl_options_get_schedule_treat_coalescing(ctx)) { + isl_set_free(set); + return set_max_coefficient(ctx, node); + } + + if (node->compressed) + set = isl_set_preimage_multi_aff(set, + isl_multi_aff_copy(node->decompress)); + mv = isl_multi_val_zero(isl_set_get_space(set)); + n = isl_set_dim(set, isl_dim_set); + for (j = 0; j < n; ++j) { + isl_val *v; + + v = compute_size(isl_set_copy(set), j); + mv = isl_multi_val_set_val(mv, j, v); + } + node->sizes = mv; + isl_set_free(set); + if (!node->sizes) + return isl_stat_error; + return compute_max_coefficient(ctx, node); +} + +/* Add a new node to the graph representing the given instance set. + * "nvar" is the (possibly compressed) number of variables and + * may be smaller than then number of set variables in "set" + * if "compressed" is set. + * If "compressed" is set, then "hull" represents the constraints + * that were used to derive the compression, while "compress" and + * "decompress" map the original space to the compressed space and + * vice versa. + * If "compressed" is not set, then "hull", "compress" and "decompress" + * should be NULL. + * + * Compute the size of the instance set and bounds on the coefficients, + * if needed. + */ +static isl_stat add_node(struct isl_sched_graph *graph, + __isl_take isl_set *set, int nvar, int compressed, + __isl_take isl_set *hull, __isl_take isl_multi_aff *compress, + __isl_take isl_multi_aff *decompress) +{ + int nparam; + isl_ctx *ctx; + isl_mat *sched; + isl_space *space; + int *coincident; + struct isl_sched_node *node; + + if (!set) + return isl_stat_error; + + ctx = isl_set_get_ctx(set); + nparam = isl_set_dim(set, isl_dim_param); + if (!ctx->opt->schedule_parametric) + nparam = 0; + sched = isl_mat_alloc(ctx, 0, 1 + nparam + nvar); + node = &graph->node[graph->n]; + graph->n++; + space = isl_set_get_space(set); + node->space = space; + node->nvar = nvar; + node->nparam = nparam; + node->sched = sched; + node->sched_map = NULL; + coincident = isl_calloc_array(ctx, int, graph->max_row); + node->coincident = coincident; + node->compressed = compressed; + node->hull = hull; + node->compress = compress; + node->decompress = decompress; + if (compute_sizes_and_max(ctx, node, set) < 0) + return isl_stat_error; + + if (!space || !sched || (graph->max_row && !coincident)) + return isl_stat_error; + if (compressed && (!hull || !compress || !decompress)) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Add a new node to the graph representing the given set. + * + * If any of the set variables is defined by an equality, then + * we perform variable compression such that we can perform + * the scheduling on the compressed domain. + */ +static isl_stat extract_node(__isl_take isl_set *set, void *user) +{ + int nvar; + int has_equality; + isl_basic_set *hull; + isl_set *hull_set; + isl_morph *morph; + isl_multi_aff *compress, *decompress; + struct isl_sched_graph *graph = user; + + hull = isl_set_affine_hull(isl_set_copy(set)); + hull = isl_basic_set_remove_divs(hull); + nvar = isl_set_dim(set, isl_dim_set); + has_equality = has_any_defining_equality(hull); + + if (has_equality < 0) + goto error; + if (!has_equality) { + isl_basic_set_free(hull); + return add_node(graph, set, nvar, 0, NULL, NULL, NULL); + } + + morph = isl_basic_set_variable_compression(hull, isl_dim_set); + nvar = isl_morph_ran_dim(morph, isl_dim_set); + compress = isl_morph_get_var_multi_aff(morph); + morph = isl_morph_inverse(morph); + decompress = isl_morph_get_var_multi_aff(morph); + isl_morph_free(morph); + + hull_set = isl_set_from_basic_set(hull); + return add_node(graph, set, nvar, 1, hull_set, compress, decompress); +error: + isl_basic_set_free(hull); + isl_set_free(set); + return isl_stat_error; +} + +struct isl_extract_edge_data { + enum isl_edge_type type; + struct isl_sched_graph *graph; +}; + +/* Merge edge2 into edge1, freeing the contents of edge2. + * Return 0 on success and -1 on failure. + * + * edge1 and edge2 are assumed to have the same value for the map field. + */ +static int merge_edge(struct isl_sched_edge *edge1, + struct isl_sched_edge *edge2) +{ + edge1->types |= edge2->types; + isl_map_free(edge2->map); + + if (is_condition(edge2)) { + if (!edge1->tagged_condition) + edge1->tagged_condition = edge2->tagged_condition; + else + edge1->tagged_condition = + isl_union_map_union(edge1->tagged_condition, + edge2->tagged_condition); + } + + if (is_conditional_validity(edge2)) { + if (!edge1->tagged_validity) + edge1->tagged_validity = edge2->tagged_validity; + else + edge1->tagged_validity = + isl_union_map_union(edge1->tagged_validity, + edge2->tagged_validity); + } + + if (is_condition(edge2) && !edge1->tagged_condition) + return -1; + if (is_conditional_validity(edge2) && !edge1->tagged_validity) + return -1; + + return 0; +} + +/* Insert dummy tags in domain and range of "map". + * + * In particular, if "map" is of the form + * + * A -> B + * + * then return + * + * [A -> dummy_tag] -> [B -> dummy_tag] + * + * where the dummy_tags are identical and equal to any dummy tags + * introduced by any other call to this function. + */ +static __isl_give isl_map *insert_dummy_tags(__isl_take isl_map *map) +{ + static char dummy; + isl_ctx *ctx; + isl_id *id; + isl_space *space; + isl_set *domain, *range; + + ctx = isl_map_get_ctx(map); + + id = isl_id_alloc(ctx, NULL, &dummy); + space = isl_space_params(isl_map_get_space(map)); + space = isl_space_set_from_params(space); + space = isl_space_set_tuple_id(space, isl_dim_set, id); + space = isl_space_map_from_set(space); + + domain = isl_map_wrap(map); + range = isl_map_wrap(isl_map_universe(space)); + map = isl_map_from_domain_and_range(domain, range); + map = isl_map_zip(map); + + return map; +} + +/* Given that at least one of "src" or "dst" is compressed, return + * a map between the spaces of these nodes restricted to the affine + * hull that was used in the compression. + */ +static __isl_give isl_map *extract_hull(struct isl_sched_node *src, + struct isl_sched_node *dst) +{ + isl_set *dom, *ran; + + if (src->compressed) + dom = isl_set_copy(src->hull); + else + dom = isl_set_universe(isl_space_copy(src->space)); + if (dst->compressed) + ran = isl_set_copy(dst->hull); + else + ran = isl_set_universe(isl_space_copy(dst->space)); + + return isl_map_from_domain_and_range(dom, ran); +} + +/* Intersect the domains of the nested relations in domain and range + * of "tagged" with "map". + */ +static __isl_give isl_map *map_intersect_domains(__isl_take isl_map *tagged, + __isl_keep isl_map *map) +{ + isl_set *set; + + tagged = isl_map_zip(tagged); + set = isl_map_wrap(isl_map_copy(map)); + tagged = isl_map_intersect_domain(tagged, set); + tagged = isl_map_zip(tagged); + return tagged; +} + +/* Return a pointer to the node that lives in the domain space of "map" + * or NULL if there is no such node. + */ +static struct isl_sched_node *find_domain_node(isl_ctx *ctx, + struct isl_sched_graph *graph, __isl_keep isl_map *map) +{ + struct isl_sched_node *node; + isl_space *space; + + space = isl_space_domain(isl_map_get_space(map)); + node = graph_find_node(ctx, graph, space); + isl_space_free(space); + + return node; +} + +/* Return a pointer to the node that lives in the range space of "map" + * or NULL if there is no such node. + */ +static struct isl_sched_node *find_range_node(isl_ctx *ctx, + struct isl_sched_graph *graph, __isl_keep isl_map *map) +{ + struct isl_sched_node *node; + isl_space *space; + + space = isl_space_range(isl_map_get_space(map)); + node = graph_find_node(ctx, graph, space); + isl_space_free(space); + + return node; +} + +/* Add a new edge to the graph based on the given map + * and add it to data->graph->edge_table[data->type]. + * If a dependence relation of a given type happens to be identical + * to one of the dependence relations of a type that was added before, + * then we don't create a new edge, but instead mark the original edge + * as also representing a dependence of the current type. + * + * Edges of type isl_edge_condition or isl_edge_conditional_validity + * may be specified as "tagged" dependence relations. That is, "map" + * may contain elements (i -> a) -> (j -> b), where i -> j denotes + * the dependence on iterations and a and b are tags. + * edge->map is set to the relation containing the elements i -> j, + * while edge->tagged_condition and edge->tagged_validity contain + * the union of all the "map" relations + * for which extract_edge is called that result in the same edge->map. + * + * If the source or the destination node is compressed, then + * intersect both "map" and "tagged" with the constraints that + * were used to construct the compression. + * This ensures that there are no schedule constraints defined + * outside of these domains, while the scheduler no longer has + * any control over those outside parts. + */ +static isl_stat extract_edge(__isl_take isl_map *map, void *user) +{ + isl_ctx *ctx = isl_map_get_ctx(map); + struct isl_extract_edge_data *data = user; + struct isl_sched_graph *graph = data->graph; + struct isl_sched_node *src, *dst; + struct isl_sched_edge *edge; + isl_map *tagged = NULL; + + if (data->type == isl_edge_condition || + data->type == isl_edge_conditional_validity) { + if (isl_map_can_zip(map)) { + tagged = isl_map_copy(map); + map = isl_set_unwrap(isl_map_domain(isl_map_zip(map))); + } else { + tagged = insert_dummy_tags(isl_map_copy(map)); + } + } + + src = find_domain_node(ctx, graph, map); + dst = find_range_node(ctx, graph, map); + + if (!src || !dst) { + isl_map_free(map); + isl_map_free(tagged); + return isl_stat_ok; + } + + if (src->compressed || dst->compressed) { + isl_map *hull; + hull = extract_hull(src, dst); + if (tagged) + tagged = map_intersect_domains(tagged, hull); + map = isl_map_intersect(map, hull); + } + + graph->edge[graph->n_edge].src = src; + graph->edge[graph->n_edge].dst = dst; + graph->edge[graph->n_edge].map = map; + graph->edge[graph->n_edge].types = 0; + graph->edge[graph->n_edge].tagged_condition = NULL; + graph->edge[graph->n_edge].tagged_validity = NULL; + set_type(&graph->edge[graph->n_edge], data->type); + if (data->type == isl_edge_condition) + graph->edge[graph->n_edge].tagged_condition = + isl_union_map_from_map(tagged); + if (data->type == isl_edge_conditional_validity) + graph->edge[graph->n_edge].tagged_validity = + isl_union_map_from_map(tagged); + + edge = graph_find_matching_edge(graph, &graph->edge[graph->n_edge]); + if (!edge) { + graph->n_edge++; + return isl_stat_error; + } + if (edge == &graph->edge[graph->n_edge]) + return graph_edge_table_add(ctx, graph, data->type, + &graph->edge[graph->n_edge++]); + + if (merge_edge(edge, &graph->edge[graph->n_edge]) < 0) + return -1; + + return graph_edge_table_add(ctx, graph, data->type, edge); +} + +/* Initialize the schedule graph "graph" from the schedule constraints "sc". + * + * The context is included in the domain before the nodes of + * the graphs are extracted in order to be able to exploit + * any possible additional equalities. + * Note that this intersection is only performed locally here. + */ +static isl_stat graph_init(struct isl_sched_graph *graph, + __isl_keep isl_schedule_constraints *sc) +{ + isl_ctx *ctx; + isl_union_set *domain; + struct isl_extract_edge_data data; + enum isl_edge_type i; + isl_stat r; + + if (!sc) + return isl_stat_error; + + ctx = isl_schedule_constraints_get_ctx(sc); + + domain = isl_schedule_constraints_get_domain(sc); + graph->n = isl_union_set_n_set(domain); + isl_union_set_free(domain); + + if (graph_alloc(ctx, graph, graph->n, + isl_schedule_constraints_n_map(sc)) < 0) + return isl_stat_error; + + if (compute_max_row(graph, sc) < 0) + return isl_stat_error; + graph->root = 1; + graph->n = 0; + domain = isl_schedule_constraints_get_domain(sc); + domain = isl_union_set_intersect_params(domain, + isl_set_copy(sc->context)); + r = isl_union_set_foreach_set(domain, &extract_node, graph); + isl_union_set_free(domain); + if (r < 0) + return isl_stat_error; + if (graph_init_table(ctx, graph) < 0) + return isl_stat_error; + for (i = isl_edge_first; i <= isl_edge_last; ++i) + graph->max_edge[i] = isl_union_map_n_map(sc->constraint[i]); + if (graph_init_edge_tables(ctx, graph) < 0) + return isl_stat_error; + graph->n_edge = 0; + data.graph = graph; + for (i = isl_edge_first; i <= isl_edge_last; ++i) { + data.type = i; + if (isl_union_map_foreach_map(sc->constraint[i], + &extract_edge, &data) < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Check whether there is any dependence from node[j] to node[i] + * or from node[i] to node[j]. + */ +static isl_bool node_follows_weak(int i, int j, void *user) +{ + isl_bool f; + struct isl_sched_graph *graph = user; + + f = graph_has_any_edge(graph, &graph->node[j], &graph->node[i]); + if (f < 0 || f) + return f; + return graph_has_any_edge(graph, &graph->node[i], &graph->node[j]); +} + +/* Check whether there is a (conditional) validity dependence from node[j] + * to node[i], forcing node[i] to follow node[j]. + */ +static isl_bool node_follows_strong(int i, int j, void *user) +{ + struct isl_sched_graph *graph = user; + + return graph_has_validity_edge(graph, &graph->node[j], &graph->node[i]); +} + +/* Use Tarjan's algorithm for computing the strongly connected components + * in the dependence graph only considering those edges defined by "follows". + */ +static int detect_ccs(isl_ctx *ctx, struct isl_sched_graph *graph, + isl_bool (*follows)(int i, int j, void *user)) +{ + int i, n; + struct isl_tarjan_graph *g = NULL; + + g = isl_tarjan_graph_init(ctx, graph->n, follows, graph); + if (!g) + return -1; + + graph->scc = 0; + i = 0; + n = graph->n; + while (n) { + while (g->order[i] != -1) { + graph->node[g->order[i]].scc = graph->scc; + --n; + ++i; + } + ++i; + graph->scc++; + } + + isl_tarjan_graph_free(g); + + return 0; +} + +/* Apply Tarjan's algorithm to detect the strongly connected components + * in the dependence graph. + * Only consider the (conditional) validity dependences and clear "weak". + */ +static int detect_sccs(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + graph->weak = 0; + return detect_ccs(ctx, graph, &node_follows_strong); +} + +/* Apply Tarjan's algorithm to detect the (weakly) connected components + * in the dependence graph. + * Consider all dependences and set "weak". + */ +static int detect_wccs(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + graph->weak = 1; + return detect_ccs(ctx, graph, &node_follows_weak); +} + +static int cmp_scc(const void *a, const void *b, void *data) +{ + struct isl_sched_graph *graph = data; + const int *i1 = a; + const int *i2 = b; + + return graph->node[*i1].scc - graph->node[*i2].scc; +} + +/* Sort the elements of graph->sorted according to the corresponding SCCs. + */ +static int sort_sccs(struct isl_sched_graph *graph) +{ + return isl_sort(graph->sorted, graph->n, sizeof(int), &cmp_scc, graph); +} + +/* Given a dependence relation R from "node" to itself, + * construct the set of coefficients of valid constraints for elements + * in that dependence relation. + * In particular, the result contains tuples of coefficients + * c_0, c_n, c_x such that + * + * c_0 + c_n n + c_x y - c_x x >= 0 for each (x,y) in R + * + * or, equivalently, + * + * c_0 + c_n n + c_x d >= 0 for each d in delta R = { y - x | (x,y) in R } + * + * We choose here to compute the dual of delta R. + * Alternatively, we could have computed the dual of R, resulting + * in a set of tuples c_0, c_n, c_x, c_y, and then + * plugged in (c_0, c_n, c_x, -c_x). + * + * If "node" has been compressed, then the dependence relation + * is also compressed before the set of coefficients is computed. + */ +static __isl_give isl_basic_set *intra_coefficients( + struct isl_sched_graph *graph, struct isl_sched_node *node, + __isl_take isl_map *map) +{ + isl_set *delta; + isl_map *key; + isl_basic_set *coef; + isl_maybe_isl_basic_set m; + + m = isl_map_to_basic_set_try_get(graph->intra_hmap, map); + if (m.valid < 0 || m.valid) { + isl_map_free(map); + return m.value; + } + + key = isl_map_copy(map); + if (node->compressed) { + map = isl_map_preimage_domain_multi_aff(map, + isl_multi_aff_copy(node->decompress)); + map = isl_map_preimage_range_multi_aff(map, + isl_multi_aff_copy(node->decompress)); + } + delta = isl_set_remove_divs(isl_map_deltas(map)); + coef = isl_set_coefficients(delta); + graph->intra_hmap = isl_map_to_basic_set_set(graph->intra_hmap, key, + isl_basic_set_copy(coef)); + + return coef; +} + +/* Given a dependence relation R, construct the set of coefficients + * of valid constraints for elements in that dependence relation. + * In particular, the result contains tuples of coefficients + * c_0, c_n, c_x, c_y such that + * + * c_0 + c_n n + c_x x + c_y y >= 0 for each (x,y) in R + * + * If the source or destination nodes of "edge" have been compressed, + * then the dependence relation is also compressed before + * the set of coefficients is computed. + */ +static __isl_give isl_basic_set *inter_coefficients( + struct isl_sched_graph *graph, struct isl_sched_edge *edge, + __isl_take isl_map *map) +{ + isl_set *set; + isl_map *key; + isl_basic_set *coef; + isl_maybe_isl_basic_set m; + + m = isl_map_to_basic_set_try_get(graph->inter_hmap, map); + if (m.valid < 0 || m.valid) { + isl_map_free(map); + return m.value; + } + + key = isl_map_copy(map); + if (edge->src->compressed) + map = isl_map_preimage_domain_multi_aff(map, + isl_multi_aff_copy(edge->src->decompress)); + if (edge->dst->compressed) + map = isl_map_preimage_range_multi_aff(map, + isl_multi_aff_copy(edge->dst->decompress)); + set = isl_map_wrap(isl_map_remove_divs(map)); + coef = isl_set_coefficients(set); + graph->inter_hmap = isl_map_to_basic_set_set(graph->inter_hmap, key, + isl_basic_set_copy(coef)); + + return coef; +} + +/* Return the position of the coefficients of the variables in + * the coefficients constraints "coef". + * + * The space of "coef" is of the form + * + * { coefficients[[cst, params] -> S] } + * + * Return the position of S. + */ +static int coef_var_offset(__isl_keep isl_basic_set *coef) +{ + int offset; + isl_space *space; + + space = isl_space_unwrap(isl_basic_set_get_space(coef)); + offset = isl_space_dim(space, isl_dim_in); + isl_space_free(space); + + return offset; +} + +/* Return the offset of the coefficients of the variables of "node" + * within the (I)LP. + * + * Within each node, the coefficients have the following order: + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + */ +static int node_var_coef_offset(struct isl_sched_node *node) +{ + return node->start + 1 + node->nparam; +} + +/* Construct an isl_dim_map for mapping constraints on coefficients + * for "node" to the corresponding positions in graph->lp. + * "offset" is the offset of the coefficients for the variables + * in the input constraints. + * "s" is the sign of the mapping. + * + * The input constraints are given in terms of the coefficients (c_0, c_n, c_x). + * The mapping produced by this function essentially plugs in + * (0, 0, c_i_x^+ - c_i_x^-) if s = 1 and + * (0, 0, -c_i_x^+ + c_i_x^-) if s = -1. + * In graph->lp, the c_i_x^- appear before their c_i_x^+ counterpart. + * + * The caller can extend the mapping to also map the other coefficients + * (and therefore not plug in 0). + */ +static __isl_give isl_dim_map *intra_dim_map(isl_ctx *ctx, + struct isl_sched_graph *graph, struct isl_sched_node *node, + int offset, int s) +{ + int pos; + unsigned total; + isl_dim_map *dim_map; + + total = isl_basic_set_total_dim(graph->lp); + pos = node_var_coef_offset(node); + dim_map = isl_dim_map_alloc(ctx, total); + isl_dim_map_range(dim_map, pos, 2, offset, 1, node->nvar, -s); + isl_dim_map_range(dim_map, pos + 1, 2, offset, 1, node->nvar, s); + + return dim_map; +} + +/* Construct an isl_dim_map for mapping constraints on coefficients + * for "src" (node i) and "dst" (node j) to the corresponding positions + * in graph->lp. + * "offset" is the offset of the coefficients for the variables of "src" + * in the input constraints. + * "s" is the sign of the mapping. + * + * The input constraints are given in terms of the coefficients + * (c_0, c_n, c_x, c_y). + * The mapping produced by this function essentially plugs in + * (c_j_0 - c_i_0, c_j_n - c_i_n, + * c_j_x^+ - c_j_x^-, -(c_i_x^+ - c_i_x^-)) if s = 1 and + * (-c_j_0 + c_i_0, -c_j_n + c_i_n, + * - (c_j_x^+ - c_j_x^-), c_i_x^+ - c_i_x^-) if s = -1. + * In graph->lp, the c_*^- appear before their c_*^+ counterpart. + * + * The caller can further extend the mapping. + */ +static __isl_give isl_dim_map *inter_dim_map(isl_ctx *ctx, + struct isl_sched_graph *graph, struct isl_sched_node *src, + struct isl_sched_node *dst, int offset, int s) +{ + int pos; + unsigned total; + isl_dim_map *dim_map; + + total = isl_basic_set_total_dim(graph->lp); + dim_map = isl_dim_map_alloc(ctx, total); + + isl_dim_map_range(dim_map, dst->start, 0, 0, 0, 1, s); + isl_dim_map_range(dim_map, dst->start + 1, 1, 1, 1, dst->nparam, s); + pos = node_var_coef_offset(dst); + isl_dim_map_range(dim_map, pos, 2, offset + src->nvar, 1, + dst->nvar, -s); + isl_dim_map_range(dim_map, pos + 1, 2, offset + src->nvar, 1, + dst->nvar, s); + + isl_dim_map_range(dim_map, src->start, 0, 0, 0, 1, -s); + isl_dim_map_range(dim_map, src->start + 1, 1, 1, 1, src->nparam, -s); + pos = node_var_coef_offset(src); + isl_dim_map_range(dim_map, pos, 2, offset, 1, src->nvar, s); + isl_dim_map_range(dim_map, pos + 1, 2, offset, 1, src->nvar, -s); + + return dim_map; +} + +/* Add constraints to graph->lp that force validity for the given + * dependence from a node i to itself. + * That is, add constraints that enforce + * + * (c_i_0 + c_i_n n + c_i_x y) - (c_i_0 + c_i_n n + c_i_x x) + * = c_i_x (y - x) >= 0 + * + * for each (x,y) in R. + * We obtain general constraints on coefficients (c_0, c_n, c_x) + * of valid constraints for (y - x) and then plug in (0, 0, c_i_x^+ - c_i_x^-), + * where c_i_x = c_i_x^+ - c_i_x^-, with c_i_x^+ and c_i_x^- non-negative. + * In graph->lp, the c_i_x^- appear before their c_i_x^+ counterpart. + * + * Actually, we do not construct constraints for the c_i_x themselves, + * but for the coefficients of c_i_x written as a linear combination + * of the columns in node->cmap. + */ +static isl_stat add_intra_validity_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge) +{ + int offset; + isl_map *map = isl_map_copy(edge->map); + isl_ctx *ctx = isl_map_get_ctx(map); + isl_dim_map *dim_map; + isl_basic_set *coef; + struct isl_sched_node *node = edge->src; + + coef = intra_coefficients(graph, node, map); + + offset = coef_var_offset(coef); + + coef = isl_basic_set_transform_dims(coef, isl_dim_set, + offset, isl_mat_copy(node->cmap)); + if (!coef) + return isl_stat_error; + + dim_map = intra_dim_map(ctx, graph, node, offset, 1); + graph->lp = isl_basic_set_extend_constraints(graph->lp, + coef->n_eq, coef->n_ineq); + graph->lp = isl_basic_set_add_constraints_dim_map(graph->lp, + coef, dim_map); + + return isl_stat_ok; +} + +/* Add constraints to graph->lp that force validity for the given + * dependence from node i to node j. + * That is, add constraints that enforce + * + * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) >= 0 + * + * for each (x,y) in R. + * We obtain general constraints on coefficients (c_0, c_n, c_x, c_y) + * of valid constraints for R and then plug in + * (c_j_0 - c_i_0, c_j_n - c_i_n, c_j_x^+ - c_j_x^- - (c_i_x^+ - c_i_x^-)), + * where c_* = c_*^+ - c_*^-, with c_*^+ and c_*^- non-negative. + * In graph->lp, the c_*^- appear before their c_*^+ counterpart. + * + * Actually, we do not construct constraints for the c_*_x themselves, + * but for the coefficients of c_*_x written as a linear combination + * of the columns in node->cmap. + */ +static isl_stat add_inter_validity_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge) +{ + int offset; + isl_map *map = isl_map_copy(edge->map); + isl_ctx *ctx = isl_map_get_ctx(map); + isl_dim_map *dim_map; + isl_basic_set *coef; + struct isl_sched_node *src = edge->src; + struct isl_sched_node *dst = edge->dst; + + coef = inter_coefficients(graph, edge, map); + + offset = coef_var_offset(coef); + + coef = isl_basic_set_transform_dims(coef, isl_dim_set, + offset, isl_mat_copy(src->cmap)); + coef = isl_basic_set_transform_dims(coef, isl_dim_set, + offset + src->nvar, isl_mat_copy(dst->cmap)); + if (!coef) + return isl_stat_error; + + dim_map = inter_dim_map(ctx, graph, src, dst, offset, 1); + + edge->start = graph->lp->n_ineq; + graph->lp = isl_basic_set_extend_constraints(graph->lp, + coef->n_eq, coef->n_ineq); + graph->lp = isl_basic_set_add_constraints_dim_map(graph->lp, + coef, dim_map); + if (!graph->lp) + return isl_stat_error; + edge->end = graph->lp->n_ineq; + + return isl_stat_ok; +} + +/* Add constraints to graph->lp that bound the dependence distance for the given + * dependence from a node i to itself. + * If s = 1, we add the constraint + * + * c_i_x (y - x) <= m_0 + m_n n + * + * or + * + * -c_i_x (y - x) + m_0 + m_n n >= 0 + * + * for each (x,y) in R. + * If s = -1, we add the constraint + * + * -c_i_x (y - x) <= m_0 + m_n n + * + * or + * + * c_i_x (y - x) + m_0 + m_n n >= 0 + * + * for each (x,y) in R. + * We obtain general constraints on coefficients (c_0, c_n, c_x) + * of valid constraints for (y - x) and then plug in (m_0, m_n, -s * c_i_x), + * with each coefficient (except m_0) represented as a pair of non-negative + * coefficients. + * + * Actually, we do not construct constraints for the c_i_x themselves, + * but for the coefficients of c_i_x written as a linear combination + * of the columns in node->cmap. + * + * + * If "local" is set, then we add constraints + * + * c_i_x (y - x) <= 0 + * + * or + * + * -c_i_x (y - x) <= 0 + * + * instead, forcing the dependence distance to be (less than or) equal to 0. + * That is, we plug in (0, 0, -s * c_i_x), + * Note that dependences marked local are treated as validity constraints + * by add_all_validity_constraints and therefore also have + * their distances bounded by 0 from below. + */ +static isl_stat add_intra_proximity_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge, int s, int local) +{ + int offset; + unsigned nparam; + isl_map *map = isl_map_copy(edge->map); + isl_ctx *ctx = isl_map_get_ctx(map); + isl_dim_map *dim_map; + isl_basic_set *coef; + struct isl_sched_node *node = edge->src; + + coef = intra_coefficients(graph, node, map); + + offset = coef_var_offset(coef); + + coef = isl_basic_set_transform_dims(coef, isl_dim_set, + offset, isl_mat_copy(node->cmap)); + if (!coef) + return isl_stat_error; + + nparam = isl_space_dim(node->space, isl_dim_param); + dim_map = intra_dim_map(ctx, graph, node, offset, -s); + + if (!local) { + isl_dim_map_range(dim_map, 1, 0, 0, 0, 1, 1); + isl_dim_map_range(dim_map, 4, 2, 1, 1, nparam, -1); + isl_dim_map_range(dim_map, 5, 2, 1, 1, nparam, 1); + } + graph->lp = isl_basic_set_extend_constraints(graph->lp, + coef->n_eq, coef->n_ineq); + graph->lp = isl_basic_set_add_constraints_dim_map(graph->lp, + coef, dim_map); + + return isl_stat_ok; +} + +/* Add constraints to graph->lp that bound the dependence distance for the given + * dependence from node i to node j. + * If s = 1, we add the constraint + * + * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) + * <= m_0 + m_n n + * + * or + * + * -(c_j_0 + c_j_n n + c_j_x y) + (c_i_0 + c_i_n n + c_i_x x) + + * m_0 + m_n n >= 0 + * + * for each (x,y) in R. + * If s = -1, we add the constraint + * + * -((c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x)) + * <= m_0 + m_n n + * + * or + * + * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) + + * m_0 + m_n n >= 0 + * + * for each (x,y) in R. + * We obtain general constraints on coefficients (c_0, c_n, c_x, c_y) + * of valid constraints for R and then plug in + * (m_0 - s*c_j_0 + s*c_i_0, m_n - s*c_j_n + s*c_i_n, + * -s*c_j_x+s*c_i_x) + * with each coefficient (except m_0, c_*_0 and c_*_n) + * represented as a pair of non-negative coefficients. + * + * Actually, we do not construct constraints for the c_*_x themselves, + * but for the coefficients of c_*_x written as a linear combination + * of the columns in node->cmap. + * + * + * If "local" is set, then we add constraints + * + * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) <= 0 + * + * or + * + * -((c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x)) <= 0 + * + * instead, forcing the dependence distance to be (less than or) equal to 0. + * That is, we plug in + * (-s*c_j_0 + s*c_i_0, -s*c_j_n + s*c_i_n, -s*c_j_x+s*c_i_x). + * Note that dependences marked local are treated as validity constraints + * by add_all_validity_constraints and therefore also have + * their distances bounded by 0 from below. + */ +static isl_stat add_inter_proximity_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge, int s, int local) +{ + int offset; + unsigned nparam; + isl_map *map = isl_map_copy(edge->map); + isl_ctx *ctx = isl_map_get_ctx(map); + isl_dim_map *dim_map; + isl_basic_set *coef; + struct isl_sched_node *src = edge->src; + struct isl_sched_node *dst = edge->dst; + + coef = inter_coefficients(graph, edge, map); + + offset = coef_var_offset(coef); + + coef = isl_basic_set_transform_dims(coef, isl_dim_set, + offset, isl_mat_copy(src->cmap)); + coef = isl_basic_set_transform_dims(coef, isl_dim_set, + offset + src->nvar, isl_mat_copy(dst->cmap)); + if (!coef) + return isl_stat_error; + + nparam = isl_space_dim(src->space, isl_dim_param); + dim_map = inter_dim_map(ctx, graph, src, dst, offset, -s); + + if (!local) { + isl_dim_map_range(dim_map, 1, 0, 0, 0, 1, 1); + isl_dim_map_range(dim_map, 4, 2, 1, 1, nparam, -1); + isl_dim_map_range(dim_map, 5, 2, 1, 1, nparam, 1); + } + + graph->lp = isl_basic_set_extend_constraints(graph->lp, + coef->n_eq, coef->n_ineq); + graph->lp = isl_basic_set_add_constraints_dim_map(graph->lp, + coef, dim_map); + + return isl_stat_ok; +} + +/* Add all validity constraints to graph->lp. + * + * An edge that is forced to be local needs to have its dependence + * distances equal to zero. We take care of bounding them by 0 from below + * here. add_all_proximity_constraints takes care of bounding them by 0 + * from above. + * + * If "use_coincidence" is set, then we treat coincidence edges as local edges. + * Otherwise, we ignore them. + */ +static int add_all_validity_constraints(struct isl_sched_graph *graph, + int use_coincidence) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge= &graph->edge[i]; + int local; + + local = is_local(edge) || + (is_coincidence(edge) && use_coincidence); + if (!is_validity(edge) && !local) + continue; + if (edge->src != edge->dst) + continue; + if (add_intra_validity_constraints(graph, edge) < 0) + return -1; + } + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge = &graph->edge[i]; + int local; + + local = is_local(edge) || + (is_coincidence(edge) && use_coincidence); + if (!is_validity(edge) && !local) + continue; + if (edge->src == edge->dst) + continue; + if (add_inter_validity_constraints(graph, edge) < 0) + return -1; + } + + return 0; +} + +/* Add constraints to graph->lp that bound the dependence distance + * for all dependence relations. + * If a given proximity dependence is identical to a validity + * dependence, then the dependence distance is already bounded + * from below (by zero), so we only need to bound the distance + * from above. (This includes the case of "local" dependences + * which are treated as validity dependence by add_all_validity_constraints.) + * Otherwise, we need to bound the distance both from above and from below. + * + * If "use_coincidence" is set, then we treat coincidence edges as local edges. + * Otherwise, we ignore them. + */ +static int add_all_proximity_constraints(struct isl_sched_graph *graph, + int use_coincidence) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge= &graph->edge[i]; + int local; + + local = is_local(edge) || + (is_coincidence(edge) && use_coincidence); + if (!is_proximity(edge) && !local) + continue; + if (edge->src == edge->dst && + add_intra_proximity_constraints(graph, edge, 1, local) < 0) + return -1; + if (edge->src != edge->dst && + add_inter_proximity_constraints(graph, edge, 1, local) < 0) + return -1; + if (is_validity(edge) || local) + continue; + if (edge->src == edge->dst && + add_intra_proximity_constraints(graph, edge, -1, 0) < 0) + return -1; + if (edge->src != edge->dst && + add_inter_proximity_constraints(graph, edge, -1, 0) < 0) + return -1; + } + + return 0; +} + +/* Compute a basis for the rows in the linear part of the schedule + * and extend this basis to a full basis. The remaining rows + * can then be used to force linear independence from the rows + * in the schedule. + * + * In particular, given the schedule rows S, we compute + * + * S = H Q + * S U = H + * + * with H the Hermite normal form of S. That is, all but the + * first rank columns of H are zero and so each row in S is + * a linear combination of the first rank rows of Q. + * The matrix Q is then transposed because we will write the + * coefficients of the next schedule row as a column vector s + * and express this s as a linear combination s = Q c of the + * computed basis. + * Similarly, the matrix U is transposed such that we can + * compute the coefficients c = U s from a schedule row s. + */ +static int node_update_cmap(struct isl_sched_node *node) +{ + isl_mat *H, *U, *Q; + int n_row = isl_mat_rows(node->sched); + + H = isl_mat_sub_alloc(node->sched, 0, n_row, + 1 + node->nparam, node->nvar); + + H = isl_mat_left_hermite(H, 0, &U, &Q); + isl_mat_free(node->cmap); + isl_mat_free(node->cinv); + isl_mat_free(node->ctrans); + node->ctrans = isl_mat_copy(Q); + node->cmap = isl_mat_transpose(Q); + node->cinv = isl_mat_transpose(U); + node->rank = isl_mat_initial_non_zero_cols(H); + isl_mat_free(H); + + if (!node->cmap || !node->cinv || !node->ctrans || node->rank < 0) + return -1; + return 0; +} + +/* Is "edge" marked as a validity or a conditional validity edge? + */ +static int is_any_validity(struct isl_sched_edge *edge) +{ + return is_validity(edge) || is_conditional_validity(edge); +} + +/* How many times should we count the constraints in "edge"? + * + * If carry is set, then we are counting the number of + * (validity or conditional validity) constraints that will be added + * in setup_carry_lp and we count each edge exactly once. + * + * Otherwise, we count as follows + * validity -> 1 (>= 0) + * validity+proximity -> 2 (>= 0 and upper bound) + * proximity -> 2 (lower and upper bound) + * local(+any) -> 2 (>= 0 and <= 0) + * + * If an edge is only marked conditional_validity then it counts + * as zero since it is only checked afterwards. + * + * If "use_coincidence" is set, then we treat coincidence edges as local edges. + * Otherwise, we ignore them. + */ +static int edge_multiplicity(struct isl_sched_edge *edge, int carry, + int use_coincidence) +{ + if (carry) + return 1; + if (is_proximity(edge) || is_local(edge)) + return 2; + if (use_coincidence && is_coincidence(edge)) + return 2; + if (is_validity(edge)) + return 1; + return 0; +} + +/* Count the number of equality and inequality constraints + * that will be added for the given map. + * + * "use_coincidence" is set if we should take into account coincidence edges. + */ +static int count_map_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge, __isl_take isl_map *map, + int *n_eq, int *n_ineq, int carry, int use_coincidence) +{ + isl_basic_set *coef; + int f = edge_multiplicity(edge, carry, use_coincidence); + + if (f == 0) { + isl_map_free(map); + return 0; + } + + if (edge->src == edge->dst) + coef = intra_coefficients(graph, edge->src, map); + else + coef = inter_coefficients(graph, edge, map); + if (!coef) + return -1; + *n_eq += f * coef->n_eq; + *n_ineq += f * coef->n_ineq; + isl_basic_set_free(coef); + + return 0; +} + +/* Count the number of equality and inequality constraints + * that will be added to the main lp problem. + * We count as follows + * validity -> 1 (>= 0) + * validity+proximity -> 2 (>= 0 and upper bound) + * proximity -> 2 (lower and upper bound) + * local(+any) -> 2 (>= 0 and <= 0) + * + * If "use_coincidence" is set, then we treat coincidence edges as local edges. + * Otherwise, we ignore them. + */ +static int count_constraints(struct isl_sched_graph *graph, + int *n_eq, int *n_ineq, int use_coincidence) +{ + int i; + + *n_eq = *n_ineq = 0; + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge= &graph->edge[i]; + isl_map *map = isl_map_copy(edge->map); + + if (count_map_constraints(graph, edge, map, n_eq, n_ineq, + 0, use_coincidence) < 0) + return -1; + } + + return 0; +} + +/* Count the number of constraints that will be added by + * add_bound_constant_constraints to bound the values of the constant terms + * and increment *n_eq and *n_ineq accordingly. + * + * In practice, add_bound_constant_constraints only adds inequalities. + */ +static isl_stat count_bound_constant_constraints(isl_ctx *ctx, + struct isl_sched_graph *graph, int *n_eq, int *n_ineq) +{ + if (isl_options_get_schedule_max_constant_term(ctx) == -1) + return isl_stat_ok; + + *n_ineq += graph->n; + + return isl_stat_ok; +} + +/* Add constraints to bound the values of the constant terms in the schedule, + * if requested by the user. + * + * The maximal value of the constant terms is defined by the option + * "schedule_max_constant_term". + * + * Within each node, the coefficients have the following order: + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + */ +static isl_stat add_bound_constant_constraints(isl_ctx *ctx, + struct isl_sched_graph *graph) +{ + int i, k; + int max; + int total; + + max = isl_options_get_schedule_max_constant_term(ctx); + if (max == -1) + return isl_stat_ok; + + total = isl_basic_set_dim(graph->lp, isl_dim_set); + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + k = isl_basic_set_alloc_inequality(graph->lp); + if (k < 0) + return isl_stat_error; + isl_seq_clr(graph->lp->ineq[k], 1 + total); + isl_int_set_si(graph->lp->ineq[k][1 + node->start], -1); + isl_int_set_si(graph->lp->ineq[k][0], max); + } + + return isl_stat_ok; +} + +/* Count the number of constraints that will be added by + * add_bound_coefficient_constraints and increment *n_eq and *n_ineq + * accordingly. + * + * In practice, add_bound_coefficient_constraints only adds inequalities. + */ +static int count_bound_coefficient_constraints(isl_ctx *ctx, + struct isl_sched_graph *graph, int *n_eq, int *n_ineq) +{ + int i; + + if (isl_options_get_schedule_max_coefficient(ctx) == -1 && + !isl_options_get_schedule_treat_coalescing(ctx)) + return 0; + + for (i = 0; i < graph->n; ++i) + *n_ineq += graph->node[i].nparam + 2 * graph->node[i].nvar; + + return 0; +} + +/* Add constraints to graph->lp that bound the values of + * the parameter schedule coefficients of "node" to "max" and + * the variable schedule coefficients to the corresponding entry + * in node->max. + * In either case, a negative value means that no bound needs to be imposed. + * + * For parameter coefficients, this amounts to adding a constraint + * + * c_n <= max + * + * i.e., + * + * -c_n + max >= 0 + * + * The variables coefficients are, however, not represented directly. + * Instead, the variables coefficients c_x are written as a linear + * combination c_x = cmap c_z of some other coefficients c_z, + * which are in turn encoded as c_z = c_z^+ - c_z^-. + * Let a_j be the elements of row i of node->cmap, then + * + * -max_i <= c_x_i <= max_i + * + * is encoded as + * + * -max_i <= \sum_j a_j (c_z_j^+ - c_z_j^-) <= max_i + * + * or + * + * -\sum_j a_j (c_z_j^+ - c_z_j^-) + max_i >= 0 + * \sum_j a_j (c_z_j^+ - c_z_j^-) + max_i >= 0 + */ +static isl_stat node_add_coefficient_constraints(isl_ctx *ctx, + struct isl_sched_graph *graph, struct isl_sched_node *node, int max) +{ + int i, j, k; + int total; + isl_vec *ineq; + + total = isl_basic_set_dim(graph->lp, isl_dim_set); + + for (j = 0; j < node->nparam; ++j) { + int dim; + + if (max < 0) + continue; + + k = isl_basic_set_alloc_inequality(graph->lp); + if (k < 0) + return isl_stat_error; + dim = 1 + node->start + 1 + j; + isl_seq_clr(graph->lp->ineq[k], 1 + total); + isl_int_set_si(graph->lp->ineq[k][dim], -1); + isl_int_set_si(graph->lp->ineq[k][0], max); + } + + ineq = isl_vec_alloc(ctx, 1 + total); + ineq = isl_vec_clr(ineq); + if (!ineq) + return isl_stat_error; + for (i = 0; i < node->nvar; ++i) { + int pos = 1 + node_var_coef_offset(node); + + if (isl_int_is_neg(node->max->el[i])) + continue; + + for (j = 0; j < node->nvar; ++j) { + isl_int_set(ineq->el[pos + 2 * j], + node->cmap->row[i][j]); + isl_int_neg(ineq->el[pos + 2 * j + 1], + node->cmap->row[i][j]); + } + isl_int_set(ineq->el[0], node->max->el[i]); + + k = isl_basic_set_alloc_inequality(graph->lp); + if (k < 0) + goto error; + isl_seq_cpy(graph->lp->ineq[k], ineq->el, 1 + total); + + isl_seq_neg(ineq->el + pos, ineq->el + pos, 2 * node->nvar); + k = isl_basic_set_alloc_inequality(graph->lp); + if (k < 0) + goto error; + isl_seq_cpy(graph->lp->ineq[k], ineq->el, 1 + total); + } + isl_vec_free(ineq); + + return isl_stat_ok; +error: + isl_vec_free(ineq); + return isl_stat_error; +} + +/* Add constraints that bound the values of the variable and parameter + * coefficients of the schedule. + * + * The maximal value of the coefficients is defined by the option + * 'schedule_max_coefficient' and the entries in node->max. + * These latter entries are only set if either the schedule_max_coefficient + * option or the schedule_treat_coalescing option is set. + */ +static isl_stat add_bound_coefficient_constraints(isl_ctx *ctx, + struct isl_sched_graph *graph) +{ + int i; + int max; + + max = isl_options_get_schedule_max_coefficient(ctx); + + if (max == -1 && !isl_options_get_schedule_treat_coalescing(ctx)) + return isl_stat_ok; + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + + if (node_add_coefficient_constraints(ctx, graph, node, max) < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +/* Add a constraint to graph->lp that equates the value at position + * "sum_pos" to the sum of the "n" values starting at "first". + */ +static isl_stat add_sum_constraint(struct isl_sched_graph *graph, + int sum_pos, int first, int n) +{ + int i, k; + int total; + + total = isl_basic_set_dim(graph->lp, isl_dim_set); + + k = isl_basic_set_alloc_equality(graph->lp); + if (k < 0) + return isl_stat_error; + isl_seq_clr(graph->lp->eq[k], 1 + total); + isl_int_set_si(graph->lp->eq[k][1 + sum_pos], -1); + for (i = 0; i < n; ++i) + isl_int_set_si(graph->lp->eq[k][1 + first + i], 1); + + return isl_stat_ok; +} + +/* Add a constraint to graph->lp that equates the value at position + * "sum_pos" to the sum of the parameter coefficients of all nodes. + * + * Within each node, the coefficients have the following order: + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + */ +static isl_stat add_param_sum_constraint(struct isl_sched_graph *graph, + int sum_pos) +{ + int i, j, k; + int total; + + total = isl_basic_set_dim(graph->lp, isl_dim_set); + + k = isl_basic_set_alloc_equality(graph->lp); + if (k < 0) + return isl_stat_error; + isl_seq_clr(graph->lp->eq[k], 1 + total); + isl_int_set_si(graph->lp->eq[k][1 + sum_pos], -1); + for (i = 0; i < graph->n; ++i) { + int pos = 1 + graph->node[i].start + 1; + + for (j = 0; j < graph->node[i].nparam; ++j) + isl_int_set_si(graph->lp->eq[k][pos + j], 1); + } + + return isl_stat_ok; +} + +/* Add a constraint to graph->lp that equates the value at position + * "sum_pos" to the sum of the variable coefficients of all nodes. + * + * Within each node, the coefficients have the following order: + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + */ +static isl_stat add_var_sum_constraint(struct isl_sched_graph *graph, + int sum_pos) +{ + int i, j, k; + int total; + + total = isl_basic_set_dim(graph->lp, isl_dim_set); + + k = isl_basic_set_alloc_equality(graph->lp); + if (k < 0) + return isl_stat_error; + isl_seq_clr(graph->lp->eq[k], 1 + total); + isl_int_set_si(graph->lp->eq[k][1 + sum_pos], -1); + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + int pos = 1 + node_var_coef_offset(node); + + for (j = 0; j < 2 * node->nvar; ++j) + isl_int_set_si(graph->lp->eq[k][pos + j], 1); + } + + return isl_stat_ok; +} + +/* Construct an ILP problem for finding schedule coefficients + * that result in non-negative, but small dependence distances + * over all dependences. + * In particular, the dependence distances over proximity edges + * are bounded by m_0 + m_n n and we compute schedule coefficients + * with small values (preferably zero) of m_n and m_0. + * + * All variables of the ILP are non-negative. The actual coefficients + * may be negative, so each coefficient is represented as the difference + * of two non-negative variables. The negative part always appears + * immediately before the positive part. + * Other than that, the variables have the following order + * + * - sum of positive and negative parts of m_n coefficients + * - m_0 + * - sum of all c_n coefficients + * (unconstrained when computing non-parametric schedules) + * - sum of positive and negative parts of all c_x coefficients + * - positive and negative parts of m_n coefficients + * - for each node + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + * + * The c_i_x are not represented directly, but through the columns of + * node->cmap. That is, the computed values are for variable t_i_x + * such that c_i_x = Q t_i_x with Q equal to node->cmap. + * + * The constraints are those from the edges plus two or three equalities + * to express the sums. + * + * If "use_coincidence" is set, then we treat coincidence edges as local edges. + * Otherwise, we ignore them. + */ +static isl_stat setup_lp(isl_ctx *ctx, struct isl_sched_graph *graph, + int use_coincidence) +{ + int i; + unsigned nparam; + unsigned total; + isl_space *space; + int parametric; + int param_pos; + int n_eq, n_ineq; + + parametric = ctx->opt->schedule_parametric; + nparam = isl_space_dim(graph->node[0].space, isl_dim_param); + param_pos = 4; + total = param_pos + 2 * nparam; + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[graph->sorted[i]]; + if (node_update_cmap(node) < 0) + return isl_stat_error; + node->start = total; + total += 1 + node->nparam + 2 * node->nvar; + } + + if (count_constraints(graph, &n_eq, &n_ineq, use_coincidence) < 0) + return isl_stat_error; + if (count_bound_constant_constraints(ctx, graph, &n_eq, &n_ineq) < 0) + return isl_stat_error; + if (count_bound_coefficient_constraints(ctx, graph, &n_eq, &n_ineq) < 0) + return isl_stat_error; + + space = isl_space_set_alloc(ctx, 0, total); + isl_basic_set_free(graph->lp); + n_eq += 2 + parametric; + + graph->lp = isl_basic_set_alloc_space(space, 0, n_eq, n_ineq); + + if (add_sum_constraint(graph, 0, param_pos, 2 * nparam) < 0) + return isl_stat_error; + if (parametric && add_param_sum_constraint(graph, 2) < 0) + return isl_stat_error; + if (add_var_sum_constraint(graph, 3) < 0) + return isl_stat_error; + if (add_bound_constant_constraints(ctx, graph) < 0) + return isl_stat_error; + if (add_bound_coefficient_constraints(ctx, graph) < 0) + return isl_stat_error; + if (add_all_validity_constraints(graph, use_coincidence) < 0) + return isl_stat_error; + if (add_all_proximity_constraints(graph, use_coincidence) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Analyze the conflicting constraint found by + * isl_tab_basic_set_non_trivial_lexmin. If it corresponds to the validity + * constraint of one of the edges between distinct nodes, living, moreover + * in distinct SCCs, then record the source and sink SCC as this may + * be a good place to cut between SCCs. + */ +static int check_conflict(int con, void *user) +{ + int i; + struct isl_sched_graph *graph = user; + + if (graph->src_scc >= 0) + return 0; + + con -= graph->lp->n_eq; + + if (con >= graph->lp->n_ineq) + return 0; + + for (i = 0; i < graph->n_edge; ++i) { + if (!is_validity(&graph->edge[i])) + continue; + if (graph->edge[i].src == graph->edge[i].dst) + continue; + if (graph->edge[i].src->scc == graph->edge[i].dst->scc) + continue; + if (graph->edge[i].start > con) + continue; + if (graph->edge[i].end <= con) + continue; + graph->src_scc = graph->edge[i].src->scc; + graph->dst_scc = graph->edge[i].dst->scc; + } + + return 0; +} + +/* Check whether the next schedule row of the given node needs to be + * non-trivial. Lower-dimensional domains may have some trivial rows, + * but as soon as the number of remaining required non-trivial rows + * is as large as the number or remaining rows to be computed, + * all remaining rows need to be non-trivial. + */ +static int needs_row(struct isl_sched_graph *graph, struct isl_sched_node *node) +{ + return node->nvar - node->rank >= graph->maxvar - graph->n_row; +} + +/* Solve the ILP problem constructed in setup_lp. + * For each node such that all the remaining rows of its schedule + * need to be non-trivial, we construct a non-triviality region. + * This region imposes that the next row is independent of previous rows. + * In particular the coefficients c_i_x are represented by t_i_x + * variables with c_i_x = Q t_i_x and Q a unimodular matrix such that + * its first columns span the rows of the previously computed part + * of the schedule. The non-triviality region enforces that at least + * one of the remaining components of t_i_x is non-zero, i.e., + * that the new schedule row depends on at least one of the remaining + * columns of Q. + */ +static __isl_give isl_vec *solve_lp(struct isl_sched_graph *graph) +{ + int i; + isl_vec *sol; + isl_basic_set *lp; + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + int skip = node->rank; + graph->region[i].pos = node_var_coef_offset(node) + 2 * skip; + if (needs_row(graph, node)) + graph->region[i].len = 2 * (node->nvar - skip); + else + graph->region[i].len = 0; + } + lp = isl_basic_set_copy(graph->lp); + sol = isl_tab_basic_set_non_trivial_lexmin(lp, 2, graph->n, + graph->region, &check_conflict, graph); + return sol; +} + +/* Extract the coefficients for the variables of "node" from "sol". + * + * Within each node, the coefficients have the following order: + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + * + * The c_i_x^- appear before their c_i_x^+ counterpart. + * + * Return c_i_x = c_i_x^+ - c_i_x^- + */ +static __isl_give isl_vec *extract_var_coef(struct isl_sched_node *node, + __isl_keep isl_vec *sol) +{ + int i; + int pos; + isl_vec *csol; + + if (!sol) + return NULL; + csol = isl_vec_alloc(isl_vec_get_ctx(sol), node->nvar); + if (!csol) + return NULL; + + pos = 1 + node_var_coef_offset(node); + for (i = 0; i < node->nvar; ++i) + isl_int_sub(csol->el[i], + sol->el[pos + 2 * i + 1], sol->el[pos + 2 * i]); + + return csol; +} + +/* Update the schedules of all nodes based on the given solution + * of the LP problem. + * The new row is added to the current band. + * All possibly negative coefficients are encoded as a difference + * of two non-negative variables, so we need to perform the subtraction + * here. Moreover, if use_cmap is set, then the solution does + * not refer to the actual coefficients c_i_x, but instead to variables + * t_i_x such that c_i_x = Q t_i_x and Q is equal to node->cmap. + * In this case, we then also need to perform this multiplication + * to obtain the values of c_i_x. + * + * If coincident is set, then the caller guarantees that the new + * row satisfies the coincidence constraints. + */ +static int update_schedule(struct isl_sched_graph *graph, + __isl_take isl_vec *sol, int use_cmap, int coincident) +{ + int i, j; + isl_vec *csol = NULL; + + if (!sol) + goto error; + if (sol->size == 0) + isl_die(sol->ctx, isl_error_internal, + "no solution found", goto error); + if (graph->n_total_row >= graph->max_row) + isl_die(sol->ctx, isl_error_internal, + "too many schedule rows", goto error); + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + int pos = node->start; + int row = isl_mat_rows(node->sched); + + isl_vec_free(csol); + csol = extract_var_coef(node, sol); + if (!csol) + goto error; + + isl_map_free(node->sched_map); + node->sched_map = NULL; + node->sched = isl_mat_add_rows(node->sched, 1); + if (!node->sched) + goto error; + for (j = 0; j < 1 + node->nparam; ++j) + node->sched = isl_mat_set_element(node->sched, + row, j, sol->el[1 + pos + j]); + if (use_cmap) + csol = isl_mat_vec_product(isl_mat_copy(node->cmap), + csol); + if (!csol) + goto error; + for (j = 0; j < node->nvar; ++j) + node->sched = isl_mat_set_element(node->sched, + row, 1 + node->nparam + j, csol->el[j]); + node->coincident[graph->n_total_row] = coincident; + } + isl_vec_free(sol); + isl_vec_free(csol); + + graph->n_row++; + graph->n_total_row++; + + return 0; +error: + isl_vec_free(sol); + isl_vec_free(csol); + return -1; +} + +/* Convert row "row" of node->sched into an isl_aff living in "ls" + * and return this isl_aff. + */ +static __isl_give isl_aff *extract_schedule_row(__isl_take isl_local_space *ls, + struct isl_sched_node *node, int row) +{ + int j; + isl_int v; + isl_aff *aff; + + isl_int_init(v); + + aff = isl_aff_zero_on_domain(ls); + isl_mat_get_element(node->sched, row, 0, &v); + aff = isl_aff_set_constant(aff, v); + for (j = 0; j < node->nparam; ++j) { + isl_mat_get_element(node->sched, row, 1 + j, &v); + aff = isl_aff_set_coefficient(aff, isl_dim_param, j, v); + } + for (j = 0; j < node->nvar; ++j) { + isl_mat_get_element(node->sched, row, 1 + node->nparam + j, &v); + aff = isl_aff_set_coefficient(aff, isl_dim_in, j, v); + } + + isl_int_clear(v); + + return aff; +} + +/* Convert the "n" rows starting at "first" of node->sched into a multi_aff + * and return this multi_aff. + * + * The result is defined over the uncompressed node domain. + */ +static __isl_give isl_multi_aff *node_extract_partial_schedule_multi_aff( + struct isl_sched_node *node, int first, int n) +{ + int i; + isl_space *space; + isl_local_space *ls; + isl_aff *aff; + isl_multi_aff *ma; + int nrow; + + if (!node) + return NULL; + nrow = isl_mat_rows(node->sched); + if (node->compressed) + space = isl_multi_aff_get_domain_space(node->decompress); + else + space = isl_space_copy(node->space); + ls = isl_local_space_from_space(isl_space_copy(space)); + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, n); + ma = isl_multi_aff_zero(space); + + for (i = first; i < first + n; ++i) { + aff = extract_schedule_row(isl_local_space_copy(ls), node, i); + ma = isl_multi_aff_set_aff(ma, i - first, aff); + } + + isl_local_space_free(ls); + + if (node->compressed) + ma = isl_multi_aff_pullback_multi_aff(ma, + isl_multi_aff_copy(node->compress)); + + return ma; +} + +/* Convert node->sched into a multi_aff and return this multi_aff. + * + * The result is defined over the uncompressed node domain. + */ +static __isl_give isl_multi_aff *node_extract_schedule_multi_aff( + struct isl_sched_node *node) +{ + int nrow; + + nrow = isl_mat_rows(node->sched); + return node_extract_partial_schedule_multi_aff(node, 0, nrow); +} + +/* Convert node->sched into a map and return this map. + * + * The result is cached in node->sched_map, which needs to be released + * whenever node->sched is updated. + * It is defined over the uncompressed node domain. + */ +static __isl_give isl_map *node_extract_schedule(struct isl_sched_node *node) +{ + if (!node->sched_map) { + isl_multi_aff *ma; + + ma = node_extract_schedule_multi_aff(node); + node->sched_map = isl_map_from_multi_aff(ma); + } + + return isl_map_copy(node->sched_map); +} + +/* Construct a map that can be used to update a dependence relation + * based on the current schedule. + * That is, construct a map expressing that source and sink + * are executed within the same iteration of the current schedule. + * This map can then be intersected with the dependence relation. + * This is not the most efficient way, but this shouldn't be a critical + * operation. + */ +static __isl_give isl_map *specializer(struct isl_sched_node *src, + struct isl_sched_node *dst) +{ + isl_map *src_sched, *dst_sched; + + src_sched = node_extract_schedule(src); + dst_sched = node_extract_schedule(dst); + return isl_map_apply_range(src_sched, isl_map_reverse(dst_sched)); +} + +/* Intersect the domains of the nested relations in domain and range + * of "umap" with "map". + */ +static __isl_give isl_union_map *intersect_domains( + __isl_take isl_union_map *umap, __isl_keep isl_map *map) +{ + isl_union_set *uset; + + umap = isl_union_map_zip(umap); + uset = isl_union_set_from_set(isl_map_wrap(isl_map_copy(map))); + umap = isl_union_map_intersect_domain(umap, uset); + umap = isl_union_map_zip(umap); + return umap; +} + +/* Update the dependence relation of the given edge based + * on the current schedule. + * If the dependence is carried completely by the current schedule, then + * it is removed from the edge_tables. It is kept in the list of edges + * as otherwise all edge_tables would have to be recomputed. + */ +static int update_edge(struct isl_sched_graph *graph, + struct isl_sched_edge *edge) +{ + int empty; + isl_map *id; + + id = specializer(edge->src, edge->dst); + edge->map = isl_map_intersect(edge->map, isl_map_copy(id)); + if (!edge->map) + goto error; + + if (edge->tagged_condition) { + edge->tagged_condition = + intersect_domains(edge->tagged_condition, id); + if (!edge->tagged_condition) + goto error; + } + if (edge->tagged_validity) { + edge->tagged_validity = + intersect_domains(edge->tagged_validity, id); + if (!edge->tagged_validity) + goto error; + } + + empty = isl_map_plain_is_empty(edge->map); + if (empty < 0) + goto error; + if (empty) + graph_remove_edge(graph, edge); + + isl_map_free(id); + return 0; +error: + isl_map_free(id); + return -1; +} + +/* Does the domain of "umap" intersect "uset"? + */ +static int domain_intersects(__isl_keep isl_union_map *umap, + __isl_keep isl_union_set *uset) +{ + int empty; + + umap = isl_union_map_copy(umap); + umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(uset)); + empty = isl_union_map_is_empty(umap); + isl_union_map_free(umap); + + return empty < 0 ? -1 : !empty; +} + +/* Does the range of "umap" intersect "uset"? + */ +static int range_intersects(__isl_keep isl_union_map *umap, + __isl_keep isl_union_set *uset) +{ + int empty; + + umap = isl_union_map_copy(umap); + umap = isl_union_map_intersect_range(umap, isl_union_set_copy(uset)); + empty = isl_union_map_is_empty(umap); + isl_union_map_free(umap); + + return empty < 0 ? -1 : !empty; +} + +/* Are the condition dependences of "edge" local with respect to + * the current schedule? + * + * That is, are domain and range of the condition dependences mapped + * to the same point? + * + * In other words, is the condition false? + */ +static int is_condition_false(struct isl_sched_edge *edge) +{ + isl_union_map *umap; + isl_map *map, *sched, *test; + int empty, local; + + empty = isl_union_map_is_empty(edge->tagged_condition); + if (empty < 0 || empty) + return empty; + + umap = isl_union_map_copy(edge->tagged_condition); + umap = isl_union_map_zip(umap); + umap = isl_union_set_unwrap(isl_union_map_domain(umap)); + map = isl_map_from_union_map(umap); + + sched = node_extract_schedule(edge->src); + map = isl_map_apply_domain(map, sched); + sched = node_extract_schedule(edge->dst); + map = isl_map_apply_range(map, sched); + + test = isl_map_identity(isl_map_get_space(map)); + local = isl_map_is_subset(map, test); + isl_map_free(map); + isl_map_free(test); + + return local; +} + +/* For each conditional validity constraint that is adjacent + * to a condition with domain in condition_source or range in condition_sink, + * turn it into an unconditional validity constraint. + */ +static int unconditionalize_adjacent_validity(struct isl_sched_graph *graph, + __isl_take isl_union_set *condition_source, + __isl_take isl_union_set *condition_sink) +{ + int i; + + condition_source = isl_union_set_coalesce(condition_source); + condition_sink = isl_union_set_coalesce(condition_sink); + + for (i = 0; i < graph->n_edge; ++i) { + int adjacent; + isl_union_map *validity; + + if (!is_conditional_validity(&graph->edge[i])) + continue; + if (is_validity(&graph->edge[i])) + continue; + + validity = graph->edge[i].tagged_validity; + adjacent = domain_intersects(validity, condition_sink); + if (adjacent >= 0 && !adjacent) + adjacent = range_intersects(validity, condition_source); + if (adjacent < 0) + goto error; + if (!adjacent) + continue; + + set_validity(&graph->edge[i]); + } + + isl_union_set_free(condition_source); + isl_union_set_free(condition_sink); + return 0; +error: + isl_union_set_free(condition_source); + isl_union_set_free(condition_sink); + return -1; +} + +/* Update the dependence relations of all edges based on the current schedule + * and enforce conditional validity constraints that are adjacent + * to satisfied condition constraints. + * + * First check if any of the condition constraints are satisfied + * (i.e., not local to the outer schedule) and keep track of + * their domain and range. + * Then update all dependence relations (which removes the non-local + * constraints). + * Finally, if any condition constraints turned out to be satisfied, + * then turn all adjacent conditional validity constraints into + * unconditional validity constraints. + */ +static int update_edges(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + int i; + int any = 0; + isl_union_set *source, *sink; + + source = isl_union_set_empty(isl_space_params_alloc(ctx, 0)); + sink = isl_union_set_empty(isl_space_params_alloc(ctx, 0)); + for (i = 0; i < graph->n_edge; ++i) { + int local; + isl_union_set *uset; + isl_union_map *umap; + + if (!is_condition(&graph->edge[i])) + continue; + if (is_local(&graph->edge[i])) + continue; + local = is_condition_false(&graph->edge[i]); + if (local < 0) + goto error; + if (local) + continue; + + any = 1; + + umap = isl_union_map_copy(graph->edge[i].tagged_condition); + uset = isl_union_map_domain(umap); + source = isl_union_set_union(source, uset); + + umap = isl_union_map_copy(graph->edge[i].tagged_condition); + uset = isl_union_map_range(umap); + sink = isl_union_set_union(sink, uset); + } + + for (i = graph->n_edge - 1; i >= 0; --i) { + if (update_edge(graph, &graph->edge[i]) < 0) + goto error; + } + + if (any) + return unconditionalize_adjacent_validity(graph, source, sink); + + isl_union_set_free(source); + isl_union_set_free(sink); + return 0; +error: + isl_union_set_free(source); + isl_union_set_free(sink); + return -1; +} + +static void next_band(struct isl_sched_graph *graph) +{ + graph->band_start = graph->n_total_row; +} + +/* Return the union of the universe domains of the nodes in "graph" + * that satisfy "pred". + */ +static __isl_give isl_union_set *isl_sched_graph_domain(isl_ctx *ctx, + struct isl_sched_graph *graph, + int (*pred)(struct isl_sched_node *node, int data), int data) +{ + int i; + isl_set *set; + isl_union_set *dom; + + for (i = 0; i < graph->n; ++i) + if (pred(&graph->node[i], data)) + break; + + if (i >= graph->n) + isl_die(ctx, isl_error_internal, + "empty component", return NULL); + + set = isl_set_universe(isl_space_copy(graph->node[i].space)); + dom = isl_union_set_from_set(set); + + for (i = i + 1; i < graph->n; ++i) { + if (!pred(&graph->node[i], data)) + continue; + set = isl_set_universe(isl_space_copy(graph->node[i].space)); + dom = isl_union_set_union(dom, isl_union_set_from_set(set)); + } + + return dom; +} + +/* Return a list of unions of universe domains, where each element + * in the list corresponds to an SCC (or WCC) indexed by node->scc. + */ +static __isl_give isl_union_set_list *extract_sccs(isl_ctx *ctx, + struct isl_sched_graph *graph) +{ + int i; + isl_union_set_list *filters; + + filters = isl_union_set_list_alloc(ctx, graph->scc); + for (i = 0; i < graph->scc; ++i) { + isl_union_set *dom; + + dom = isl_sched_graph_domain(ctx, graph, &node_scc_exactly, i); + filters = isl_union_set_list_add(filters, dom); + } + + return filters; +} + +/* Return a list of two unions of universe domains, one for the SCCs up + * to and including graph->src_scc and another for the other SCCs. + */ +static __isl_give isl_union_set_list *extract_split(isl_ctx *ctx, + struct isl_sched_graph *graph) +{ + isl_union_set *dom; + isl_union_set_list *filters; + + filters = isl_union_set_list_alloc(ctx, 2); + dom = isl_sched_graph_domain(ctx, graph, + &node_scc_at_most, graph->src_scc); + filters = isl_union_set_list_add(filters, dom); + dom = isl_sched_graph_domain(ctx, graph, + &node_scc_at_least, graph->src_scc + 1); + filters = isl_union_set_list_add(filters, dom); + + return filters; +} + +/* Copy nodes that satisfy node_pred from the src dependence graph + * to the dst dependence graph. + */ +static int copy_nodes(struct isl_sched_graph *dst, struct isl_sched_graph *src, + int (*node_pred)(struct isl_sched_node *node, int data), int data) +{ + int i; + + dst->n = 0; + for (i = 0; i < src->n; ++i) { + int j; + + if (!node_pred(&src->node[i], data)) + continue; + + j = dst->n; + dst->node[j].space = isl_space_copy(src->node[i].space); + dst->node[j].compressed = src->node[i].compressed; + dst->node[j].hull = isl_set_copy(src->node[i].hull); + dst->node[j].compress = + isl_multi_aff_copy(src->node[i].compress); + dst->node[j].decompress = + isl_multi_aff_copy(src->node[i].decompress); + dst->node[j].nvar = src->node[i].nvar; + dst->node[j].nparam = src->node[i].nparam; + dst->node[j].sched = isl_mat_copy(src->node[i].sched); + dst->node[j].sched_map = isl_map_copy(src->node[i].sched_map); + dst->node[j].coincident = src->node[i].coincident; + dst->node[j].sizes = isl_multi_val_copy(src->node[i].sizes); + dst->node[j].max = isl_vec_copy(src->node[i].max); + dst->n++; + + if (!dst->node[j].space || !dst->node[j].sched) + return -1; + if (dst->node[j].compressed && + (!dst->node[j].hull || !dst->node[j].compress || + !dst->node[j].decompress)) + return -1; + } + + return 0; +} + +/* Copy non-empty edges that satisfy edge_pred from the src dependence graph + * to the dst dependence graph. + * If the source or destination node of the edge is not in the destination + * graph, then it must be a backward proximity edge and it should simply + * be ignored. + */ +static int copy_edges(isl_ctx *ctx, struct isl_sched_graph *dst, + struct isl_sched_graph *src, + int (*edge_pred)(struct isl_sched_edge *edge, int data), int data) +{ + int i; + enum isl_edge_type t; + + dst->n_edge = 0; + for (i = 0; i < src->n_edge; ++i) { + struct isl_sched_edge *edge = &src->edge[i]; + isl_map *map; + isl_union_map *tagged_condition; + isl_union_map *tagged_validity; + struct isl_sched_node *dst_src, *dst_dst; + + if (!edge_pred(edge, data)) + continue; + + if (isl_map_plain_is_empty(edge->map)) + continue; + + dst_src = graph_find_node(ctx, dst, edge->src->space); + dst_dst = graph_find_node(ctx, dst, edge->dst->space); + if (!dst_src || !dst_dst) { + if (is_validity(edge) || is_conditional_validity(edge)) + isl_die(ctx, isl_error_internal, + "backward (conditional) validity edge", + return -1); + continue; + } + + map = isl_map_copy(edge->map); + tagged_condition = isl_union_map_copy(edge->tagged_condition); + tagged_validity = isl_union_map_copy(edge->tagged_validity); + + dst->edge[dst->n_edge].src = dst_src; + dst->edge[dst->n_edge].dst = dst_dst; + dst->edge[dst->n_edge].map = map; + dst->edge[dst->n_edge].tagged_condition = tagged_condition; + dst->edge[dst->n_edge].tagged_validity = tagged_validity; + dst->edge[dst->n_edge].types = edge->types; + dst->n_edge++; + + if (edge->tagged_condition && !tagged_condition) + return -1; + if (edge->tagged_validity && !tagged_validity) + return -1; + + for (t = isl_edge_first; t <= isl_edge_last; ++t) { + if (edge != + graph_find_edge(src, t, edge->src, edge->dst)) + continue; + if (graph_edge_table_add(ctx, dst, t, + &dst->edge[dst->n_edge - 1]) < 0) + return -1; + } + } + + return 0; +} + +/* Compute the maximal number of variables over all nodes. + * This is the maximal number of linearly independent schedule + * rows that we need to compute. + * Just in case we end up in a part of the dependence graph + * with only lower-dimensional domains, we make sure we will + * compute the required amount of extra linearly independent rows. + */ +static int compute_maxvar(struct isl_sched_graph *graph) +{ + int i; + + graph->maxvar = 0; + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + int nvar; + + if (node_update_cmap(node) < 0) + return -1; + nvar = node->nvar + graph->n_row - node->rank; + if (nvar > graph->maxvar) + graph->maxvar = nvar; + } + + return 0; +} + +/* Extract the subgraph of "graph" that consists of the node satisfying + * "node_pred" and the edges satisfying "edge_pred" and store + * the result in "sub". + */ +static int extract_sub_graph(isl_ctx *ctx, struct isl_sched_graph *graph, + int (*node_pred)(struct isl_sched_node *node, int data), + int (*edge_pred)(struct isl_sched_edge *edge, int data), + int data, struct isl_sched_graph *sub) +{ + int i, n = 0, n_edge = 0; + int t; + + for (i = 0; i < graph->n; ++i) + if (node_pred(&graph->node[i], data)) + ++n; + for (i = 0; i < graph->n_edge; ++i) + if (edge_pred(&graph->edge[i], data)) + ++n_edge; + if (graph_alloc(ctx, sub, n, n_edge) < 0) + return -1; + if (copy_nodes(sub, graph, node_pred, data) < 0) + return -1; + if (graph_init_table(ctx, sub) < 0) + return -1; + for (t = 0; t <= isl_edge_last; ++t) + sub->max_edge[t] = graph->max_edge[t]; + if (graph_init_edge_tables(ctx, sub) < 0) + return -1; + if (copy_edges(ctx, sub, graph, edge_pred, data) < 0) + return -1; + sub->n_row = graph->n_row; + sub->max_row = graph->max_row; + sub->n_total_row = graph->n_total_row; + sub->band_start = graph->band_start; + + return 0; +} + +static __isl_give isl_schedule_node *compute_schedule(isl_schedule_node *node, + struct isl_sched_graph *graph); +static __isl_give isl_schedule_node *compute_schedule_wcc( + isl_schedule_node *node, struct isl_sched_graph *graph); + +/* Compute a schedule for a subgraph of "graph". In particular, for + * the graph composed of nodes that satisfy node_pred and edges that + * that satisfy edge_pred. + * If the subgraph is known to consist of a single component, then wcc should + * be set and then we call compute_schedule_wcc on the constructed subgraph. + * Otherwise, we call compute_schedule, which will check whether the subgraph + * is connected. + * + * The schedule is inserted at "node" and the updated schedule node + * is returned. + */ +static __isl_give isl_schedule_node *compute_sub_schedule( + __isl_take isl_schedule_node *node, isl_ctx *ctx, + struct isl_sched_graph *graph, + int (*node_pred)(struct isl_sched_node *node, int data), + int (*edge_pred)(struct isl_sched_edge *edge, int data), + int data, int wcc) +{ + struct isl_sched_graph split = { 0 }; + + if (extract_sub_graph(ctx, graph, node_pred, edge_pred, data, + &split) < 0) + goto error; + + if (wcc) + node = compute_schedule_wcc(node, &split); + else + node = compute_schedule(node, &split); + + graph_free(ctx, &split); + return node; +error: + graph_free(ctx, &split); + return isl_schedule_node_free(node); +} + +static int edge_scc_exactly(struct isl_sched_edge *edge, int scc) +{ + return edge->src->scc == scc && edge->dst->scc == scc; +} + +static int edge_dst_scc_at_most(struct isl_sched_edge *edge, int scc) +{ + return edge->dst->scc <= scc; +} + +static int edge_src_scc_at_least(struct isl_sched_edge *edge, int scc) +{ + return edge->src->scc >= scc; +} + +/* Reset the current band by dropping all its schedule rows. + */ +static int reset_band(struct isl_sched_graph *graph) +{ + int i; + int drop; + + drop = graph->n_total_row - graph->band_start; + graph->n_total_row -= drop; + graph->n_row -= drop; + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + + isl_map_free(node->sched_map); + node->sched_map = NULL; + + node->sched = isl_mat_drop_rows(node->sched, + graph->band_start, drop); + + if (!node->sched) + return -1; + } + + return 0; +} + +/* Split the current graph into two parts and compute a schedule for each + * part individually. In particular, one part consists of all SCCs up + * to and including graph->src_scc, while the other part contains the other + * SCCs. The split is enforced by a sequence node inserted at position "node" + * in the schedule tree. Return the updated schedule node. + * If either of these two parts consists of a sequence, then it is spliced + * into the sequence containing the two parts. + * + * The current band is reset. It would be possible to reuse + * the previously computed rows as the first rows in the next + * band, but recomputing them may result in better rows as we are looking + * at a smaller part of the dependence graph. + */ +static __isl_give isl_schedule_node *compute_split_schedule( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph) +{ + int is_seq; + isl_ctx *ctx; + isl_union_set_list *filters; + + if (!node) + return NULL; + + if (reset_band(graph) < 0) + return isl_schedule_node_free(node); + + next_band(graph); + + ctx = isl_schedule_node_get_ctx(node); + filters = extract_split(ctx, graph); + node = isl_schedule_node_insert_sequence(node, filters); + node = isl_schedule_node_child(node, 1); + node = isl_schedule_node_child(node, 0); + + node = compute_sub_schedule(node, ctx, graph, + &node_scc_at_least, &edge_src_scc_at_least, + graph->src_scc + 1, 0); + is_seq = isl_schedule_node_get_type(node) == isl_schedule_node_sequence; + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + if (is_seq) + node = isl_schedule_node_sequence_splice_child(node, 1); + node = isl_schedule_node_child(node, 0); + node = isl_schedule_node_child(node, 0); + node = compute_sub_schedule(node, ctx, graph, + &node_scc_at_most, &edge_dst_scc_at_most, + graph->src_scc, 0); + is_seq = isl_schedule_node_get_type(node) == isl_schedule_node_sequence; + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + if (is_seq) + node = isl_schedule_node_sequence_splice_child(node, 0); + + return node; +} + +/* Insert a band node at position "node" in the schedule tree corresponding + * to the current band in "graph". Mark the band node permutable + * if "permutable" is set. + * The partial schedules and the coincidence property are extracted + * from the graph nodes. + * Return the updated schedule node. + */ +static __isl_give isl_schedule_node *insert_current_band( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph, + int permutable) +{ + int i; + int start, end, n; + isl_multi_aff *ma; + isl_multi_pw_aff *mpa; + isl_multi_union_pw_aff *mupa; + + if (!node) + return NULL; + + if (graph->n < 1) + isl_die(isl_schedule_node_get_ctx(node), isl_error_internal, + "graph should have at least one node", + return isl_schedule_node_free(node)); + + start = graph->band_start; + end = graph->n_total_row; + n = end - start; + + ma = node_extract_partial_schedule_multi_aff(&graph->node[0], start, n); + mpa = isl_multi_pw_aff_from_multi_aff(ma); + mupa = isl_multi_union_pw_aff_from_multi_pw_aff(mpa); + + for (i = 1; i < graph->n; ++i) { + isl_multi_union_pw_aff *mupa_i; + + ma = node_extract_partial_schedule_multi_aff(&graph->node[i], + start, n); + mpa = isl_multi_pw_aff_from_multi_aff(ma); + mupa_i = isl_multi_union_pw_aff_from_multi_pw_aff(mpa); + mupa = isl_multi_union_pw_aff_union_add(mupa, mupa_i); + } + node = isl_schedule_node_insert_partial_schedule(node, mupa); + + for (i = 0; i < n; ++i) + node = isl_schedule_node_band_member_set_coincident(node, i, + graph->node[0].coincident[start + i]); + node = isl_schedule_node_band_set_permutable(node, permutable); + + return node; +} + +/* Update the dependence relations based on the current schedule, + * add the current band to "node" and then continue with the computation + * of the next band. + * Return the updated schedule node. + */ +static __isl_give isl_schedule_node *compute_next_band( + __isl_take isl_schedule_node *node, + struct isl_sched_graph *graph, int permutable) +{ + isl_ctx *ctx; + + if (!node) + return NULL; + + ctx = isl_schedule_node_get_ctx(node); + if (update_edges(ctx, graph) < 0) + return isl_schedule_node_free(node); + node = insert_current_band(node, graph, permutable); + next_band(graph); + + node = isl_schedule_node_child(node, 0); + node = compute_schedule(node, graph); + node = isl_schedule_node_parent(node); + + return node; +} + +/* Add constraints to graph->lp that force the dependence "map" (which + * is part of the dependence relation of "edge") + * to be respected and attempt to carry it, where the edge is one from + * a node j to itself. "pos" is the sequence number of the given map. + * That is, add constraints that enforce + * + * (c_j_0 + c_j_n n + c_j_x y) - (c_j_0 + c_j_n n + c_j_x x) + * = c_j_x (y - x) >= e_i + * + * for each (x,y) in R. + * We obtain general constraints on coefficients (c_0, c_n, c_x) + * of valid constraints for (y - x) and then plug in (-e_i, 0, c_j_x), + * with each coefficient in c_j_x represented as a pair of non-negative + * coefficients. + */ +static int add_intra_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge, __isl_take isl_map *map, int pos) +{ + int offset; + isl_ctx *ctx = isl_map_get_ctx(map); + isl_dim_map *dim_map; + isl_basic_set *coef; + struct isl_sched_node *node = edge->src; + + coef = intra_coefficients(graph, node, map); + if (!coef) + return -1; + + offset = coef_var_offset(coef); + dim_map = intra_dim_map(ctx, graph, node, offset, 1); + isl_dim_map_range(dim_map, 3 + pos, 0, 0, 0, 1, -1); + graph->lp = isl_basic_set_extend_constraints(graph->lp, + coef->n_eq, coef->n_ineq); + graph->lp = isl_basic_set_add_constraints_dim_map(graph->lp, + coef, dim_map); + + return 0; +} + +/* Add constraints to graph->lp that force the dependence "map" (which + * is part of the dependence relation of "edge") + * to be respected and attempt to carry it, where the edge is one from + * node j to node k. "pos" is the sequence number of the given map. + * That is, add constraints that enforce + * + * (c_k_0 + c_k_n n + c_k_x y) - (c_j_0 + c_j_n n + c_j_x x) >= e_i + * + * for each (x,y) in R. + * We obtain general constraints on coefficients (c_0, c_n, c_x) + * of valid constraints for R and then plug in + * (-e_i + c_k_0 - c_j_0, c_k_n - c_j_n, c_k_x - c_j_x) + * with each coefficient (except e_i, c_*_0 and c_*_n) + * represented as a pair of non-negative coefficients. + */ +static int add_inter_constraints(struct isl_sched_graph *graph, + struct isl_sched_edge *edge, __isl_take isl_map *map, int pos) +{ + int offset; + isl_ctx *ctx = isl_map_get_ctx(map); + isl_dim_map *dim_map; + isl_basic_set *coef; + struct isl_sched_node *src = edge->src; + struct isl_sched_node *dst = edge->dst; + + coef = inter_coefficients(graph, edge, map); + if (!coef) + return -1; + + offset = coef_var_offset(coef); + dim_map = inter_dim_map(ctx, graph, src, dst, offset, 1); + isl_dim_map_range(dim_map, 3 + pos, 0, 0, 0, 1, -1); + graph->lp = isl_basic_set_extend_constraints(graph->lp, + coef->n_eq, coef->n_ineq); + graph->lp = isl_basic_set_add_constraints_dim_map(graph->lp, + coef, dim_map); + + return 0; +} + +/* Add constraints to graph->lp that force all (conditional) validity + * dependences to be respected and attempt to carry them. + */ +static int add_all_constraints(struct isl_sched_graph *graph) +{ + int i, j; + int pos; + + pos = 0; + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge= &graph->edge[i]; + + if (!is_any_validity(edge)) + continue; + + for (j = 0; j < edge->map->n; ++j) { + isl_basic_map *bmap; + isl_map *map; + + bmap = isl_basic_map_copy(edge->map->p[j]); + map = isl_map_from_basic_map(bmap); + + if (edge->src == edge->dst && + add_intra_constraints(graph, edge, map, pos) < 0) + return -1; + if (edge->src != edge->dst && + add_inter_constraints(graph, edge, map, pos) < 0) + return -1; + ++pos; + } + } + + return 0; +} + +/* Count the number of equality and inequality constraints + * that will be added to the carry_lp problem. + * We count each edge exactly once. + */ +static int count_all_constraints(struct isl_sched_graph *graph, + int *n_eq, int *n_ineq) +{ + int i, j; + + *n_eq = *n_ineq = 0; + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge= &graph->edge[i]; + + if (!is_any_validity(edge)) + continue; + + for (j = 0; j < edge->map->n; ++j) { + isl_basic_map *bmap; + isl_map *map; + + bmap = isl_basic_map_copy(edge->map->p[j]); + map = isl_map_from_basic_map(bmap); + + if (count_map_constraints(graph, edge, map, + n_eq, n_ineq, 1, 0) < 0) + return -1; + } + } + + return 0; +} + +/* Construct an LP problem for finding schedule coefficients + * such that the schedule carries as many dependences as possible. + * In particular, for each dependence i, we bound the dependence distance + * from below by e_i, with 0 <= e_i <= 1 and then maximize the sum + * of all e_i's. Dependences with e_i = 0 in the solution are simply + * respected, while those with e_i > 0 (in practice e_i = 1) are carried. + * Note that if the dependence relation is a union of basic maps, + * then we have to consider each basic map individually as it may only + * be possible to carry the dependences expressed by some of those + * basic maps and not all of them. + * Below, we consider each of those basic maps as a separate "edge". + * + * All variables of the LP are non-negative. The actual coefficients + * may be negative, so each coefficient is represented as the difference + * of two non-negative variables. The negative part always appears + * immediately before the positive part. + * Other than that, the variables have the following order + * + * - sum of (1 - e_i) over all edges + * - sum of all c_n coefficients + * (unconstrained when computing non-parametric schedules) + * - sum of positive and negative parts of all c_x coefficients + * - for each edge + * - e_i + * - for each node + * - c_i_0 + * - c_i_n (if parametric) + * - positive and negative parts of c_i_x + * + * The constraints are those from the (validity) edges plus three equalities + * to express the sums and n_edge inequalities to express e_i <= 1. + */ +static isl_stat setup_carry_lp(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + int i; + int k; + isl_space *dim; + unsigned total; + int n_eq, n_ineq; + int n_edge; + + n_edge = 0; + for (i = 0; i < graph->n_edge; ++i) + n_edge += graph->edge[i].map->n; + + total = 3 + n_edge; + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[graph->sorted[i]]; + node->start = total; + total += 1 + node->nparam + 2 * node->nvar; + } + + if (count_all_constraints(graph, &n_eq, &n_ineq) < 0) + return isl_stat_error; + + dim = isl_space_set_alloc(ctx, 0, total); + isl_basic_set_free(graph->lp); + n_eq += 3; + n_ineq += n_edge; + graph->lp = isl_basic_set_alloc_space(dim, 0, n_eq, n_ineq); + graph->lp = isl_basic_set_set_rational(graph->lp); + + k = isl_basic_set_alloc_equality(graph->lp); + if (k < 0) + return isl_stat_error; + isl_seq_clr(graph->lp->eq[k], 1 + total); + isl_int_set_si(graph->lp->eq[k][0], -n_edge); + isl_int_set_si(graph->lp->eq[k][1], 1); + for (i = 0; i < n_edge; ++i) + isl_int_set_si(graph->lp->eq[k][4 + i], 1); + + if (add_param_sum_constraint(graph, 1) < 0) + return isl_stat_error; + if (add_var_sum_constraint(graph, 2) < 0) + return isl_stat_error; + + for (i = 0; i < n_edge; ++i) { + k = isl_basic_set_alloc_inequality(graph->lp); + if (k < 0) + return isl_stat_error; + isl_seq_clr(graph->lp->ineq[k], 1 + total); + isl_int_set_si(graph->lp->ineq[k][4 + i], -1); + isl_int_set_si(graph->lp->ineq[k][0], 1); + } + + if (add_all_constraints(graph) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +static __isl_give isl_schedule_node *compute_component_schedule( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph, + int wcc); + +/* Comparison function for sorting the statements based on + * the corresponding value in "r". + */ +static int smaller_value(const void *a, const void *b, void *data) +{ + isl_vec *r = data; + const int *i1 = a; + const int *i2 = b; + + return isl_int_cmp(r->el[*i1], r->el[*i2]); +} + +/* If the schedule_split_scaled option is set and if the linear + * parts of the scheduling rows for all nodes in the graphs have + * a non-trivial common divisor, then split off the remainder of the + * constant term modulo this common divisor from the linear part. + * Otherwise, insert a band node directly and continue with + * the construction of the schedule. + * + * If a non-trivial common divisor is found, then + * the linear part is reduced and the remainder is enforced + * by a sequence node with the children placed in the order + * of this remainder. + * In particular, we assign an scc index based on the remainder and + * then rely on compute_component_schedule to insert the sequence and + * to continue the schedule construction on each part. + */ +static __isl_give isl_schedule_node *split_scaled( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph) +{ + int i; + int row; + int scc; + isl_ctx *ctx; + isl_int gcd, gcd_i; + isl_vec *r; + int *order; + + if (!node) + return NULL; + + ctx = isl_schedule_node_get_ctx(node); + if (!ctx->opt->schedule_split_scaled) + return compute_next_band(node, graph, 0); + if (graph->n <= 1) + return compute_next_band(node, graph, 0); + + isl_int_init(gcd); + isl_int_init(gcd_i); + + isl_int_set_si(gcd, 0); + + row = isl_mat_rows(graph->node[0].sched) - 1; + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + int cols = isl_mat_cols(node->sched); + + isl_seq_gcd(node->sched->row[row] + 1, cols - 1, &gcd_i); + isl_int_gcd(gcd, gcd, gcd_i); + } + + isl_int_clear(gcd_i); + + if (isl_int_cmp_si(gcd, 1) <= 0) { + isl_int_clear(gcd); + return compute_next_band(node, graph, 0); + } + + r = isl_vec_alloc(ctx, graph->n); + order = isl_calloc_array(ctx, int, graph->n); + if (!r || !order) + goto error; + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + + order[i] = i; + isl_int_fdiv_r(r->el[i], node->sched->row[row][0], gcd); + isl_int_fdiv_q(node->sched->row[row][0], + node->sched->row[row][0], gcd); + isl_int_mul(node->sched->row[row][0], + node->sched->row[row][0], gcd); + node->sched = isl_mat_scale_down_row(node->sched, row, gcd); + if (!node->sched) + goto error; + } + + if (isl_sort(order, graph->n, sizeof(order[0]), &smaller_value, r) < 0) + goto error; + + scc = 0; + for (i = 0; i < graph->n; ++i) { + if (i > 0 && isl_int_ne(r->el[order[i - 1]], r->el[order[i]])) + ++scc; + graph->node[order[i]].scc = scc; + } + graph->scc = ++scc; + graph->weak = 0; + + isl_int_clear(gcd); + isl_vec_free(r); + free(order); + + if (update_edges(ctx, graph) < 0) + return isl_schedule_node_free(node); + node = insert_current_band(node, graph, 0); + next_band(graph); + + node = isl_schedule_node_child(node, 0); + node = compute_component_schedule(node, graph, 0); + node = isl_schedule_node_parent(node); + + return node; +error: + isl_vec_free(r); + free(order); + isl_int_clear(gcd); + return isl_schedule_node_free(node); +} + +/* Is the schedule row "sol" trivial on node "node"? + * That is, is the solution zero on the dimensions orthogonal to + * the previously found solutions? + * Return 1 if the solution is trivial, 0 if it is not and -1 on error. + * + * Each coefficient is represented as the difference between + * two non-negative values in "sol". "sol" has been computed + * in terms of the original iterators (i.e., without use of cmap). + * We construct the schedule row s and write it as a linear + * combination of (linear combinations of) previously computed schedule rows. + * s = Q c or c = U s. + * If the final entries of c are all zero, then the solution is trivial. + */ +static int is_trivial(struct isl_sched_node *node, __isl_keep isl_vec *sol) +{ + int trivial; + isl_vec *node_sol; + + if (!sol) + return -1; + if (node->nvar == node->rank) + return 0; + + node_sol = extract_var_coef(node, sol); + node_sol = isl_mat_vec_product(isl_mat_copy(node->cinv), node_sol); + if (!node_sol) + return -1; + + trivial = isl_seq_first_non_zero(node_sol->el + node->rank, + node->nvar - node->rank) == -1; + + isl_vec_free(node_sol); + + return trivial; +} + +/* Is the schedule row "sol" trivial on any node where it should + * not be trivial? + * "sol" has been computed in terms of the original iterators + * (i.e., without use of cmap). + * Return 1 if any solution is trivial, 0 if they are not and -1 on error. + */ +static int is_any_trivial(struct isl_sched_graph *graph, + __isl_keep isl_vec *sol) +{ + int i; + + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + int trivial; + + if (!needs_row(graph, node)) + continue; + trivial = is_trivial(node, sol); + if (trivial < 0 || trivial) + return trivial; + } + + return 0; +} + +/* Does the schedule represented by "sol" perform loop coalescing on "node"? + * If so, return the position of the coalesced dimension. + * Otherwise, return node->nvar or -1 on error. + * + * In particular, look for pairs of coefficients c_i and c_j such that + * |c_j/c_i| >= size_i, i.e., |c_j| >= |c_i * size_i|. + * If any such pair is found, then return i. + * If size_i is infinity, then no check on c_i needs to be performed. + */ +static int find_node_coalescing(struct isl_sched_node *node, + __isl_keep isl_vec *sol) +{ + int i, j; + isl_int max; + isl_vec *csol; + + if (node->nvar <= 1) + return node->nvar; + + csol = extract_var_coef(node, sol); + if (!csol) + return -1; + isl_int_init(max); + for (i = 0; i < node->nvar; ++i) { + isl_val *v; + + if (isl_int_is_zero(csol->el[i])) + continue; + v = isl_multi_val_get_val(node->sizes, i); + if (!v) + goto error; + if (!isl_val_is_int(v)) { + isl_val_free(v); + continue; + } + isl_int_mul(max, v->n, csol->el[i]); + isl_val_free(v); + + for (j = 0; j < node->nvar; ++j) { + if (j == i) + continue; + if (isl_int_abs_ge(csol->el[j], max)) + break; + } + if (j < node->nvar) + break; + } + + isl_int_clear(max); + isl_vec_free(csol); + return i; +error: + isl_int_clear(max); + isl_vec_free(csol); + return -1; +} + +/* Force the schedule coefficient at position "pos" of "node" to be zero + * in "tl". + * The coefficient is encoded as the difference between two non-negative + * variables. Force these two variables to have the same value. + */ +static __isl_give isl_tab_lexmin *zero_out_node_coef( + __isl_take isl_tab_lexmin *tl, struct isl_sched_node *node, int pos) +{ + int dim; + isl_ctx *ctx; + isl_vec *eq; + + ctx = isl_space_get_ctx(node->space); + dim = isl_tab_lexmin_dim(tl); + if (dim < 0) + return isl_tab_lexmin_free(tl); + eq = isl_vec_alloc(ctx, 1 + dim); + eq = isl_vec_clr(eq); + if (!eq) + return isl_tab_lexmin_free(tl); + + pos = 1 + node_var_coef_offset(node) + 2 * pos; + isl_int_set_si(eq->el[pos], 1); + isl_int_set_si(eq->el[pos + 1], -1); + tl = isl_tab_lexmin_add_eq(tl, eq->el); + isl_vec_free(eq); + + return tl; +} + +/* Return the lexicographically smallest rational point in the basic set + * from which "tl" was constructed, double checking that this input set + * was not empty. + */ +static __isl_give isl_vec *non_empty_solution(__isl_keep isl_tab_lexmin *tl) +{ + isl_vec *sol; + + sol = isl_tab_lexmin_get_solution(tl); + if (!sol) + return NULL; + if (sol->size == 0) + isl_die(isl_vec_get_ctx(sol), isl_error_internal, + "error in schedule construction", + return isl_vec_free(sol)); + return sol; +} + +/* Does the solution "sol" of the LP problem constructed by setup_carry_lp + * carry any of the "n_edge" groups of dependences? + * The value in the first position is the sum of (1 - e_i) over all "n_edge" + * edges, with 0 <= e_i <= 1 equal to 1 when the dependences represented + * by the edge are carried by the solution. + * If the sum of the (1 - e_i) is smaller than "n_edge" then at least + * one of those is carried. + * + * Note that despite the fact that the problem is solved using a rational + * solver, the solution is guaranteed to be integral. + * Specifically, the dependence distance lower bounds e_i (and therefore + * also their sum) are integers. See Lemma 5 of [1]. + * + * Any potential denominator of the sum is cleared by this function. + * The denominator is not relevant for any of the other elements + * in the solution. + * + * [1] P. Feautrier, Some Efficient Solutions to the Affine Scheduling + * Problem, Part II: Multi-Dimensional Time. + * In Intl. Journal of Parallel Programming, 1992. + */ +static int carries_dependences(__isl_keep isl_vec *sol, int n_edge) +{ + isl_int_divexact(sol->el[1], sol->el[1], sol->el[0]); + isl_int_set_si(sol->el[0], 1); + return isl_int_cmp_si(sol->el[1], n_edge) < 0; +} + +/* Return the lexicographically smallest rational point in "lp", + * assuming that all variables are non-negative and performing some + * additional sanity checks. + * In particular, "lp" should not be empty by construction. + * Double check that this is the case. + * Also, check that dependences are carried for at least one of + * the "n_edge" edges. + * + * If the computed schedule performs loop coalescing on a given node, + * i.e., if it is of the form + * + * c_i i + c_j j + ... + * + * with |c_j/c_i| >= size_i, then force the coefficient c_i to be zero + * to cut out this solution. Repeat this process until no more loop + * coalescing occurs or until no more dependences can be carried. + * In the latter case, revert to the previously computed solution. + */ +static __isl_give isl_vec *non_neg_lexmin(struct isl_sched_graph *graph, + __isl_take isl_basic_set *lp, int n_edge) +{ + int i, pos; + isl_ctx *ctx; + isl_tab_lexmin *tl; + isl_vec *sol, *prev = NULL; + int treat_coalescing; + + if (!lp) + return NULL; + ctx = isl_basic_set_get_ctx(lp); + treat_coalescing = isl_options_get_schedule_treat_coalescing(ctx); + tl = isl_tab_lexmin_from_basic_set(lp); + + do { + sol = non_empty_solution(tl); + if (!sol) + goto error; + + if (!carries_dependences(sol, n_edge)) { + if (!prev) + isl_die(ctx, isl_error_unknown, + "unable to carry dependences", + goto error); + isl_vec_free(sol); + sol = prev; + break; + } + prev = isl_vec_free(prev); + if (!treat_coalescing) + break; + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + + pos = find_node_coalescing(node, sol); + if (pos < 0) + goto error; + if (pos < node->nvar) + break; + } + if (i < graph->n) { + prev = sol; + tl = zero_out_node_coef(tl, &graph->node[i], pos); + } + } while (i < graph->n); + + isl_tab_lexmin_free(tl); + + return sol; +error: + isl_tab_lexmin_free(tl); + isl_vec_free(prev); + isl_vec_free(sol); + return NULL; +} + +/* Construct a schedule row for each node such that as many dependences + * as possible are carried and then continue with the next band. + * + * If the computed schedule row turns out to be trivial on one or + * more nodes where it should not be trivial, then we throw it away + * and try again on each component separately. + * + * If there is only one component, then we accept the schedule row anyway, + * but we do not consider it as a complete row and therefore do not + * increment graph->n_row. Note that the ranks of the nodes that + * do get a non-trivial schedule part will get updated regardless and + * graph->maxvar is computed based on these ranks. The test for + * whether more schedule rows are required in compute_schedule_wcc + * is therefore not affected. + * + * Insert a band corresponding to the schedule row at position "node" + * of the schedule tree and continue with the construction of the schedule. + * This insertion and the continued construction is performed by split_scaled + * after optionally checking for non-trivial common divisors. + */ +static __isl_give isl_schedule_node *carry_dependences( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph) +{ + int i; + int n_edge; + int trivial; + isl_ctx *ctx; + isl_vec *sol; + isl_basic_set *lp; + + if (!node) + return NULL; + + n_edge = 0; + for (i = 0; i < graph->n_edge; ++i) + n_edge += graph->edge[i].map->n; + + ctx = isl_schedule_node_get_ctx(node); + if (setup_carry_lp(ctx, graph) < 0) + return isl_schedule_node_free(node); + + lp = isl_basic_set_copy(graph->lp); + sol = non_neg_lexmin(graph, lp, n_edge); + if (!sol) + return isl_schedule_node_free(node); + + trivial = is_any_trivial(graph, sol); + if (trivial < 0) { + sol = isl_vec_free(sol); + } else if (trivial && graph->scc > 1) { + isl_vec_free(sol); + return compute_component_schedule(node, graph, 1); + } + + if (update_schedule(graph, sol, 0, 0) < 0) + return isl_schedule_node_free(node); + if (trivial) + graph->n_row--; + + return split_scaled(node, graph); +} + +/* Topologically sort statements mapped to the same schedule iteration + * and add insert a sequence node in front of "node" + * corresponding to this order. + * If "initialized" is set, then it may be assumed that compute_maxvar + * has been called on the current band. Otherwise, call + * compute_maxvar if and before carry_dependences gets called. + * + * If it turns out to be impossible to sort the statements apart, + * because different dependences impose different orderings + * on the statements, then we extend the schedule such that + * it carries at least one more dependence. + */ +static __isl_give isl_schedule_node *sort_statements( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph, + int initialized) +{ + isl_ctx *ctx; + isl_union_set_list *filters; + + if (!node) + return NULL; + + ctx = isl_schedule_node_get_ctx(node); + if (graph->n < 1) + isl_die(ctx, isl_error_internal, + "graph should have at least one node", + return isl_schedule_node_free(node)); + + if (graph->n == 1) + return node; + + if (update_edges(ctx, graph) < 0) + return isl_schedule_node_free(node); + + if (graph->n_edge == 0) + return node; + + if (detect_sccs(ctx, graph) < 0) + return isl_schedule_node_free(node); + + next_band(graph); + if (graph->scc < graph->n) { + if (!initialized && compute_maxvar(graph) < 0) + return isl_schedule_node_free(node); + return carry_dependences(node, graph); + } + + filters = extract_sccs(ctx, graph); + node = isl_schedule_node_insert_sequence(node, filters); + + return node; +} + +/* Are there any (non-empty) (conditional) validity edges in the graph? + */ +static int has_validity_edges(struct isl_sched_graph *graph) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) { + int empty; + + empty = isl_map_plain_is_empty(graph->edge[i].map); + if (empty < 0) + return -1; + if (empty) + continue; + if (is_any_validity(&graph->edge[i])) + return 1; + } + + return 0; +} + +/* Should we apply a Feautrier step? + * That is, did the user request the Feautrier algorithm and are + * there any validity dependences (left)? + */ +static int need_feautrier_step(isl_ctx *ctx, struct isl_sched_graph *graph) +{ + if (ctx->opt->schedule_algorithm != ISL_SCHEDULE_ALGORITHM_FEAUTRIER) + return 0; + + return has_validity_edges(graph); +} + +/* Compute a schedule for a connected dependence graph using Feautrier's + * multi-dimensional scheduling algorithm and return the updated schedule node. + * + * The original algorithm is described in [1]. + * The main idea is to minimize the number of scheduling dimensions, by + * trying to satisfy as many dependences as possible per scheduling dimension. + * + * [1] P. Feautrier, Some Efficient Solutions to the Affine Scheduling + * Problem, Part II: Multi-Dimensional Time. + * In Intl. Journal of Parallel Programming, 1992. + */ +static __isl_give isl_schedule_node *compute_schedule_wcc_feautrier( + isl_schedule_node *node, struct isl_sched_graph *graph) +{ + return carry_dependences(node, graph); +} + +/* Turn off the "local" bit on all (condition) edges. + */ +static void clear_local_edges(struct isl_sched_graph *graph) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) + if (is_condition(&graph->edge[i])) + clear_local(&graph->edge[i]); +} + +/* Does "graph" have both condition and conditional validity edges? + */ +static int need_condition_check(struct isl_sched_graph *graph) +{ + int i; + int any_condition = 0; + int any_conditional_validity = 0; + + for (i = 0; i < graph->n_edge; ++i) { + if (is_condition(&graph->edge[i])) + any_condition = 1; + if (is_conditional_validity(&graph->edge[i])) + any_conditional_validity = 1; + } + + return any_condition && any_conditional_validity; +} + +/* Does "graph" contain any coincidence edge? + */ +static int has_any_coincidence(struct isl_sched_graph *graph) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) + if (is_coincidence(&graph->edge[i])) + return 1; + + return 0; +} + +/* Extract the final schedule row as a map with the iteration domain + * of "node" as domain. + */ +static __isl_give isl_map *final_row(struct isl_sched_node *node) +{ + isl_local_space *ls; + isl_aff *aff; + int row; + + row = isl_mat_rows(node->sched) - 1; + ls = isl_local_space_from_space(isl_space_copy(node->space)); + aff = extract_schedule_row(ls, node, row); + return isl_map_from_aff(aff); +} + +/* Is the conditional validity dependence in the edge with index "edge_index" + * violated by the latest (i.e., final) row of the schedule? + * That is, is i scheduled after j + * for any conditional validity dependence i -> j? + */ +static int is_violated(struct isl_sched_graph *graph, int edge_index) +{ + isl_map *src_sched, *dst_sched, *map; + struct isl_sched_edge *edge = &graph->edge[edge_index]; + int empty; + + src_sched = final_row(edge->src); + dst_sched = final_row(edge->dst); + map = isl_map_copy(edge->map); + map = isl_map_apply_domain(map, src_sched); + map = isl_map_apply_range(map, dst_sched); + map = isl_map_order_gt(map, isl_dim_in, 0, isl_dim_out, 0); + empty = isl_map_is_empty(map); + isl_map_free(map); + + if (empty < 0) + return -1; + + return !empty; +} + +/* Does "graph" have any satisfied condition edges that + * are adjacent to the conditional validity constraint with + * domain "conditional_source" and range "conditional_sink"? + * + * A satisfied condition is one that is not local. + * If a condition was forced to be local already (i.e., marked as local) + * then there is no need to check if it is in fact local. + * + * Additionally, mark all adjacent condition edges found as local. + */ +static int has_adjacent_true_conditions(struct isl_sched_graph *graph, + __isl_keep isl_union_set *conditional_source, + __isl_keep isl_union_set *conditional_sink) +{ + int i; + int any = 0; + + for (i = 0; i < graph->n_edge; ++i) { + int adjacent, local; + isl_union_map *condition; + + if (!is_condition(&graph->edge[i])) + continue; + if (is_local(&graph->edge[i])) + continue; + + condition = graph->edge[i].tagged_condition; + adjacent = domain_intersects(condition, conditional_sink); + if (adjacent >= 0 && !adjacent) + adjacent = range_intersects(condition, + conditional_source); + if (adjacent < 0) + return -1; + if (!adjacent) + continue; + + set_local(&graph->edge[i]); + + local = is_condition_false(&graph->edge[i]); + if (local < 0) + return -1; + if (!local) + any = 1; + } + + return any; +} + +/* Are there any violated conditional validity dependences with + * adjacent condition dependences that are not local with respect + * to the current schedule? + * That is, is the conditional validity constraint violated? + * + * Additionally, mark all those adjacent condition dependences as local. + * We also mark those adjacent condition dependences that were not marked + * as local before, but just happened to be local already. This ensures + * that they remain local if the schedule is recomputed. + * + * We first collect domain and range of all violated conditional validity + * dependences and then check if there are any adjacent non-local + * condition dependences. + */ +static int has_violated_conditional_constraint(isl_ctx *ctx, + struct isl_sched_graph *graph) +{ + int i; + int any = 0; + isl_union_set *source, *sink; + + source = isl_union_set_empty(isl_space_params_alloc(ctx, 0)); + sink = isl_union_set_empty(isl_space_params_alloc(ctx, 0)); + for (i = 0; i < graph->n_edge; ++i) { + isl_union_set *uset; + isl_union_map *umap; + int violated; + + if (!is_conditional_validity(&graph->edge[i])) + continue; + + violated = is_violated(graph, i); + if (violated < 0) + goto error; + if (!violated) + continue; + + any = 1; + + umap = isl_union_map_copy(graph->edge[i].tagged_validity); + uset = isl_union_map_domain(umap); + source = isl_union_set_union(source, uset); + source = isl_union_set_coalesce(source); + + umap = isl_union_map_copy(graph->edge[i].tagged_validity); + uset = isl_union_map_range(umap); + sink = isl_union_set_union(sink, uset); + sink = isl_union_set_coalesce(sink); + } + + if (any) + any = has_adjacent_true_conditions(graph, source, sink); + + isl_union_set_free(source); + isl_union_set_free(sink); + return any; +error: + isl_union_set_free(source); + isl_union_set_free(sink); + return -1; +} + +/* Examine the current band (the rows between graph->band_start and + * graph->n_total_row), deciding whether to drop it or add it to "node" + * and then continue with the computation of the next band, if any. + * If "initialized" is set, then it may be assumed that compute_maxvar + * has been called on the current band. Otherwise, call + * compute_maxvar if and before carry_dependences gets called. + * + * The caller keeps looking for a new row as long as + * graph->n_row < graph->maxvar. If the latest attempt to find + * such a row failed (i.e., we still have graph->n_row < graph->maxvar), + * then we either + * - split between SCCs and start over (assuming we found an interesting + * pair of SCCs between which to split) + * - continue with the next band (assuming the current band has at least + * one row) + * - try to carry as many dependences as possible and continue with the next + * band + * In each case, we first insert a band node in the schedule tree + * if any rows have been computed. + * + * If the caller managed to complete the schedule, we insert a band node + * (if any schedule rows were computed) and we finish off by topologically + * sorting the statements based on the remaining dependences. + */ +static __isl_give isl_schedule_node *compute_schedule_finish_band( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph, + int initialized) +{ + int insert; + + if (!node) + return NULL; + + if (graph->n_row < graph->maxvar) { + isl_ctx *ctx; + int empty = graph->n_total_row == graph->band_start; + + ctx = isl_schedule_node_get_ctx(node); + if (!ctx->opt->schedule_maximize_band_depth && !empty) + return compute_next_band(node, graph, 1); + if (graph->src_scc >= 0) + return compute_split_schedule(node, graph); + if (!empty) + return compute_next_band(node, graph, 1); + if (!initialized && compute_maxvar(graph) < 0) + return isl_schedule_node_free(node); + return carry_dependences(node, graph); + } + + insert = graph->n_total_row > graph->band_start; + if (insert) { + node = insert_current_band(node, graph, 1); + node = isl_schedule_node_child(node, 0); + } + node = sort_statements(node, graph, initialized); + if (insert) + node = isl_schedule_node_parent(node); + + return node; +} + +/* Construct a band of schedule rows for a connected dependence graph. + * The caller is responsible for determining the strongly connected + * components and calling compute_maxvar first. + * + * We try to find a sequence of as many schedule rows as possible that result + * in non-negative dependence distances (independent of the previous rows + * in the sequence, i.e., such that the sequence is tilable), with as + * many of the initial rows as possible satisfying the coincidence constraints. + * The computation stops if we can't find any more rows or if we have found + * all the rows we wanted to find. + * + * If ctx->opt->schedule_outer_coincidence is set, then we force the + * outermost dimension to satisfy the coincidence constraints. If this + * turns out to be impossible, we fall back on the general scheme above + * and try to carry as many dependences as possible. + * + * If "graph" contains both condition and conditional validity dependences, + * then we need to check that that the conditional schedule constraint + * is satisfied, i.e., there are no violated conditional validity dependences + * that are adjacent to any non-local condition dependences. + * If there are, then we mark all those adjacent condition dependences + * as local and recompute the current band. Those dependences that + * are marked local will then be forced to be local. + * The initial computation is performed with no dependences marked as local. + * If we are lucky, then there will be no violated conditional validity + * dependences adjacent to any non-local condition dependences. + * Otherwise, we mark some additional condition dependences as local and + * recompute. We continue this process until there are no violations left or + * until we are no longer able to compute a schedule. + * Since there are only a finite number of dependences, + * there will only be a finite number of iterations. + */ +static isl_stat compute_schedule_wcc_band(isl_ctx *ctx, + struct isl_sched_graph *graph) +{ + int has_coincidence; + int use_coincidence; + int force_coincidence = 0; + int check_conditional; + + if (sort_sccs(graph) < 0) + return isl_stat_error; + + clear_local_edges(graph); + check_conditional = need_condition_check(graph); + has_coincidence = has_any_coincidence(graph); + + if (ctx->opt->schedule_outer_coincidence) + force_coincidence = 1; + + use_coincidence = has_coincidence; + while (graph->n_row < graph->maxvar) { + isl_vec *sol; + int violated; + int coincident; + + graph->src_scc = -1; + graph->dst_scc = -1; + + if (setup_lp(ctx, graph, use_coincidence) < 0) + return isl_stat_error; + sol = solve_lp(graph); + if (!sol) + return isl_stat_error; + if (sol->size == 0) { + int empty = graph->n_total_row == graph->band_start; + + isl_vec_free(sol); + if (use_coincidence && (!force_coincidence || !empty)) { + use_coincidence = 0; + continue; + } + return isl_stat_ok; + } + coincident = !has_coincidence || use_coincidence; + if (update_schedule(graph, sol, 1, coincident) < 0) + return isl_stat_error; + + if (!check_conditional) + continue; + violated = has_violated_conditional_constraint(ctx, graph); + if (violated < 0) + return isl_stat_error; + if (!violated) + continue; + if (reset_band(graph) < 0) + return isl_stat_error; + use_coincidence = has_coincidence; + } + + return isl_stat_ok; +} + +/* Compute a schedule for a connected dependence graph by considering + * the graph as a whole and return the updated schedule node. + * + * The actual schedule rows of the current band are computed by + * compute_schedule_wcc_band. compute_schedule_finish_band takes + * care of integrating the band into "node" and continuing + * the computation. + */ +static __isl_give isl_schedule_node *compute_schedule_wcc_whole( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph) +{ + isl_ctx *ctx; + + if (!node) + return NULL; + + ctx = isl_schedule_node_get_ctx(node); + if (compute_schedule_wcc_band(ctx, graph) < 0) + return isl_schedule_node_free(node); + + return compute_schedule_finish_band(node, graph, 1); +} + +/* Clustering information used by compute_schedule_wcc_clustering. + * + * "n" is the number of SCCs in the original dependence graph + * "scc" is an array of "n" elements, each representing an SCC + * of the original dependence graph. All entries in the same cluster + * have the same number of schedule rows. + * "scc_cluster" maps each SCC index to the cluster to which it belongs, + * where each cluster is represented by the index of the first SCC + * in the cluster. Initially, each SCC belongs to a cluster containing + * only that SCC. + * + * "scc_in_merge" is used by merge_clusters_along_edge to keep + * track of which SCCs need to be merged. + * + * "cluster" contains the merged clusters of SCCs after the clustering + * has completed. + * + * "scc_node" is a temporary data structure used inside copy_partial. + * For each SCC, it keeps track of the number of nodes in the SCC + * that have already been copied. + */ +struct isl_clustering { + int n; + struct isl_sched_graph *scc; + struct isl_sched_graph *cluster; + int *scc_cluster; + int *scc_node; + int *scc_in_merge; +}; + +/* Initialize the clustering data structure "c" from "graph". + * + * In particular, allocate memory, extract the SCCs from "graph" + * into c->scc, initialize scc_cluster and construct + * a band of schedule rows for each SCC. + * Within each SCC, there is only one SCC by definition. + * Each SCC initially belongs to a cluster containing only that SCC. + */ +static isl_stat clustering_init(isl_ctx *ctx, struct isl_clustering *c, + struct isl_sched_graph *graph) +{ + int i; + + c->n = graph->scc; + c->scc = isl_calloc_array(ctx, struct isl_sched_graph, c->n); + c->cluster = isl_calloc_array(ctx, struct isl_sched_graph, c->n); + c->scc_cluster = isl_calloc_array(ctx, int, c->n); + c->scc_node = isl_calloc_array(ctx, int, c->n); + c->scc_in_merge = isl_calloc_array(ctx, int, c->n); + if (!c->scc || !c->cluster || + !c->scc_cluster || !c->scc_node || !c->scc_in_merge) + return isl_stat_error; + + for (i = 0; i < c->n; ++i) { + if (extract_sub_graph(ctx, graph, &node_scc_exactly, + &edge_scc_exactly, i, &c->scc[i]) < 0) + return isl_stat_error; + c->scc[i].scc = 1; + if (compute_maxvar(&c->scc[i]) < 0) + return isl_stat_error; + if (compute_schedule_wcc_band(ctx, &c->scc[i]) < 0) + return isl_stat_error; + c->scc_cluster[i] = i; + } + + return isl_stat_ok; +} + +/* Free all memory allocated for "c". + */ +static void clustering_free(isl_ctx *ctx, struct isl_clustering *c) +{ + int i; + + if (c->scc) + for (i = 0; i < c->n; ++i) + graph_free(ctx, &c->scc[i]); + free(c->scc); + if (c->cluster) + for (i = 0; i < c->n; ++i) + graph_free(ctx, &c->cluster[i]); + free(c->cluster); + free(c->scc_cluster); + free(c->scc_node); + free(c->scc_in_merge); +} + +/* Should we refrain from merging the cluster in "graph" with + * any other cluster? + * In particular, is its current schedule band empty and incomplete. + */ +static int bad_cluster(struct isl_sched_graph *graph) +{ + return graph->n_row < graph->maxvar && + graph->n_total_row == graph->band_start; +} + +/* Return the index of an edge in "graph" that can be used to merge + * two clusters in "c". + * Return graph->n_edge if no such edge can be found. + * Return -1 on error. + * + * In particular, return a proximity edge between two clusters + * that is not marked "no_merge" and such that neither of the + * two clusters has an incomplete, empty band. + * + * If there are multiple such edges, then try and find the most + * appropriate edge to use for merging. In particular, pick the edge + * with the greatest weight. If there are multiple of those, + * then pick one with the shortest distance between + * the two cluster representatives. + */ +static int find_proximity(struct isl_sched_graph *graph, + struct isl_clustering *c) +{ + int i, best = graph->n_edge, best_dist, best_weight; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge = &graph->edge[i]; + int dist, weight; + + if (!is_proximity(edge)) + continue; + if (edge->no_merge) + continue; + if (bad_cluster(&c->scc[edge->src->scc]) || + bad_cluster(&c->scc[edge->dst->scc])) + continue; + dist = c->scc_cluster[edge->dst->scc] - + c->scc_cluster[edge->src->scc]; + if (dist == 0) + continue; + weight = edge->weight; + if (best < graph->n_edge) { + if (best_weight > weight) + continue; + if (best_weight == weight && best_dist <= dist) + continue; + } + best = i; + best_dist = dist; + best_weight = weight; + } + + return best; +} + +/* Internal data structure used in mark_merge_sccs. + * + * "graph" is the dependence graph in which a strongly connected + * component is constructed. + * "scc_cluster" maps each SCC index to the cluster to which it belongs. + * "src" and "dst" are the indices of the nodes that are being merged. + */ +struct isl_mark_merge_sccs_data { + struct isl_sched_graph *graph; + int *scc_cluster; + int src; + int dst; +}; + +/* Check whether the cluster containing node "i" depends on the cluster + * containing node "j". If "i" and "j" belong to the same cluster, + * then they are taken to depend on each other to ensure that + * the resulting strongly connected component consists of complete + * clusters. Furthermore, if "i" and "j" are the two nodes that + * are being merged, then they are taken to depend on each other as well. + * Otherwise, check if there is a (conditional) validity dependence + * from node[j] to node[i], forcing node[i] to follow node[j]. + */ +static isl_bool cluster_follows(int i, int j, void *user) +{ + struct isl_mark_merge_sccs_data *data = user; + struct isl_sched_graph *graph = data->graph; + int *scc_cluster = data->scc_cluster; + + if (data->src == i && data->dst == j) + return isl_bool_true; + if (data->src == j && data->dst == i) + return isl_bool_true; + if (scc_cluster[graph->node[i].scc] == scc_cluster[graph->node[j].scc]) + return isl_bool_true; + + return graph_has_validity_edge(graph, &graph->node[j], &graph->node[i]); +} + +/* Mark all SCCs that belong to either of the two clusters in "c" + * connected by the edge in "graph" with index "edge", or to any + * of the intermediate clusters. + * The marking is recorded in c->scc_in_merge. + * + * The given edge has been selected for merging two clusters, + * meaning that there is at least a proximity edge between the two nodes. + * However, there may also be (indirect) validity dependences + * between the two nodes. When merging the two clusters, all clusters + * containing one or more of the intermediate nodes along the + * indirect validity dependences need to be merged in as well. + * + * First collect all such nodes by computing the strongly connected + * component (SCC) containing the two nodes connected by the edge, where + * the two nodes are considered to depend on each other to make + * sure they end up in the same SCC. Similarly, each node is considered + * to depend on every other node in the same cluster to ensure + * that the SCC consists of complete clusters. + * + * Then the original SCCs that contain any of these nodes are marked + * in c->scc_in_merge. + */ +static isl_stat mark_merge_sccs(isl_ctx *ctx, struct isl_sched_graph *graph, + int edge, struct isl_clustering *c) +{ + struct isl_mark_merge_sccs_data data; + struct isl_tarjan_graph *g; + int i; + + for (i = 0; i < c->n; ++i) + c->scc_in_merge[i] = 0; + + data.graph = graph; + data.scc_cluster = c->scc_cluster; + data.src = graph->edge[edge].src - graph->node; + data.dst = graph->edge[edge].dst - graph->node; + + g = isl_tarjan_graph_component(ctx, graph->n, data.dst, + &cluster_follows, &data); + if (!g) + goto error; + + i = g->op; + if (i < 3) + isl_die(ctx, isl_error_internal, + "expecting at least two nodes in component", + goto error); + if (g->order[--i] != -1) + isl_die(ctx, isl_error_internal, + "expecting end of component marker", goto error); + + for (--i; i >= 0 && g->order[i] != -1; --i) { + int scc = graph->node[g->order[i]].scc; + c->scc_in_merge[scc] = 1; + } + + isl_tarjan_graph_free(g); + return isl_stat_ok; +error: + isl_tarjan_graph_free(g); + return isl_stat_error; +} + +/* Construct the identifier "cluster_i". + */ +static __isl_give isl_id *cluster_id(isl_ctx *ctx, int i) +{ + char name[40]; + + snprintf(name, sizeof(name), "cluster_%d", i); + return isl_id_alloc(ctx, name, NULL); +} + +/* Construct the space of the cluster with index "i" containing + * the strongly connected component "scc". + * + * In particular, construct a space called cluster_i with dimension equal + * to the number of schedule rows in the current band of "scc". + */ +static __isl_give isl_space *cluster_space(struct isl_sched_graph *scc, int i) +{ + int nvar; + isl_space *space; + isl_id *id; + + nvar = scc->n_total_row - scc->band_start; + space = isl_space_copy(scc->node[0].space); + space = isl_space_params(space); + space = isl_space_set_from_params(space); + space = isl_space_add_dims(space, isl_dim_set, nvar); + id = cluster_id(isl_space_get_ctx(space), i); + space = isl_space_set_tuple_id(space, isl_dim_set, id); + + return space; +} + +/* Collect the domain of the graph for merging clusters. + * + * In particular, for each cluster with first SCC "i", construct + * a set in the space called cluster_i with dimension equal + * to the number of schedule rows in the current band of the cluster. + */ +static __isl_give isl_union_set *collect_domain(isl_ctx *ctx, + struct isl_sched_graph *graph, struct isl_clustering *c) +{ + int i; + isl_space *space; + isl_union_set *domain; + + space = isl_space_params_alloc(ctx, 0); + domain = isl_union_set_empty(space); + + for (i = 0; i < graph->scc; ++i) { + isl_space *space; + + if (!c->scc_in_merge[i]) + continue; + if (c->scc_cluster[i] != i) + continue; + space = cluster_space(&c->scc[i], i); + domain = isl_union_set_add_set(domain, isl_set_universe(space)); + } + + return domain; +} + +/* Construct a map from the original instances to the corresponding + * cluster instance in the current bands of the clusters in "c". + */ +static __isl_give isl_union_map *collect_cluster_map(isl_ctx *ctx, + struct isl_sched_graph *graph, struct isl_clustering *c) +{ + int i, j; + isl_space *space; + isl_union_map *cluster_map; + + space = isl_space_params_alloc(ctx, 0); + cluster_map = isl_union_map_empty(space); + for (i = 0; i < graph->scc; ++i) { + int start, n; + isl_id *id; + + if (!c->scc_in_merge[i]) + continue; + + id = cluster_id(ctx, c->scc_cluster[i]); + start = c->scc[i].band_start; + n = c->scc[i].n_total_row - start; + for (j = 0; j < c->scc[i].n; ++j) { + isl_multi_aff *ma; + isl_map *map; + struct isl_sched_node *node = &c->scc[i].node[j]; + + ma = node_extract_partial_schedule_multi_aff(node, + start, n); + ma = isl_multi_aff_set_tuple_id(ma, isl_dim_out, + isl_id_copy(id)); + map = isl_map_from_multi_aff(ma); + cluster_map = isl_union_map_add_map(cluster_map, map); + } + isl_id_free(id); + } + + return cluster_map; +} + +/* Add "umap" to the schedule constraints "sc" of all types of "edge" + * that are not isl_edge_condition or isl_edge_conditional_validity. + */ +static __isl_give isl_schedule_constraints *add_non_conditional_constraints( + struct isl_sched_edge *edge, __isl_keep isl_union_map *umap, + __isl_take isl_schedule_constraints *sc) +{ + enum isl_edge_type t; + + if (!sc) + return NULL; + + for (t = isl_edge_first; t <= isl_edge_last; ++t) { + if (t == isl_edge_condition || + t == isl_edge_conditional_validity) + continue; + if (!is_type(edge, t)) + continue; + sc->constraint[t] = isl_union_map_union(sc->constraint[t], + isl_union_map_copy(umap)); + if (!sc->constraint[t]) + return isl_schedule_constraints_free(sc); + } + + return sc; +} + +/* Add schedule constraints of types isl_edge_condition and + * isl_edge_conditional_validity to "sc" by applying "umap" to + * the domains of the wrapped relations in domain and range + * of the corresponding tagged constraints of "edge". + */ +static __isl_give isl_schedule_constraints *add_conditional_constraints( + struct isl_sched_edge *edge, __isl_keep isl_union_map *umap, + __isl_take isl_schedule_constraints *sc) +{ + enum isl_edge_type t; + isl_union_map *tagged; + + for (t = isl_edge_condition; t <= isl_edge_conditional_validity; ++t) { + if (!is_type(edge, t)) + continue; + if (t == isl_edge_condition) + tagged = isl_union_map_copy(edge->tagged_condition); + else + tagged = isl_union_map_copy(edge->tagged_validity); + tagged = isl_union_map_zip(tagged); + tagged = isl_union_map_apply_domain(tagged, + isl_union_map_copy(umap)); + tagged = isl_union_map_zip(tagged); + sc->constraint[t] = isl_union_map_union(sc->constraint[t], + tagged); + if (!sc->constraint[t]) + return isl_schedule_constraints_free(sc); + } + + return sc; +} + +/* Given a mapping "cluster_map" from the original instances to + * the cluster instances, add schedule constraints on the clusters + * to "sc" corresponding to the original constraints represented by "edge". + * + * For non-tagged dependence constraints, the cluster constraints + * are obtained by applying "cluster_map" to the edge->map. + * + * For tagged dependence constraints, "cluster_map" needs to be applied + * to the domains of the wrapped relations in domain and range + * of the tagged dependence constraints. Pick out the mappings + * from these domains from "cluster_map" and construct their product. + * This mapping can then be applied to the pair of domains. + */ +static __isl_give isl_schedule_constraints *collect_edge_constraints( + struct isl_sched_edge *edge, __isl_keep isl_union_map *cluster_map, + __isl_take isl_schedule_constraints *sc) +{ + isl_union_map *umap; + isl_space *space; + isl_union_set *uset; + isl_union_map *umap1, *umap2; + + if (!sc) + return NULL; + + umap = isl_union_map_from_map(isl_map_copy(edge->map)); + umap = isl_union_map_apply_domain(umap, + isl_union_map_copy(cluster_map)); + umap = isl_union_map_apply_range(umap, + isl_union_map_copy(cluster_map)); + sc = add_non_conditional_constraints(edge, umap, sc); + isl_union_map_free(umap); + + if (!sc || (!is_condition(edge) && !is_conditional_validity(edge))) + return sc; + + space = isl_space_domain(isl_map_get_space(edge->map)); + uset = isl_union_set_from_set(isl_set_universe(space)); + umap1 = isl_union_map_copy(cluster_map); + umap1 = isl_union_map_intersect_domain(umap1, uset); + space = isl_space_range(isl_map_get_space(edge->map)); + uset = isl_union_set_from_set(isl_set_universe(space)); + umap2 = isl_union_map_copy(cluster_map); + umap2 = isl_union_map_intersect_domain(umap2, uset); + umap = isl_union_map_product(umap1, umap2); + + sc = add_conditional_constraints(edge, umap, sc); + + isl_union_map_free(umap); + return sc; +} + +/* Given a mapping "cluster_map" from the original instances to + * the cluster instances, add schedule constraints on the clusters + * to "sc" corresponding to all edges in "graph" between nodes that + * belong to SCCs that are marked for merging in "scc_in_merge". + */ +static __isl_give isl_schedule_constraints *collect_constraints( + struct isl_sched_graph *graph, int *scc_in_merge, + __isl_keep isl_union_map *cluster_map, + __isl_take isl_schedule_constraints *sc) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge = &graph->edge[i]; + + if (!scc_in_merge[edge->src->scc]) + continue; + if (!scc_in_merge[edge->dst->scc]) + continue; + sc = collect_edge_constraints(edge, cluster_map, sc); + } + + return sc; +} + +/* Construct a dependence graph for scheduling clusters with respect + * to each other and store the result in "merge_graph". + * In particular, the nodes of the graph correspond to the schedule + * dimensions of the current bands of those clusters that have been + * marked for merging in "c". + * + * First construct an isl_schedule_constraints object for this domain + * by transforming the edges in "graph" to the domain. + * Then initialize a dependence graph for scheduling from these + * constraints. + */ +static isl_stat init_merge_graph(isl_ctx *ctx, struct isl_sched_graph *graph, + struct isl_clustering *c, struct isl_sched_graph *merge_graph) +{ + isl_union_set *domain; + isl_union_map *cluster_map; + isl_schedule_constraints *sc; + isl_stat r; + + domain = collect_domain(ctx, graph, c); + sc = isl_schedule_constraints_on_domain(domain); + if (!sc) + return isl_stat_error; + cluster_map = collect_cluster_map(ctx, graph, c); + sc = collect_constraints(graph, c->scc_in_merge, cluster_map, sc); + isl_union_map_free(cluster_map); + + r = graph_init(merge_graph, sc); + + isl_schedule_constraints_free(sc); + + return r; +} + +/* Compute the maximal number of remaining schedule rows that still need + * to be computed for the nodes that belong to clusters with the maximal + * dimension for the current band (i.e., the band that is to be merged). + * Only clusters that are about to be merged are considered. + * "maxvar" is the maximal dimension for the current band. + * "c" contains information about the clusters. + * + * Return the maximal number of remaining schedule rows or -1 on error. + */ +static int compute_maxvar_max_slack(int maxvar, struct isl_clustering *c) +{ + int i, j; + int max_slack; + + max_slack = 0; + for (i = 0; i < c->n; ++i) { + int nvar; + struct isl_sched_graph *scc; + + if (!c->scc_in_merge[i]) + continue; + scc = &c->scc[i]; + nvar = scc->n_total_row - scc->band_start; + if (nvar != maxvar) + continue; + for (j = 0; j < scc->n; ++j) { + struct isl_sched_node *node = &scc->node[j]; + int slack; + + if (node_update_cmap(node) < 0) + return -1; + slack = node->nvar - node->rank; + if (slack > max_slack) + max_slack = slack; + } + } + + return max_slack; +} + +/* If there are any clusters where the dimension of the current band + * (i.e., the band that is to be merged) is smaller than "maxvar" and + * if there are any nodes in such a cluster where the number + * of remaining schedule rows that still need to be computed + * is greater than "max_slack", then return the smallest current band + * dimension of all these clusters. Otherwise return the original value + * of "maxvar". Return -1 in case of any error. + * Only clusters that are about to be merged are considered. + * "c" contains information about the clusters. + */ +static int limit_maxvar_to_slack(int maxvar, int max_slack, + struct isl_clustering *c) +{ + int i, j; + + for (i = 0; i < c->n; ++i) { + int nvar; + struct isl_sched_graph *scc; + + if (!c->scc_in_merge[i]) + continue; + scc = &c->scc[i]; + nvar = scc->n_total_row - scc->band_start; + if (nvar >= maxvar) + continue; + for (j = 0; j < scc->n; ++j) { + struct isl_sched_node *node = &scc->node[j]; + int slack; + + if (node_update_cmap(node) < 0) + return -1; + slack = node->nvar - node->rank; + if (slack > max_slack) { + maxvar = nvar; + break; + } + } + } + + return maxvar; +} + +/* Adjust merge_graph->maxvar based on the number of remaining schedule rows + * that still need to be computed. In particular, if there is a node + * in a cluster where the dimension of the current band is smaller + * than merge_graph->maxvar, but the number of remaining schedule rows + * is greater than that of any node in a cluster with the maximal + * dimension for the current band (i.e., merge_graph->maxvar), + * then adjust merge_graph->maxvar to the (smallest) current band dimension + * of those clusters. Without this adjustment, the total number of + * schedule dimensions would be increased, resulting in a skewed view + * of the number of coincident dimensions. + * "c" contains information about the clusters. + * + * If the maximize_band_depth option is set and merge_graph->maxvar is reduced, + * then there is no point in attempting any merge since it will be rejected + * anyway. Set merge_graph->maxvar to zero in such cases. + */ +static isl_stat adjust_maxvar_to_slack(isl_ctx *ctx, + struct isl_sched_graph *merge_graph, struct isl_clustering *c) +{ + int max_slack, maxvar; + + max_slack = compute_maxvar_max_slack(merge_graph->maxvar, c); + if (max_slack < 0) + return isl_stat_error; + maxvar = limit_maxvar_to_slack(merge_graph->maxvar, max_slack, c); + if (maxvar < 0) + return isl_stat_error; + + if (maxvar < merge_graph->maxvar) { + if (isl_options_get_schedule_maximize_band_depth(ctx)) + merge_graph->maxvar = 0; + else + merge_graph->maxvar = maxvar; + } + + return isl_stat_ok; +} + +/* Return the number of coincident dimensions in the current band of "graph", + * where the nodes of "graph" are assumed to be scheduled by a single band. + */ +static int get_n_coincident(struct isl_sched_graph *graph) +{ + int i; + + for (i = graph->band_start; i < graph->n_total_row; ++i) + if (!graph->node[0].coincident[i]) + break; + + return i - graph->band_start; +} + +/* Should the clusters be merged based on the cluster schedule + * in the current (and only) band of "merge_graph", given that + * coincidence should be maximized? + * + * If the number of coincident schedule dimensions in the merged band + * would be less than the maximal number of coincident schedule dimensions + * in any of the merged clusters, then the clusters should not be merged. + */ +static isl_bool ok_to_merge_coincident(struct isl_clustering *c, + struct isl_sched_graph *merge_graph) +{ + int i; + int n_coincident; + int max_coincident; + + max_coincident = 0; + for (i = 0; i < c->n; ++i) { + if (!c->scc_in_merge[i]) + continue; + n_coincident = get_n_coincident(&c->scc[i]); + if (n_coincident > max_coincident) + max_coincident = n_coincident; + } + + n_coincident = get_n_coincident(merge_graph); + + return n_coincident >= max_coincident; +} + +/* Return the transformation on "node" expressed by the current (and only) + * band of "merge_graph" applied to the clusters in "c". + * + * First find the representation of "node" in its SCC in "c" and + * extract the transformation expressed by the current band. + * Then extract the transformation applied by "merge_graph" + * to the cluster to which this SCC belongs. + * Combine the two to obtain the complete transformation on the node. + * + * Note that the range of the first transformation is an anonymous space, + * while the domain of the second is named "cluster_X". The range + * of the former therefore needs to be adjusted before the two + * can be combined. + */ +static __isl_give isl_map *extract_node_transformation(isl_ctx *ctx, + struct isl_sched_node *node, struct isl_clustering *c, + struct isl_sched_graph *merge_graph) +{ + struct isl_sched_node *scc_node, *cluster_node; + int start, n; + isl_id *id; + isl_space *space; + isl_multi_aff *ma, *ma2; + + scc_node = graph_find_node(ctx, &c->scc[node->scc], node->space); + start = c->scc[node->scc].band_start; + n = c->scc[node->scc].n_total_row - start; + ma = node_extract_partial_schedule_multi_aff(scc_node, start, n); + space = cluster_space(&c->scc[node->scc], c->scc_cluster[node->scc]); + cluster_node = graph_find_node(ctx, merge_graph, space); + if (space && !cluster_node) + isl_die(ctx, isl_error_internal, "unable to find cluster", + space = isl_space_free(space)); + id = isl_space_get_tuple_id(space, isl_dim_set); + ma = isl_multi_aff_set_tuple_id(ma, isl_dim_out, id); + isl_space_free(space); + n = merge_graph->n_total_row; + ma2 = node_extract_partial_schedule_multi_aff(cluster_node, 0, n); + ma = isl_multi_aff_pullback_multi_aff(ma2, ma); + + return isl_map_from_multi_aff(ma); +} + +/* Give a set of distances "set", are they bounded by a small constant + * in direction "pos"? + * In practice, check if they are bounded by 2 by checking that there + * are no elements with a value greater than or equal to 3 or + * smaller than or equal to -3. + */ +static isl_bool distance_is_bounded(__isl_keep isl_set *set, int pos) +{ + isl_bool bounded; + isl_set *test; + + if (!set) + return isl_bool_error; + + test = isl_set_copy(set); + test = isl_set_lower_bound_si(test, isl_dim_set, pos, 3); + bounded = isl_set_is_empty(test); + isl_set_free(test); + + if (bounded < 0 || !bounded) + return bounded; + + test = isl_set_copy(set); + test = isl_set_upper_bound_si(test, isl_dim_set, pos, -3); + bounded = isl_set_is_empty(test); + isl_set_free(test); + + return bounded; +} + +/* Does the set "set" have a fixed (but possible parametric) value + * at dimension "pos"? + */ +static isl_bool has_single_value(__isl_keep isl_set *set, int pos) +{ + int n; + isl_bool single; + + if (!set) + return isl_bool_error; + set = isl_set_copy(set); + n = isl_set_dim(set, isl_dim_set); + set = isl_set_project_out(set, isl_dim_set, pos + 1, n - (pos + 1)); + set = isl_set_project_out(set, isl_dim_set, 0, pos); + single = isl_set_is_singleton(set); + isl_set_free(set); + + return single; +} + +/* Does "map" have a fixed (but possible parametric) value + * at dimension "pos" of either its domain or its range? + */ +static isl_bool has_singular_src_or_dst(__isl_keep isl_map *map, int pos) +{ + isl_set *set; + isl_bool single; + + set = isl_map_domain(isl_map_copy(map)); + single = has_single_value(set, pos); + isl_set_free(set); + + if (single < 0 || single) + return single; + + set = isl_map_range(isl_map_copy(map)); + single = has_single_value(set, pos); + isl_set_free(set); + + return single; +} + +/* Does the edge "edge" from "graph" have bounded dependence distances + * in the merged graph "merge_graph" of a selection of clusters in "c"? + * + * Extract the complete transformations of the source and destination + * nodes of the edge, apply them to the edge constraints and + * compute the differences. Finally, check if these differences are bounded + * in each direction. + * + * If the dimension of the band is greater than the number of + * dimensions that can be expected to be optimized by the edge + * (based on its weight), then also allow the differences to be unbounded + * in the remaining dimensions, but only if either the source or + * the destination has a fixed value in that direction. + * This allows a statement that produces values that are used by + * several instances of another statement to be merged with that + * other statement. + * However, merging such clusters will introduce an inherently + * large proximity distance inside the merged cluster, meaning + * that proximity distances will no longer be optimized in + * subsequent merges. These merges are therefore only allowed + * after all other possible merges have been tried. + * The first time such a merge is encountered, the weight of the edge + * is replaced by a negative weight. The second time (i.e., after + * all merges over edges with a non-negative weight have been tried), + * the merge is allowed. + */ +static isl_bool has_bounded_distances(isl_ctx *ctx, struct isl_sched_edge *edge, + struct isl_sched_graph *graph, struct isl_clustering *c, + struct isl_sched_graph *merge_graph) +{ + int i, n, n_slack; + isl_bool bounded; + isl_map *map, *t; + isl_set *dist; + + map = isl_map_copy(edge->map); + t = extract_node_transformation(ctx, edge->src, c, merge_graph); + map = isl_map_apply_domain(map, t); + t = extract_node_transformation(ctx, edge->dst, c, merge_graph); + map = isl_map_apply_range(map, t); + dist = isl_map_deltas(isl_map_copy(map)); + + bounded = isl_bool_true; + n = isl_set_dim(dist, isl_dim_set); + n_slack = n - edge->weight; + if (edge->weight < 0) + n_slack -= graph->max_weight + 1; + for (i = 0; i < n; ++i) { + isl_bool bounded_i, singular_i; + + bounded_i = distance_is_bounded(dist, i); + if (bounded_i < 0) + goto error; + if (bounded_i) + continue; + if (edge->weight >= 0) + bounded = isl_bool_false; + n_slack--; + if (n_slack < 0) + break; + singular_i = has_singular_src_or_dst(map, i); + if (singular_i < 0) + goto error; + if (singular_i) + continue; + bounded = isl_bool_false; + break; + } + if (!bounded && i >= n && edge->weight >= 0) + edge->weight -= graph->max_weight + 1; + isl_map_free(map); + isl_set_free(dist); + + return bounded; +error: + isl_map_free(map); + isl_set_free(dist); + return isl_bool_error; +} + +/* Should the clusters be merged based on the cluster schedule + * in the current (and only) band of "merge_graph"? + * "graph" is the original dependence graph, while "c" records + * which SCCs are involved in the latest merge. + * + * In particular, is there at least one proximity constraint + * that is optimized by the merge? + * + * A proximity constraint is considered to be optimized + * if the dependence distances are small. + */ +static isl_bool ok_to_merge_proximity(isl_ctx *ctx, + struct isl_sched_graph *graph, struct isl_clustering *c, + struct isl_sched_graph *merge_graph) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge = &graph->edge[i]; + isl_bool bounded; + + if (!is_proximity(edge)) + continue; + if (!c->scc_in_merge[edge->src->scc]) + continue; + if (!c->scc_in_merge[edge->dst->scc]) + continue; + if (c->scc_cluster[edge->dst->scc] == + c->scc_cluster[edge->src->scc]) + continue; + bounded = has_bounded_distances(ctx, edge, graph, c, + merge_graph); + if (bounded < 0 || bounded) + return bounded; + } + + return isl_bool_false; +} + +/* Should the clusters be merged based on the cluster schedule + * in the current (and only) band of "merge_graph"? + * "graph" is the original dependence graph, while "c" records + * which SCCs are involved in the latest merge. + * + * If the current band is empty, then the clusters should not be merged. + * + * If the band depth should be maximized and the merge schedule + * is incomplete (meaning that the dimension of some of the schedule + * bands in the original schedule will be reduced), then the clusters + * should not be merged. + * + * If the schedule_maximize_coincidence option is set, then check that + * the number of coincident schedule dimensions is not reduced. + * + * Finally, only allow the merge if at least one proximity + * constraint is optimized. + */ +static isl_bool ok_to_merge(isl_ctx *ctx, struct isl_sched_graph *graph, + struct isl_clustering *c, struct isl_sched_graph *merge_graph) +{ + if (merge_graph->n_total_row == merge_graph->band_start) + return isl_bool_false; + + if (isl_options_get_schedule_maximize_band_depth(ctx) && + merge_graph->n_total_row < merge_graph->maxvar) + return isl_bool_false; + + if (isl_options_get_schedule_maximize_coincidence(ctx)) { + isl_bool ok; + + ok = ok_to_merge_coincident(c, merge_graph); + if (ok < 0 || !ok) + return ok; + } + + return ok_to_merge_proximity(ctx, graph, c, merge_graph); +} + +/* Apply the schedule in "t_node" to the "n" rows starting at "first" + * of the schedule in "node" and return the result. + * + * That is, essentially compute + * + * T * N(first:first+n-1) + * + * taking into account the constant term and the parameter coefficients + * in "t_node". + */ +static __isl_give isl_mat *node_transformation(isl_ctx *ctx, + struct isl_sched_node *t_node, struct isl_sched_node *node, + int first, int n) +{ + int i, j; + isl_mat *t; + int n_row, n_col, n_param, n_var; + + n_param = node->nparam; + n_var = node->nvar; + n_row = isl_mat_rows(t_node->sched); + n_col = isl_mat_cols(node->sched); + t = isl_mat_alloc(ctx, n_row, n_col); + if (!t) + return NULL; + for (i = 0; i < n_row; ++i) { + isl_seq_cpy(t->row[i], t_node->sched->row[i], 1 + n_param); + isl_seq_clr(t->row[i] + 1 + n_param, n_var); + for (j = 0; j < n; ++j) + isl_seq_addmul(t->row[i], + t_node->sched->row[i][1 + n_param + j], + node->sched->row[first + j], + 1 + n_param + n_var); + } + return t; +} + +/* Apply the cluster schedule in "t_node" to the current band + * schedule of the nodes in "graph". + * + * In particular, replace the rows starting at band_start + * by the result of applying the cluster schedule in "t_node" + * to the original rows. + * + * The coincidence of the schedule is determined by the coincidence + * of the cluster schedule. + */ +static isl_stat transform(isl_ctx *ctx, struct isl_sched_graph *graph, + struct isl_sched_node *t_node) +{ + int i, j; + int n_new; + int start, n; + + start = graph->band_start; + n = graph->n_total_row - start; + + n_new = isl_mat_rows(t_node->sched); + for (i = 0; i < graph->n; ++i) { + struct isl_sched_node *node = &graph->node[i]; + isl_mat *t; + + t = node_transformation(ctx, t_node, node, start, n); + node->sched = isl_mat_drop_rows(node->sched, start, n); + node->sched = isl_mat_concat(node->sched, t); + node->sched_map = isl_map_free(node->sched_map); + if (!node->sched) + return isl_stat_error; + for (j = 0; j < n_new; ++j) + node->coincident[start + j] = t_node->coincident[j]; + } + graph->n_total_row -= n; + graph->n_row -= n; + graph->n_total_row += n_new; + graph->n_row += n_new; + + return isl_stat_ok; +} + +/* Merge the clusters marked for merging in "c" into a single + * cluster using the cluster schedule in the current band of "merge_graph". + * The representative SCC for the new cluster is the SCC with + * the smallest index. + * + * The current band schedule of each SCC in the new cluster is obtained + * by applying the schedule of the corresponding original cluster + * to the original band schedule. + * All SCCs in the new cluster have the same number of schedule rows. + */ +static isl_stat merge(isl_ctx *ctx, struct isl_clustering *c, + struct isl_sched_graph *merge_graph) +{ + int i; + int cluster = -1; + isl_space *space; + + for (i = 0; i < c->n; ++i) { + struct isl_sched_node *node; + + if (!c->scc_in_merge[i]) + continue; + if (cluster < 0) + cluster = i; + space = cluster_space(&c->scc[i], c->scc_cluster[i]); + if (!space) + return isl_stat_error; + node = graph_find_node(ctx, merge_graph, space); + isl_space_free(space); + if (!node) + isl_die(ctx, isl_error_internal, + "unable to find cluster", + return isl_stat_error); + if (transform(ctx, &c->scc[i], node) < 0) + return isl_stat_error; + c->scc_cluster[i] = cluster; + } + + return isl_stat_ok; +} + +/* Try and merge the clusters of SCCs marked in c->scc_in_merge + * by scheduling the current cluster bands with respect to each other. + * + * Construct a dependence graph with a space for each cluster and + * with the coordinates of each space corresponding to the schedule + * dimensions of the current band of that cluster. + * Construct a cluster schedule in this cluster dependence graph and + * apply it to the current cluster bands if it is applicable + * according to ok_to_merge. + * + * If the number of remaining schedule dimensions in a cluster + * with a non-maximal current schedule dimension is greater than + * the number of remaining schedule dimensions in clusters + * with a maximal current schedule dimension, then restrict + * the number of rows to be computed in the cluster schedule + * to the minimal such non-maximal current schedule dimension. + * Do this by adjusting merge_graph.maxvar. + * + * Return isl_bool_true if the clusters have effectively been merged + * into a single cluster. + * + * Note that since the standard scheduling algorithm minimizes the maximal + * distance over proximity constraints, the proximity constraints between + * the merged clusters may not be optimized any further than what is + * sufficient to bring the distances within the limits of the internal + * proximity constraints inside the individual clusters. + * It may therefore make sense to perform an additional translation step + * to bring the clusters closer to each other, while maintaining + * the linear part of the merging schedule found using the standard + * scheduling algorithm. + */ +static isl_bool try_merge(isl_ctx *ctx, struct isl_sched_graph *graph, + struct isl_clustering *c) +{ + struct isl_sched_graph merge_graph = { 0 }; + isl_bool merged; + + if (init_merge_graph(ctx, graph, c, &merge_graph) < 0) + goto error; + + if (compute_maxvar(&merge_graph) < 0) + goto error; + if (adjust_maxvar_to_slack(ctx, &merge_graph,c) < 0) + goto error; + if (compute_schedule_wcc_band(ctx, &merge_graph) < 0) + goto error; + merged = ok_to_merge(ctx, graph, c, &merge_graph); + if (merged && merge(ctx, c, &merge_graph) < 0) + goto error; + + graph_free(ctx, &merge_graph); + return merged; +error: + graph_free(ctx, &merge_graph); + return isl_bool_error; +} + +/* Is there any edge marked "no_merge" between two SCCs that are + * about to be merged (i.e., that are set in "scc_in_merge")? + * "merge_edge" is the proximity edge along which the clusters of SCCs + * are going to be merged. + * + * If there is any edge between two SCCs with a negative weight, + * while the weight of "merge_edge" is non-negative, then this + * means that the edge was postponed. "merge_edge" should then + * also be postponed since merging along the edge with negative weight should + * be postponed until all edges with non-negative weight have been tried. + * Replace the weight of "merge_edge" by a negative weight as well and + * tell the caller not to attempt a merge. + */ +static int any_no_merge(struct isl_sched_graph *graph, int *scc_in_merge, + struct isl_sched_edge *merge_edge) +{ + int i; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge = &graph->edge[i]; + + if (!scc_in_merge[edge->src->scc]) + continue; + if (!scc_in_merge[edge->dst->scc]) + continue; + if (edge->no_merge) + return 1; + if (merge_edge->weight >= 0 && edge->weight < 0) { + merge_edge->weight -= graph->max_weight + 1; + return 1; + } + } + + return 0; +} + +/* Merge the two clusters in "c" connected by the edge in "graph" + * with index "edge" into a single cluster. + * If it turns out to be impossible to merge these two clusters, + * then mark the edge as "no_merge" such that it will not be + * considered again. + * + * First mark all SCCs that need to be merged. This includes the SCCs + * in the two clusters, but it may also include the SCCs + * of intermediate clusters. + * If there is already a no_merge edge between any pair of such SCCs, + * then simply mark the current edge as no_merge as well. + * Likewise, if any of those edges was postponed by has_bounded_distances, + * then postpone the current edge as well. + * Otherwise, try and merge the clusters and mark "edge" as "no_merge" + * if the clusters did not end up getting merged, unless the non-merge + * is due to the fact that the edge was postponed. This postponement + * can be recognized by a change in weight (from non-negative to negative). + */ +static isl_stat merge_clusters_along_edge(isl_ctx *ctx, + struct isl_sched_graph *graph, int edge, struct isl_clustering *c) +{ + isl_bool merged; + int edge_weight = graph->edge[edge].weight; + + if (mark_merge_sccs(ctx, graph, edge, c) < 0) + return isl_stat_error; + + if (any_no_merge(graph, c->scc_in_merge, &graph->edge[edge])) + merged = isl_bool_false; + else + merged = try_merge(ctx, graph, c); + if (merged < 0) + return isl_stat_error; + if (!merged && edge_weight == graph->edge[edge].weight) + graph->edge[edge].no_merge = 1; + + return isl_stat_ok; +} + +/* Does "node" belong to the cluster identified by "cluster"? + */ +static int node_cluster_exactly(struct isl_sched_node *node, int cluster) +{ + return node->cluster == cluster; +} + +/* Does "edge" connect two nodes belonging to the cluster + * identified by "cluster"? + */ +static int edge_cluster_exactly(struct isl_sched_edge *edge, int cluster) +{ + return edge->src->cluster == cluster && edge->dst->cluster == cluster; +} + +/* Swap the schedule of "node1" and "node2". + * Both nodes have been derived from the same node in a common parent graph. + * Since the "coincident" field is shared with that node + * in the parent graph, there is no need to also swap this field. + */ +static void swap_sched(struct isl_sched_node *node1, + struct isl_sched_node *node2) +{ + isl_mat *sched; + isl_map *sched_map; + + sched = node1->sched; + node1->sched = node2->sched; + node2->sched = sched; + + sched_map = node1->sched_map; + node1->sched_map = node2->sched_map; + node2->sched_map = sched_map; +} + +/* Copy the current band schedule from the SCCs that form the cluster + * with index "pos" to the actual cluster at position "pos". + * By construction, the index of the first SCC that belongs to the cluster + * is also "pos". + * + * The order of the nodes inside both the SCCs and the cluster + * is assumed to be same as the order in the original "graph". + * + * Since the SCC graphs will no longer be used after this function, + * the schedules are actually swapped rather than copied. + */ +static isl_stat copy_partial(struct isl_sched_graph *graph, + struct isl_clustering *c, int pos) +{ + int i, j; + + c->cluster[pos].n_total_row = c->scc[pos].n_total_row; + c->cluster[pos].n_row = c->scc[pos].n_row; + c->cluster[pos].maxvar = c->scc[pos].maxvar; + j = 0; + for (i = 0; i < graph->n; ++i) { + int k; + int s; + + if (graph->node[i].cluster != pos) + continue; + s = graph->node[i].scc; + k = c->scc_node[s]++; + swap_sched(&c->cluster[pos].node[j], &c->scc[s].node[k]); + if (c->scc[s].maxvar > c->cluster[pos].maxvar) + c->cluster[pos].maxvar = c->scc[s].maxvar; + ++j; + } + + return isl_stat_ok; +} + +/* Is there a (conditional) validity dependence from node[j] to node[i], + * forcing node[i] to follow node[j] or do the nodes belong to the same + * cluster? + */ +static isl_bool node_follows_strong_or_same_cluster(int i, int j, void *user) +{ + struct isl_sched_graph *graph = user; + + if (graph->node[i].cluster == graph->node[j].cluster) + return isl_bool_true; + return graph_has_validity_edge(graph, &graph->node[j], &graph->node[i]); +} + +/* Extract the merged clusters of SCCs in "graph", sort them, and + * store them in c->clusters. Update c->scc_cluster accordingly. + * + * First keep track of the cluster containing the SCC to which a node + * belongs in the node itself. + * Then extract the clusters into c->clusters, copying the current + * band schedule from the SCCs that belong to the cluster. + * Do this only once per cluster. + * + * Finally, topologically sort the clusters and update c->scc_cluster + * to match the new scc numbering. While the SCCs were originally + * sorted already, some SCCs that depend on some other SCCs may + * have been merged with SCCs that appear before these other SCCs. + * A reordering may therefore be required. + */ +static isl_stat extract_clusters(isl_ctx *ctx, struct isl_sched_graph *graph, + struct isl_clustering *c) +{ + int i; + + for (i = 0; i < graph->n; ++i) + graph->node[i].cluster = c->scc_cluster[graph->node[i].scc]; + + for (i = 0; i < graph->scc; ++i) { + if (c->scc_cluster[i] != i) + continue; + if (extract_sub_graph(ctx, graph, &node_cluster_exactly, + &edge_cluster_exactly, i, &c->cluster[i]) < 0) + return isl_stat_error; + c->cluster[i].src_scc = -1; + c->cluster[i].dst_scc = -1; + if (copy_partial(graph, c, i) < 0) + return isl_stat_error; + } + + if (detect_ccs(ctx, graph, &node_follows_strong_or_same_cluster) < 0) + return isl_stat_error; + for (i = 0; i < graph->n; ++i) + c->scc_cluster[graph->node[i].scc] = graph->node[i].cluster; + + return isl_stat_ok; +} + +/* Compute weights on the proximity edges of "graph" that can + * be used by find_proximity to find the most appropriate + * proximity edge to use to merge two clusters in "c". + * The weights are also used by has_bounded_distances to determine + * whether the merge should be allowed. + * Store the maximum of the computed weights in graph->max_weight. + * + * The computed weight is a measure for the number of remaining schedule + * dimensions that can still be completely aligned. + * In particular, compute the number of equalities between + * input dimensions and output dimensions in the proximity constraints. + * The directions that are already handled by outer schedule bands + * are projected out prior to determining this number. + * + * Edges that will never be considered by find_proximity are ignored. + */ +static isl_stat compute_weights(struct isl_sched_graph *graph, + struct isl_clustering *c) +{ + int i; + + graph->max_weight = 0; + + for (i = 0; i < graph->n_edge; ++i) { + struct isl_sched_edge *edge = &graph->edge[i]; + struct isl_sched_node *src = edge->src; + struct isl_sched_node *dst = edge->dst; + isl_basic_map *hull; + int n_in, n_out; + + if (!is_proximity(edge)) + continue; + if (bad_cluster(&c->scc[edge->src->scc]) || + bad_cluster(&c->scc[edge->dst->scc])) + continue; + if (c->scc_cluster[edge->dst->scc] == + c->scc_cluster[edge->src->scc]) + continue; + + hull = isl_map_affine_hull(isl_map_copy(edge->map)); + hull = isl_basic_map_transform_dims(hull, isl_dim_in, 0, + isl_mat_copy(src->ctrans)); + hull = isl_basic_map_transform_dims(hull, isl_dim_out, 0, + isl_mat_copy(dst->ctrans)); + hull = isl_basic_map_project_out(hull, + isl_dim_in, 0, src->rank); + hull = isl_basic_map_project_out(hull, + isl_dim_out, 0, dst->rank); + hull = isl_basic_map_remove_divs(hull); + n_in = isl_basic_map_dim(hull, isl_dim_in); + n_out = isl_basic_map_dim(hull, isl_dim_out); + hull = isl_basic_map_drop_constraints_not_involving_dims(hull, + isl_dim_in, 0, n_in); + hull = isl_basic_map_drop_constraints_not_involving_dims(hull, + isl_dim_out, 0, n_out); + if (!hull) + return isl_stat_error; + edge->weight = hull->n_eq; + isl_basic_map_free(hull); + + if (edge->weight > graph->max_weight) + graph->max_weight = edge->weight; + } + + return isl_stat_ok; +} + +/* Call compute_schedule_finish_band on each of the clusters in "c" + * in their topological order. This order is determined by the scc + * fields of the nodes in "graph". + * Combine the results in a sequence expressing the topological order. + * + * If there is only one cluster left, then there is no need to introduce + * a sequence node. Also, in this case, the cluster necessarily contains + * the SCC at position 0 in the original graph and is therefore also + * stored in the first cluster of "c". + */ +static __isl_give isl_schedule_node *finish_bands_clustering( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph, + struct isl_clustering *c) +{ + int i; + isl_ctx *ctx; + isl_union_set_list *filters; + + if (graph->scc == 1) + return compute_schedule_finish_band(node, &c->cluster[0], 0); + + ctx = isl_schedule_node_get_ctx(node); + + filters = extract_sccs(ctx, graph); + node = isl_schedule_node_insert_sequence(node, filters); + + for (i = 0; i < graph->scc; ++i) { + int j = c->scc_cluster[i]; + node = isl_schedule_node_child(node, i); + node = isl_schedule_node_child(node, 0); + node = compute_schedule_finish_band(node, &c->cluster[j], 0); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + } + + return node; +} + +/* Compute a schedule for a connected dependence graph by first considering + * each strongly connected component (SCC) in the graph separately and then + * incrementally combining them into clusters. + * Return the updated schedule node. + * + * Initially, each cluster consists of a single SCC, each with its + * own band schedule. The algorithm then tries to merge pairs + * of clusters along a proximity edge until no more suitable + * proximity edges can be found. During this merging, the schedule + * is maintained in the individual SCCs. + * After the merging is completed, the full resulting clusters + * are extracted and in finish_bands_clustering, + * compute_schedule_finish_band is called on each of them to integrate + * the band into "node" and to continue the computation. + * + * compute_weights initializes the weights that are used by find_proximity. + */ +static __isl_give isl_schedule_node *compute_schedule_wcc_clustering( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph) +{ + isl_ctx *ctx; + struct isl_clustering c; + int i; + + ctx = isl_schedule_node_get_ctx(node); + + if (clustering_init(ctx, &c, graph) < 0) + goto error; + + if (compute_weights(graph, &c) < 0) + goto error; + + for (;;) { + i = find_proximity(graph, &c); + if (i < 0) + goto error; + if (i >= graph->n_edge) + break; + if (merge_clusters_along_edge(ctx, graph, i, &c) < 0) + goto error; + } + + if (extract_clusters(ctx, graph, &c) < 0) + goto error; + + node = finish_bands_clustering(node, graph, &c); + + clustering_free(ctx, &c); + return node; +error: + clustering_free(ctx, &c); + return isl_schedule_node_free(node); +} + +/* Compute a schedule for a connected dependence graph and return + * the updated schedule node. + * + * If Feautrier's algorithm is selected, we first recursively try to satisfy + * as many validity dependences as possible. When all validity dependences + * are satisfied we extend the schedule to a full-dimensional schedule. + * + * Call compute_schedule_wcc_whole or compute_schedule_wcc_clustering + * depending on whether the user has selected the option to try and + * compute a schedule for the entire (weakly connected) component first. + * If there is only a single strongly connected component (SCC), then + * there is no point in trying to combine SCCs + * in compute_schedule_wcc_clustering, so compute_schedule_wcc_whole + * is called instead. + */ +static __isl_give isl_schedule_node *compute_schedule_wcc( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph) +{ + isl_ctx *ctx; + + if (!node) + return NULL; + + ctx = isl_schedule_node_get_ctx(node); + if (detect_sccs(ctx, graph) < 0) + return isl_schedule_node_free(node); + + if (compute_maxvar(graph) < 0) + return isl_schedule_node_free(node); + + if (need_feautrier_step(ctx, graph)) + return compute_schedule_wcc_feautrier(node, graph); + + if (graph->scc <= 1 || isl_options_get_schedule_whole_component(ctx)) + return compute_schedule_wcc_whole(node, graph); + else + return compute_schedule_wcc_clustering(node, graph); +} + +/* Compute a schedule for each group of nodes identified by node->scc + * separately and then combine them in a sequence node (or as set node + * if graph->weak is set) inserted at position "node" of the schedule tree. + * Return the updated schedule node. + * + * If "wcc" is set then each of the groups belongs to a single + * weakly connected component in the dependence graph so that + * there is no need for compute_sub_schedule to look for weakly + * connected components. + */ +static __isl_give isl_schedule_node *compute_component_schedule( + __isl_take isl_schedule_node *node, struct isl_sched_graph *graph, + int wcc) +{ + int component; + isl_ctx *ctx; + isl_union_set_list *filters; + + if (!node) + return NULL; + ctx = isl_schedule_node_get_ctx(node); + + filters = extract_sccs(ctx, graph); + if (graph->weak) + node = isl_schedule_node_insert_set(node, filters); + else + node = isl_schedule_node_insert_sequence(node, filters); + + for (component = 0; component < graph->scc; ++component) { + node = isl_schedule_node_child(node, component); + node = isl_schedule_node_child(node, 0); + node = compute_sub_schedule(node, ctx, graph, + &node_scc_exactly, + &edge_scc_exactly, component, wcc); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + } + + return node; +} + +/* Compute a schedule for the given dependence graph and insert it at "node". + * Return the updated schedule node. + * + * We first check if the graph is connected (through validity and conditional + * validity dependences) and, if not, compute a schedule + * for each component separately. + * If the schedule_serialize_sccs option is set, then we check for strongly + * connected components instead and compute a separate schedule for + * each such strongly connected component. + */ +static __isl_give isl_schedule_node *compute_schedule(isl_schedule_node *node, + struct isl_sched_graph *graph) +{ + isl_ctx *ctx; + + if (!node) + return NULL; + + ctx = isl_schedule_node_get_ctx(node); + if (isl_options_get_schedule_serialize_sccs(ctx)) { + if (detect_sccs(ctx, graph) < 0) + return isl_schedule_node_free(node); + } else { + if (detect_wccs(ctx, graph) < 0) + return isl_schedule_node_free(node); + } + + if (graph->scc > 1) + return compute_component_schedule(node, graph, 1); + + return compute_schedule_wcc(node, graph); +} + +/* Compute a schedule on sc->domain that respects the given schedule + * constraints. + * + * In particular, the schedule respects all the validity dependences. + * If the default isl scheduling algorithm is used, it tries to minimize + * the dependence distances over the proximity dependences. + * If Feautrier's scheduling algorithm is used, the proximity dependence + * distances are only minimized during the extension to a full-dimensional + * schedule. + * + * If there are any condition and conditional validity dependences, + * then the conditional validity dependences may be violated inside + * a tilable band, provided they have no adjacent non-local + * condition dependences. + */ +__isl_give isl_schedule *isl_schedule_constraints_compute_schedule( + __isl_take isl_schedule_constraints *sc) +{ + isl_ctx *ctx = isl_schedule_constraints_get_ctx(sc); + struct isl_sched_graph graph = { 0 }; + isl_schedule *sched; + isl_schedule_node *node; + isl_union_set *domain; + + sc = isl_schedule_constraints_align_params(sc); + + domain = isl_schedule_constraints_get_domain(sc); + if (isl_union_set_n_set(domain) == 0) { + isl_schedule_constraints_free(sc); + return isl_schedule_from_domain(domain); + } + + if (graph_init(&graph, sc) < 0) + domain = isl_union_set_free(domain); + + node = isl_schedule_node_from_domain(domain); + node = isl_schedule_node_child(node, 0); + if (graph.n > 0) + node = compute_schedule(node, &graph); + sched = isl_schedule_node_get_schedule(node); + isl_schedule_node_free(node); + + graph_free(ctx, &graph); + isl_schedule_constraints_free(sc); + + return sched; +} + +/* Compute a schedule for the given union of domains that respects + * all the validity dependences and minimizes + * the dependence distances over the proximity dependences. + * + * This function is kept for backward compatibility. + */ +__isl_give isl_schedule *isl_union_set_compute_schedule( + __isl_take isl_union_set *domain, + __isl_take isl_union_map *validity, + __isl_take isl_union_map *proximity) +{ + isl_schedule_constraints *sc; + + sc = isl_schedule_constraints_on_domain(domain); + sc = isl_schedule_constraints_set_validity(sc, validity); + sc = isl_schedule_constraints_set_proximity(sc, proximity); + + return isl_schedule_constraints_compute_schedule(sc); +} Index: lib/Analysis/isl/isl_seq.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_seq.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_SEQ_H +#define ISL_SEQ_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Some common operations on sequences of isl_int's */ + +void isl_seq_clr(isl_int *p, unsigned len); +void isl_seq_set(isl_int *p, isl_int v, unsigned len); +void isl_seq_set_si(isl_int *p, int v, unsigned len); +void isl_seq_neg(isl_int *dst, isl_int *src, unsigned len); +void isl_seq_cpy(isl_int *dst, isl_int *src, unsigned len); +void isl_seq_addmul(isl_int *dst, isl_int f, isl_int *src, unsigned len); +void isl_seq_submul(isl_int *dst, isl_int f, isl_int *src, unsigned len); +void isl_seq_swp_or_cpy(isl_int *dst, isl_int *src, unsigned len); +void isl_seq_scale(isl_int *dst, isl_int *src, isl_int f, unsigned len); +void isl_seq_scale_down(isl_int *dst, isl_int *src, isl_int f, unsigned len); +void isl_seq_cdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len); +void isl_seq_fdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len); +void isl_seq_fdiv_r(isl_int *dst, isl_int *src, isl_int m, unsigned len); +void isl_seq_combine(isl_int *dst, isl_int m1, isl_int *src1, + isl_int m2, isl_int *src2, unsigned len); +void isl_seq_elim(isl_int *dst, isl_int *src, unsigned pos, unsigned len, + isl_int *m); +void isl_seq_abs_max(isl_int *p, unsigned len, isl_int *max); +void isl_seq_gcd(isl_int *p, unsigned len, isl_int *gcd); +void isl_seq_lcm(isl_int *p, unsigned len, isl_int *lcm); +void isl_seq_normalize(struct isl_ctx *ctx, isl_int *p, unsigned len); +void isl_seq_inner_product(isl_int *p1, isl_int *p2, unsigned len, + isl_int *prod); +int isl_seq_first_non_zero(isl_int *p, unsigned len); +int isl_seq_last_non_zero(isl_int *p, unsigned len); +int isl_seq_abs_min_non_zero(isl_int *p, unsigned len); +int isl_seq_eq(isl_int *p1, isl_int *p2, unsigned len); +int isl_seq_cmp(isl_int *p1, isl_int *p2, unsigned len); +int isl_seq_is_neg(isl_int *p1, isl_int *p2, unsigned len); + +uint32_t isl_seq_get_hash(isl_int *p, unsigned len); +uint32_t isl_seq_get_hash_bits(isl_int *p, unsigned len, unsigned bits); + +#if defined(__cplusplus) +} +#endif + +#endif Index: lib/Analysis/isl/isl_seq.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_seq.c @@ -0,0 +1,329 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include + +void isl_seq_clr(isl_int *p, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_set_si(p[i], 0); +} + +void isl_seq_set_si(isl_int *p, int v, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_set_si(p[i], v); +} + +void isl_seq_set(isl_int *p, isl_int v, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_set(p[i], v); +} + +void isl_seq_neg(isl_int *dst, isl_int *src, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_neg(dst[i], src[i]); +} + +void isl_seq_cpy(isl_int *dst, isl_int *src, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_set(dst[i], src[i]); +} + +void isl_seq_submul(isl_int *dst, isl_int f, isl_int *src, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_submul(dst[i], f, src[i]); +} + +void isl_seq_addmul(isl_int *dst, isl_int f, isl_int *src, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_addmul(dst[i], f, src[i]); +} + +void isl_seq_swp_or_cpy(isl_int *dst, isl_int *src, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_swap_or_set(dst[i], src[i]); +} + +void isl_seq_scale(isl_int *dst, isl_int *src, isl_int m, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_mul(dst[i], src[i], m); +} + +void isl_seq_scale_down(isl_int *dst, isl_int *src, isl_int m, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_divexact(dst[i], src[i], m); +} + +void isl_seq_cdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_cdiv_q(dst[i], src[i], m); +} + +void isl_seq_fdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_fdiv_q(dst[i], src[i], m); +} + +void isl_seq_fdiv_r(isl_int *dst, isl_int *src, isl_int m, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + isl_int_fdiv_r(dst[i], src[i], m); +} + +void isl_seq_combine(isl_int *dst, isl_int m1, isl_int *src1, + isl_int m2, isl_int *src2, unsigned len) +{ + int i; + isl_int tmp; + + if (dst == src1 && isl_int_is_one(m1)) { + if (isl_int_is_zero(m2)) + return; + for (i = 0; i < len; ++i) + isl_int_addmul(src1[i], m2, src2[i]); + return; + } + + isl_int_init(tmp); + for (i = 0; i < len; ++i) { + isl_int_mul(tmp, m1, src1[i]); + isl_int_addmul(tmp, m2, src2[i]); + isl_int_set(dst[i], tmp); + } + isl_int_clear(tmp); +} + +/* + * Let d = dst[pos] and s = src[pos] + * dst is replaced by |s| dst - sgn(s)d src + */ +void isl_seq_elim(isl_int *dst, isl_int *src, unsigned pos, unsigned len, + isl_int *m) +{ + isl_int a; + isl_int b; + + if (isl_int_is_zero(dst[pos])) + return; + + isl_int_init(a); + isl_int_init(b); + + isl_int_gcd(a, src[pos], dst[pos]); + isl_int_divexact(b, dst[pos], a); + if (isl_int_is_pos(src[pos])) + isl_int_neg(b, b); + isl_int_divexact(a, src[pos], a); + isl_int_abs(a, a); + isl_seq_combine(dst, a, dst, b, src, len); + + if (m) + isl_int_mul(*m, *m, a); + + isl_int_clear(a); + isl_int_clear(b); +} + +int isl_seq_eq(isl_int *p1, isl_int *p2, unsigned len) +{ + int i; + for (i = 0; i < len; ++i) + if (isl_int_ne(p1[i], p2[i])) + return 0; + return 1; +} + +int isl_seq_cmp(isl_int *p1, isl_int *p2, unsigned len) +{ + int i; + int cmp; + for (i = 0; i < len; ++i) + if ((cmp = isl_int_cmp(p1[i], p2[i])) != 0) + return cmp; + return 0; +} + +int isl_seq_is_neg(isl_int *p1, isl_int *p2, unsigned len) +{ + int i; + + for (i = 0; i < len; ++i) { + if (isl_int_abs_ne(p1[i], p2[i])) + return 0; + if (isl_int_is_zero(p1[i])) + continue; + if (isl_int_eq(p1[i], p2[i])) + return 0; + } + return 1; +} + +int isl_seq_first_non_zero(isl_int *p, unsigned len) +{ + int i; + + for (i = 0; i < len; ++i) + if (!isl_int_is_zero(p[i])) + return i; + return -1; +} + +int isl_seq_last_non_zero(isl_int *p, unsigned len) +{ + int i; + + for (i = len - 1; i >= 0; --i) + if (!isl_int_is_zero(p[i])) + return i; + return -1; +} + +void isl_seq_abs_max(isl_int *p, unsigned len, isl_int *max) +{ + int i; + + isl_int_set_si(*max, 0); + + for (i = 0; i < len; ++i) + if (isl_int_abs_gt(p[i], *max)) + isl_int_abs(*max, p[i]); +} + +int isl_seq_abs_min_non_zero(isl_int *p, unsigned len) +{ + int i, min = isl_seq_first_non_zero(p, len); + if (min < 0) + return -1; + for (i = min + 1; i < len; ++i) { + if (isl_int_is_zero(p[i])) + continue; + if (isl_int_abs_lt(p[i], p[min])) + min = i; + } + return min; +} + +void isl_seq_gcd(isl_int *p, unsigned len, isl_int *gcd) +{ + int i, min = isl_seq_abs_min_non_zero(p, len); + + if (min < 0) { + isl_int_set_si(*gcd, 0); + return; + } + isl_int_abs(*gcd, p[min]); + for (i = 0; isl_int_cmp_si(*gcd, 1) > 0 && i < len; ++i) { + if (i == min) + continue; + if (isl_int_is_zero(p[i])) + continue; + isl_int_gcd(*gcd, *gcd, p[i]); + } +} + +void isl_seq_normalize(struct isl_ctx *ctx, isl_int *p, unsigned len) +{ + if (len == 0) + return; + isl_seq_gcd(p, len, &ctx->normalize_gcd); + if (!isl_int_is_zero(ctx->normalize_gcd) && + !isl_int_is_one(ctx->normalize_gcd)) + isl_seq_scale_down(p, p, ctx->normalize_gcd, len); +} + +void isl_seq_lcm(isl_int *p, unsigned len, isl_int *lcm) +{ + int i; + + if (len == 0) { + isl_int_set_si(*lcm, 1); + return; + } + isl_int_set(*lcm, p[0]); + for (i = 1; i < len; ++i) + isl_int_lcm(*lcm, *lcm, p[i]); +} + +void isl_seq_inner_product(isl_int *p1, isl_int *p2, unsigned len, + isl_int *prod) +{ + int i; + if (len == 0) { + isl_int_set_si(*prod, 0); + return; + } + isl_int_mul(*prod, p1[0], p2[0]); + for (i = 1; i < len; ++i) + isl_int_addmul(*prod, p1[i], p2[i]); +} + +uint32_t isl_seq_hash(isl_int *p, unsigned len, uint32_t hash) +{ + int i; + for (i = 0; i < len; ++i) { + if (isl_int_is_zero(p[i])) + continue; + hash *= 16777619; + hash ^= (i & 0xFF); + hash = isl_int_hash(p[i], hash); + } + return hash; +} + +uint32_t isl_seq_get_hash(isl_int *p, unsigned len) +{ + uint32_t hash = isl_hash_init(); + + return isl_seq_hash(p, len, hash); +} + +uint32_t isl_seq_get_hash_bits(isl_int *p, unsigned len, unsigned bits) +{ + uint32_t hash; + + hash = isl_seq_get_hash(p, len); + return isl_hash_bits(hash, bits); +} + +void isl_seq_dump(isl_int *p, unsigned len) +{ + int i; + + for (i = 0; i < len; ++i) { + if (i) + fprintf(stderr, " "); + isl_int_print(stderr, p[i], 0); + } + fprintf(stderr, "\n"); +} Index: lib/Analysis/isl/isl_set_list.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_set_list.c @@ -0,0 +1,32 @@ +#include +#include + +#undef EL +#define EL isl_basic_set + +#include + +#undef EL +#define EL isl_set + +#include + +#undef EL +#define EL isl_union_set + +#include + +#undef BASE +#define BASE basic_set + +#include + +#undef BASE +#define BASE set + +#include + +#undef BASE +#define BASE union_set + +#include Index: lib/Analysis/isl/isl_sort.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_sort.h @@ -0,0 +1,9 @@ +#ifndef ISL_SORT_H +#define ISL_SORT_H + +#include + +int isl_sort(void *const pbase, size_t total_elems, size_t size, + int (*cmp)(const void *, const void *, void *arg), void *arg); + +#endif Index: lib/Analysis/isl/isl_sort.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_sort.c @@ -0,0 +1,157 @@ +/* + * The code of this file was taken from http://jeffreystedfast.blogspot.be, + * where it was posted in 2011 by Jeffrey Stedfast under the MIT license. + * The MIT license text is as follows: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#define MID(lo, hi) (lo + ((hi - lo) >> 1)) + +/* The code here is an optimized merge sort. Starting from a generic merge sort + * the following optimizations were applied: + * + * o Batching of memcpy() calls: Instead of calling memcpy() to copy each and + * every element into a temporary buffer, blocks of elements are copied + * at a time. + * + * o To reduce the number of memcpy() calls further, copying leading + * and trailing elements into our temporary buffer is avoided, in case it is + * not necessary to merge them. + * + * A further optimization could be to specialize memcpy calls based on the + * size of the types we compare. For now, this code does not include the + * relevant optimization, as clang e.g. inlines a very efficient memcpy() + * implementation. It is not clear, that the specialized version as provided in + * the blog post, is really superior to the one that will be inlined by + * default. So we decided to keep the code simple until this optimization was + * proven to be beneficial. + */ + +static void +msort (void *array, void *buf, size_t low, size_t high, size_t size, + int (* compare) (const void *, const void *, void *), void *arg) +{ + char *a1, *al, *am, *ah, *ls, *hs, *lo, *hi, *b; + size_t copied = 0; + size_t mid; + + mid = MID (low, high); + + if (mid + 1 < high) + msort (array, buf, mid + 1, high, size, compare, arg); + + if (mid > low) + msort (array, buf, low, mid, size, compare, arg); + + ah = ((char *) array) + ((high + 1) * size); + am = ((char *) array) + ((mid + 1) * size); + a1 = al = ((char *) array) + (low * size); + + b = (char *) buf; + lo = al; + hi = am; + + do { + ls = lo; + hs = hi; + + if (lo > al || hi > am) { + /* our last loop already compared lo & hi and found lo <= hi */ + lo += size; + } + + while (lo < am && compare (lo, hi, arg) <= 0) + lo += size; + + if (lo < am) { + if (copied == 0) { + /* avoid copying the leading items */ + a1 = lo; + ls = lo; + } + + /* our last compare tells us hi < lo */ + hi += size; + + while (hi < ah && compare (hi, lo, arg) < 0) + hi += size; + + if (lo > ls) { + memcpy (b, ls, lo - ls); + copied += (lo - ls); + b += (lo - ls); + } + + memcpy (b, hs, hi - hs); + copied += (hi - hs); + b += (hi - hs); + } else if (copied) { + memcpy (b, ls, lo - ls); + copied += (lo - ls); + b += (lo - ls); + + /* copy everything we needed to re-order back into array */ + memcpy (a1, buf, copied); + return; + } else { + /* everything already in order */ + return; + } + } while (hi < ah); + + if (lo < am) { + memcpy (b, lo, am - lo); + copied += (am - lo); + } + + memcpy (a1, buf, copied); +} + +static int +MergeSort (void *base, size_t nmemb, size_t size, + int (* compare) (const void *, const void *, void *), void *arg) +{ + void *tmp; + + if (nmemb < 2) + return 0; + + if (!(tmp = malloc (nmemb * size))) { + errno = ENOMEM; + return -1; + } + + msort (base, tmp, 0, nmemb - 1, size, compare, arg); + + free (tmp); + + return 0; +} + +int isl_sort(void *const pbase, size_t total_elems, size_t size, + int (*cmp)(const void *, const void *, void *arg), void *arg) +{ + return MergeSort (pbase, total_elems, size, cmp, arg); +} Index: lib/Analysis/isl/isl_space.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_space.c @@ -0,0 +1,2509 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2013-2014 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include + +isl_ctx *isl_space_get_ctx(__isl_keep isl_space *dim) +{ + return dim ? dim->ctx : NULL; +} + +__isl_give isl_space *isl_space_alloc(isl_ctx *ctx, + unsigned nparam, unsigned n_in, unsigned n_out) +{ + isl_space *dim; + + dim = isl_alloc_type(ctx, struct isl_space); + if (!dim) + return NULL; + + dim->ctx = ctx; + isl_ctx_ref(ctx); + dim->ref = 1; + dim->nparam = nparam; + dim->n_in = n_in; + dim->n_out = n_out; + + dim->tuple_id[0] = NULL; + dim->tuple_id[1] = NULL; + + dim->nested[0] = NULL; + dim->nested[1] = NULL; + + dim->n_id = 0; + dim->ids = NULL; + + return dim; +} + +/* Mark the space as being that of a set, by setting the domain tuple + * to isl_id_none. + */ +static __isl_give isl_space *mark_as_set(__isl_take isl_space *space) +{ + space = isl_space_cow(space); + if (!space) + return NULL; + space = isl_space_set_tuple_id(space, isl_dim_in, &isl_id_none); + return space; +} + +/* Is the space that of a set? + */ +isl_bool isl_space_is_set(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + if (space->n_in != 0 || space->nested[0]) + return isl_bool_false; + if (space->tuple_id[0] != &isl_id_none) + return isl_bool_false; + return isl_bool_true; +} + +/* Is the given space that of a map? + */ +isl_bool isl_space_is_map(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + return space->tuple_id[0] != &isl_id_none && + space->tuple_id[1] != &isl_id_none; +} + +__isl_give isl_space *isl_space_set_alloc(isl_ctx *ctx, + unsigned nparam, unsigned dim) +{ + isl_space *space; + space = isl_space_alloc(ctx, nparam, 0, dim); + space = mark_as_set(space); + return space; +} + +/* Mark the space as being that of a parameter domain, by setting + * both tuples to isl_id_none. + */ +static __isl_give isl_space *mark_as_params(isl_space *space) +{ + if (!space) + return NULL; + space = isl_space_set_tuple_id(space, isl_dim_in, &isl_id_none); + space = isl_space_set_tuple_id(space, isl_dim_out, &isl_id_none); + return space; +} + +/* Is the space that of a parameter domain? + */ +isl_bool isl_space_is_params(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + if (space->n_in != 0 || space->nested[0] || + space->n_out != 0 || space->nested[1]) + return isl_bool_false; + if (space->tuple_id[0] != &isl_id_none) + return isl_bool_false; + if (space->tuple_id[1] != &isl_id_none) + return isl_bool_false; + return isl_bool_true; +} + +/* Create a space for a parameter domain. + */ +__isl_give isl_space *isl_space_params_alloc(isl_ctx *ctx, unsigned nparam) +{ + isl_space *space; + space = isl_space_alloc(ctx, nparam, 0, 0); + space = mark_as_params(space); + return space; +} + +static unsigned global_pos(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos) +{ + struct isl_ctx *ctx = dim->ctx; + + switch (type) { + case isl_dim_param: + isl_assert(ctx, pos < dim->nparam, + return isl_space_dim(dim, isl_dim_all)); + return pos; + case isl_dim_in: + isl_assert(ctx, pos < dim->n_in, + return isl_space_dim(dim, isl_dim_all)); + return pos + dim->nparam; + case isl_dim_out: + isl_assert(ctx, pos < dim->n_out, + return isl_space_dim(dim, isl_dim_all)); + return pos + dim->nparam + dim->n_in; + default: + isl_assert(ctx, 0, return isl_space_dim(dim, isl_dim_all)); + } + return isl_space_dim(dim, isl_dim_all); +} + +/* Extend length of ids array to the total number of dimensions. + */ +static __isl_give isl_space *extend_ids(__isl_take isl_space *dim) +{ + isl_id **ids; + int i; + + if (isl_space_dim(dim, isl_dim_all) <= dim->n_id) + return dim; + + if (!dim->ids) { + dim->ids = isl_calloc_array(dim->ctx, + isl_id *, isl_space_dim(dim, isl_dim_all)); + if (!dim->ids) + goto error; + } else { + ids = isl_realloc_array(dim->ctx, dim->ids, + isl_id *, isl_space_dim(dim, isl_dim_all)); + if (!ids) + goto error; + dim->ids = ids; + for (i = dim->n_id; i < isl_space_dim(dim, isl_dim_all); ++i) + dim->ids[i] = NULL; + } + + dim->n_id = isl_space_dim(dim, isl_dim_all); + + return dim; +error: + isl_space_free(dim); + return NULL; +} + +static __isl_give isl_space *set_id(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + dim = isl_space_cow(dim); + + if (!dim) + goto error; + + pos = global_pos(dim, type, pos); + if (pos == isl_space_dim(dim, isl_dim_all)) + goto error; + + if (pos >= dim->n_id) { + if (!id) + return dim; + dim = extend_ids(dim); + if (!dim) + goto error; + } + + dim->ids[pos] = id; + + return dim; +error: + isl_id_free(id); + isl_space_free(dim); + return NULL; +} + +static __isl_keep isl_id *get_id(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos) +{ + if (!dim) + return NULL; + + pos = global_pos(dim, type, pos); + if (pos == isl_space_dim(dim, isl_dim_all)) + return NULL; + if (pos >= dim->n_id) + return NULL; + return dim->ids[pos]; +} + +static unsigned offset(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + switch (type) { + case isl_dim_param: return 0; + case isl_dim_in: return dim->nparam; + case isl_dim_out: return dim->nparam + dim->n_in; + default: return 0; + } +} + +static unsigned n(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + switch (type) { + case isl_dim_param: return dim->nparam; + case isl_dim_in: return dim->n_in; + case isl_dim_out: return dim->n_out; + case isl_dim_all: return dim->nparam + dim->n_in + dim->n_out; + default: return 0; + } +} + +unsigned isl_space_dim(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + if (!dim) + return 0; + return n(dim, type); +} + +unsigned isl_space_offset(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + if (!dim) + return 0; + return offset(dim, type); +} + +static __isl_give isl_space *copy_ids(__isl_take isl_space *dst, + enum isl_dim_type dst_type, unsigned offset, __isl_keep isl_space *src, + enum isl_dim_type src_type) +{ + int i; + isl_id *id; + + if (!dst) + return NULL; + + for (i = 0; i < n(src, src_type); ++i) { + id = get_id(src, src_type, i); + if (!id) + continue; + dst = set_id(dst, dst_type, offset + i, isl_id_copy(id)); + if (!dst) + return NULL; + } + return dst; +} + +__isl_take isl_space *isl_space_dup(__isl_keep isl_space *dim) +{ + isl_space *dup; + if (!dim) + return NULL; + dup = isl_space_alloc(dim->ctx, dim->nparam, dim->n_in, dim->n_out); + if (!dup) + return NULL; + if (dim->tuple_id[0] && + !(dup->tuple_id[0] = isl_id_copy(dim->tuple_id[0]))) + goto error; + if (dim->tuple_id[1] && + !(dup->tuple_id[1] = isl_id_copy(dim->tuple_id[1]))) + goto error; + if (dim->nested[0] && !(dup->nested[0] = isl_space_copy(dim->nested[0]))) + goto error; + if (dim->nested[1] && !(dup->nested[1] = isl_space_copy(dim->nested[1]))) + goto error; + if (!dim->ids) + return dup; + dup = copy_ids(dup, isl_dim_param, 0, dim, isl_dim_param); + dup = copy_ids(dup, isl_dim_in, 0, dim, isl_dim_in); + dup = copy_ids(dup, isl_dim_out, 0, dim, isl_dim_out); + return dup; +error: + isl_space_free(dup); + return NULL; +} + +__isl_give isl_space *isl_space_cow(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + + if (dim->ref == 1) + return dim; + dim->ref--; + return isl_space_dup(dim); +} + +__isl_give isl_space *isl_space_copy(__isl_keep isl_space *dim) +{ + if (!dim) + return NULL; + + dim->ref++; + return dim; +} + +__isl_null isl_space *isl_space_free(__isl_take isl_space *space) +{ + int i; + + if (!space) + return NULL; + + if (--space->ref > 0) + return NULL; + + isl_id_free(space->tuple_id[0]); + isl_id_free(space->tuple_id[1]); + + isl_space_free(space->nested[0]); + isl_space_free(space->nested[1]); + + for (i = 0; i < space->n_id; ++i) + isl_id_free(space->ids[i]); + free(space->ids); + isl_ctx_deref(space->ctx); + + free(space); + + return NULL; +} + +/* Check if "s" is a valid dimension or tuple name. + * We currently only forbid names that look like a number. + * + * s is assumed to be non-NULL. + */ +static int name_ok(isl_ctx *ctx, const char *s) +{ + char *p; + long dummy; + + dummy = strtol(s, &p, 0); + if (p != s) + isl_die(ctx, isl_error_invalid, "name looks like a number", + return 0); + + return 1; +} + +/* Is it possible for the given dimension type to have a tuple id? + */ +static int space_can_have_id(__isl_keep isl_space *space, + enum isl_dim_type type) +{ + if (!space) + return 0; + if (isl_space_is_params(space)) + isl_die(space->ctx, isl_error_invalid, + "parameter spaces don't have tuple ids", return 0); + if (isl_space_is_set(space) && type != isl_dim_set) + isl_die(space->ctx, isl_error_invalid, + "set spaces can only have a set id", return 0); + if (type != isl_dim_in && type != isl_dim_out) + isl_die(space->ctx, isl_error_invalid, + "only input, output and set tuples can have ids", + return 0); + + return 1; +} + +/* Does the tuple have an id? + */ +isl_bool isl_space_has_tuple_id(__isl_keep isl_space *dim, + enum isl_dim_type type) +{ + if (!space_can_have_id(dim, type)) + return isl_bool_error; + return dim->tuple_id[type - isl_dim_in] != NULL; +} + +__isl_give isl_id *isl_space_get_tuple_id(__isl_keep isl_space *dim, + enum isl_dim_type type) +{ + int has_id; + + if (!dim) + return NULL; + has_id = isl_space_has_tuple_id(dim, type); + if (has_id < 0) + return NULL; + if (!has_id) + isl_die(dim->ctx, isl_error_invalid, + "tuple has no id", return NULL); + return isl_id_copy(dim->tuple_id[type - isl_dim_in]); +} + +__isl_give isl_space *isl_space_set_tuple_id(__isl_take isl_space *dim, + enum isl_dim_type type, __isl_take isl_id *id) +{ + dim = isl_space_cow(dim); + if (!dim || !id) + goto error; + if (type != isl_dim_in && type != isl_dim_out) + isl_die(dim->ctx, isl_error_invalid, + "only input, output and set tuples can have names", + goto error); + + isl_id_free(dim->tuple_id[type - isl_dim_in]); + dim->tuple_id[type - isl_dim_in] = id; + + return dim; +error: + isl_id_free(id); + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_reset_tuple_id(__isl_take isl_space *dim, + enum isl_dim_type type) +{ + dim = isl_space_cow(dim); + if (!dim) + return NULL; + if (type != isl_dim_in && type != isl_dim_out) + isl_die(dim->ctx, isl_error_invalid, + "only input, output and set tuples can have names", + goto error); + + isl_id_free(dim->tuple_id[type - isl_dim_in]); + dim->tuple_id[type - isl_dim_in] = NULL; + + return dim; +error: + isl_space_free(dim); + return NULL; +} + +/* Set the id of the given dimension of "space" to "id". + * If the dimension already has an id, then it is replaced. + * If the dimension is a parameter, then we need to change it + * in the nested spaces (if any) as well. + */ +__isl_give isl_space *isl_space_set_dim_id(__isl_take isl_space *space, + enum isl_dim_type type, unsigned pos, __isl_take isl_id *id) +{ + space = isl_space_cow(space); + if (!space || !id) + goto error; + + if (type == isl_dim_param) { + int i; + + for (i = 0; i < 2; ++i) { + if (!space->nested[i]) + continue; + space->nested[i] = + isl_space_set_dim_id(space->nested[i], + type, pos, isl_id_copy(id)); + if (!space->nested[i]) + goto error; + } + } + + isl_id_free(get_id(space, type, pos)); + return set_id(space, type, pos, id); +error: + isl_id_free(id); + isl_space_free(space); + return NULL; +} + +/* Reset the id of the given dimension of "space". + * If the dimension already has an id, then it is removed. + * If the dimension is a parameter, then we need to reset it + * in the nested spaces (if any) as well. + */ +__isl_give isl_space *isl_space_reset_dim_id(__isl_take isl_space *space, + enum isl_dim_type type, unsigned pos) +{ + space = isl_space_cow(space); + if (!space) + goto error; + + if (type == isl_dim_param) { + int i; + + for (i = 0; i < 2; ++i) { + if (!space->nested[i]) + continue; + space->nested[i] = + isl_space_reset_dim_id(space->nested[i], + type, pos); + if (!space->nested[i]) + goto error; + } + } + + isl_id_free(get_id(space, type, pos)); + return set_id(space, type, pos, NULL); +error: + isl_space_free(space); + return NULL; +} + +isl_bool isl_space_has_dim_id(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos) +{ + if (!dim) + return isl_bool_error; + return get_id(dim, type, pos) != NULL; +} + +__isl_give isl_id *isl_space_get_dim_id(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos) +{ + if (!dim) + return NULL; + if (!get_id(dim, type, pos)) + isl_die(dim->ctx, isl_error_invalid, + "dim has no id", return NULL); + return isl_id_copy(get_id(dim, type, pos)); +} + +__isl_give isl_space *isl_space_set_tuple_name(__isl_take isl_space *dim, + enum isl_dim_type type, const char *s) +{ + isl_id *id; + + if (!dim) + return NULL; + + if (!s) + return isl_space_reset_tuple_id(dim, type); + + if (!name_ok(dim->ctx, s)) + goto error; + + id = isl_id_alloc(dim->ctx, s, NULL); + return isl_space_set_tuple_id(dim, type, id); +error: + isl_space_free(dim); + return NULL; +} + +/* Does the tuple have a name? + */ +isl_bool isl_space_has_tuple_name(__isl_keep isl_space *space, + enum isl_dim_type type) +{ + isl_id *id; + + if (!space_can_have_id(space, type)) + return isl_bool_error; + id = space->tuple_id[type - isl_dim_in]; + return id && id->name; +} + +const char *isl_space_get_tuple_name(__isl_keep isl_space *dim, + enum isl_dim_type type) +{ + isl_id *id; + if (!dim) + return NULL; + if (type != isl_dim_in && type != isl_dim_out) + return NULL; + id = dim->tuple_id[type - isl_dim_in]; + return id ? id->name : NULL; +} + +__isl_give isl_space *isl_space_set_dim_name(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos, + const char *s) +{ + isl_id *id; + + if (!dim) + return NULL; + if (!s) + return isl_space_reset_dim_id(dim, type, pos); + if (!name_ok(dim->ctx, s)) + goto error; + id = isl_id_alloc(dim->ctx, s, NULL); + return isl_space_set_dim_id(dim, type, pos, id); +error: + isl_space_free(dim); + return NULL; +} + +/* Does the given dimension have a name? + */ +isl_bool isl_space_has_dim_name(__isl_keep isl_space *space, + enum isl_dim_type type, unsigned pos) +{ + isl_id *id; + + if (!space) + return isl_bool_error; + id = get_id(space, type, pos); + return id && id->name; +} + +__isl_keep const char *isl_space_get_dim_name(__isl_keep isl_space *dim, + enum isl_dim_type type, unsigned pos) +{ + isl_id *id = get_id(dim, type, pos); + return id ? id->name : NULL; +} + +int isl_space_find_dim_by_id(__isl_keep isl_space *dim, enum isl_dim_type type, + __isl_keep isl_id *id) +{ + int i; + int offset; + int n; + + if (!dim || !id) + return -1; + + offset = isl_space_offset(dim, type); + n = isl_space_dim(dim, type); + for (i = 0; i < n && offset + i < dim->n_id; ++i) + if (dim->ids[offset + i] == id) + return i; + + return -1; +} + +int isl_space_find_dim_by_name(__isl_keep isl_space *space, + enum isl_dim_type type, const char *name) +{ + int i; + int offset; + int n; + + if (!space || !name) + return -1; + + offset = isl_space_offset(space, type); + n = isl_space_dim(space, type); + for (i = 0; i < n && offset + i < space->n_id; ++i) + if (space->ids[offset + i]->name && + !strcmp(space->ids[offset + i]->name, name)) + return i; + + return -1; +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of "space". + */ +__isl_give isl_space *isl_space_reset_user(__isl_take isl_space *space) +{ + int i; + isl_ctx *ctx; + isl_id *id; + const char *name; + + if (!space) + return NULL; + + ctx = isl_space_get_ctx(space); + + for (i = 0; i < space->nparam && i < space->n_id; ++i) { + if (!isl_id_get_user(space->ids[i])) + continue; + space = isl_space_cow(space); + if (!space) + return NULL; + name = isl_id_get_name(space->ids[i]); + id = isl_id_alloc(ctx, name, NULL); + isl_id_free(space->ids[i]); + space->ids[i] = id; + if (!id) + return isl_space_free(space); + } + + for (i = 0; i < 2; ++i) { + if (!space->tuple_id[i]) + continue; + if (!isl_id_get_user(space->tuple_id[i])) + continue; + space = isl_space_cow(space); + if (!space) + return NULL; + name = isl_id_get_name(space->tuple_id[i]); + id = isl_id_alloc(ctx, name, NULL); + isl_id_free(space->tuple_id[i]); + space->tuple_id[i] = id; + if (!id) + return isl_space_free(space); + } + + for (i = 0; i < 2; ++i) { + if (!space->nested[i]) + continue; + space = isl_space_cow(space); + if (!space) + return NULL; + space->nested[i] = isl_space_reset_user(space->nested[i]); + if (!space->nested[i]) + return isl_space_free(space); + } + + return space; +} + +static __isl_keep isl_id *tuple_id(__isl_keep isl_space *dim, + enum isl_dim_type type) +{ + if (!dim) + return NULL; + if (type == isl_dim_in) + return dim->tuple_id[0]; + if (type == isl_dim_out) + return dim->tuple_id[1]; + return NULL; +} + +static __isl_keep isl_space *nested(__isl_keep isl_space *dim, + enum isl_dim_type type) +{ + if (!dim) + return NULL; + if (type == isl_dim_in) + return dim->nested[0]; + if (type == isl_dim_out) + return dim->nested[1]; + return NULL; +} + +/* Are the two spaces the same, apart from positions and names of parameters? + */ +static int isl_space_has_equal_tuples(__isl_keep isl_space *space1, + __isl_keep isl_space *space2) +{ + if (!space1 || !space2) + return -1; + if (space1 == space2) + return 1; + return isl_space_tuple_is_equal(space1, isl_dim_in, + space2, isl_dim_in) && + isl_space_tuple_is_equal(space1, isl_dim_out, + space2, isl_dim_out); +} + +/* Check if the tuple of type "type1" of "space1" is the same as + * the tuple of type "type2" of "space2". + * + * That is, check if the tuples have the same identifier, the same dimension + * and the same internal structure. + * The identifiers of the dimensions inside the tuples do not affect the result. + * + * Note that this function only checks the tuples themselves. + * If nested tuples are involved, then we need to be careful not + * to have result affected by possibly differing parameters + * in those nested tuples. + */ +isl_bool isl_space_tuple_is_equal(__isl_keep isl_space *space1, + enum isl_dim_type type1, __isl_keep isl_space *space2, + enum isl_dim_type type2) +{ + isl_id *id1, *id2; + isl_space *nested1, *nested2; + + if (!space1 || !space2) + return isl_bool_error; + + if (space1 == space2 && type1 == type2) + return isl_bool_true; + + if (n(space1, type1) != n(space2, type2)) + return isl_bool_false; + id1 = tuple_id(space1, type1); + id2 = tuple_id(space2, type2); + if (!id1 ^ !id2) + return isl_bool_false; + if (id1 && id1 != id2) + return isl_bool_false; + nested1 = nested(space1, type1); + nested2 = nested(space2, type2); + if (!nested1 ^ !nested2) + return isl_bool_false; + if (nested1 && !isl_space_has_equal_tuples(nested1, nested2)) + return isl_bool_false; + return isl_bool_true; +} + +/* This is the old, undocumented, name for isl_space_tuple_is_equal. + * It will be removed at some point. + */ +int isl_space_tuple_match(__isl_keep isl_space *space1, enum isl_dim_type type1, + __isl_keep isl_space *space2, enum isl_dim_type type2) +{ + return isl_space_tuple_is_equal(space1, type1, space2, type2); +} + +static int match(__isl_keep isl_space *dim1, enum isl_dim_type dim1_type, + __isl_keep isl_space *dim2, enum isl_dim_type dim2_type) +{ + int i; + + if (dim1 == dim2 && dim1_type == dim2_type) + return 1; + + if (!isl_space_tuple_is_equal(dim1, dim1_type, dim2, dim2_type)) + return 0; + + if (!dim1->ids && !dim2->ids) + return 1; + + for (i = 0; i < n(dim1, dim1_type); ++i) { + if (get_id(dim1, dim1_type, i) != get_id(dim2, dim2_type, i)) + return 0; + } + return 1; +} + +int isl_space_match(__isl_keep isl_space *dim1, enum isl_dim_type dim1_type, + __isl_keep isl_space *dim2, enum isl_dim_type dim2_type) +{ + if (!dim1 || !dim2) + return -1; + + return match(dim1, dim1_type, dim2, dim2_type); +} + +static void get_ids(__isl_keep isl_space *dim, enum isl_dim_type type, + unsigned first, unsigned n, __isl_keep isl_id **ids) +{ + int i; + + for (i = 0; i < n ; ++i) + ids[i] = get_id(dim, type, first + i); +} + +__isl_give isl_space *isl_space_extend(__isl_take isl_space *space, + unsigned nparam, unsigned n_in, unsigned n_out) +{ + isl_id **ids = NULL; + + if (!space) + return NULL; + if (space->nparam == nparam && + space->n_in == n_in && space->n_out == n_out) + return space; + + isl_assert(space->ctx, space->nparam <= nparam, goto error); + isl_assert(space->ctx, space->n_in <= n_in, goto error); + isl_assert(space->ctx, space->n_out <= n_out, goto error); + + space = isl_space_cow(space); + if (!space) + goto error; + + if (space->ids) { + unsigned n; + n = nparam + n_in + n_out; + if (n < nparam || n < n_in || n < n_out) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "overflow in total number of dimensions", + goto error); + ids = isl_calloc_array(space->ctx, isl_id *, n); + if (!ids) + goto error; + get_ids(space, isl_dim_param, 0, space->nparam, ids); + get_ids(space, isl_dim_in, 0, space->n_in, ids + nparam); + get_ids(space, isl_dim_out, 0, space->n_out, + ids + nparam + n_in); + free(space->ids); + space->ids = ids; + space->n_id = nparam + n_in + n_out; + } + space->nparam = nparam; + space->n_in = n_in; + space->n_out = n_out; + + return space; +error: + free(ids); + isl_space_free(space); + return NULL; +} + +__isl_give isl_space *isl_space_add_dims(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned n) +{ + dim = isl_space_reset(dim, type); + if (!dim) + return NULL; + switch (type) { + case isl_dim_param: + dim = isl_space_extend(dim, + dim->nparam + n, dim->n_in, dim->n_out); + if (dim && dim->nested[0] && + !(dim->nested[0] = isl_space_add_dims(dim->nested[0], + isl_dim_param, n))) + goto error; + if (dim && dim->nested[1] && + !(dim->nested[1] = isl_space_add_dims(dim->nested[1], + isl_dim_param, n))) + goto error; + return dim; + case isl_dim_in: + return isl_space_extend(dim, + dim->nparam, dim->n_in + n, dim->n_out); + case isl_dim_out: + return isl_space_extend(dim, + dim->nparam, dim->n_in, dim->n_out + n); + default: + isl_die(dim->ctx, isl_error_invalid, + "cannot add dimensions of specified type", goto error); + } +error: + isl_space_free(dim); + return NULL; +} + +static int valid_dim_type(enum isl_dim_type type) +{ + switch (type) { + case isl_dim_param: + case isl_dim_in: + case isl_dim_out: + return 1; + default: + return 0; + } +} + +/* Insert "n" dimensions of type "type" at position "pos". + * If we are inserting parameters, then they are also inserted in + * any nested spaces. + */ +__isl_give isl_space *isl_space_insert_dims(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned pos, unsigned n) +{ + isl_id **ids = NULL; + + if (!dim) + return NULL; + if (n == 0) + return isl_space_reset(dim, type); + + if (!valid_dim_type(type)) + isl_die(dim->ctx, isl_error_invalid, + "cannot insert dimensions of specified type", + goto error); + + isl_assert(dim->ctx, pos <= isl_space_dim(dim, type), goto error); + + dim = isl_space_cow(dim); + if (!dim) + return NULL; + + if (dim->ids) { + enum isl_dim_type t, o = isl_dim_param; + int off; + int s[3]; + ids = isl_calloc_array(dim->ctx, isl_id *, + dim->nparam + dim->n_in + dim->n_out + n); + if (!ids) + goto error; + off = 0; + s[isl_dim_param - o] = dim->nparam; + s[isl_dim_in - o] = dim->n_in; + s[isl_dim_out - o] = dim->n_out; + for (t = isl_dim_param; t <= isl_dim_out; ++t) { + if (t != type) { + get_ids(dim, t, 0, s[t - o], ids + off); + off += s[t - o]; + } else { + get_ids(dim, t, 0, pos, ids + off); + off += pos + n; + get_ids(dim, t, pos, s[t - o] - pos, ids + off); + off += s[t - o] - pos; + } + } + free(dim->ids); + dim->ids = ids; + dim->n_id = dim->nparam + dim->n_in + dim->n_out + n; + } + switch (type) { + case isl_dim_param: dim->nparam += n; break; + case isl_dim_in: dim->n_in += n; break; + case isl_dim_out: dim->n_out += n; break; + default: ; + } + dim = isl_space_reset(dim, type); + + if (type == isl_dim_param) { + if (dim && dim->nested[0] && + !(dim->nested[0] = isl_space_insert_dims(dim->nested[0], + isl_dim_param, pos, n))) + goto error; + if (dim && dim->nested[1] && + !(dim->nested[1] = isl_space_insert_dims(dim->nested[1], + isl_dim_param, pos, n))) + goto error; + } + + return dim; +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_move_dims(__isl_take isl_space *dim, + enum isl_dim_type dst_type, unsigned dst_pos, + enum isl_dim_type src_type, unsigned src_pos, unsigned n) +{ + int i; + + if (!dim) + return NULL; + if (n == 0) { + dim = isl_space_reset(dim, src_type); + return isl_space_reset(dim, dst_type); + } + + isl_assert(dim->ctx, src_pos + n <= isl_space_dim(dim, src_type), + goto error); + + if (dst_type == src_type && dst_pos == src_pos) + return dim; + + isl_assert(dim->ctx, dst_type != src_type, goto error); + + dim = isl_space_reset(dim, src_type); + dim = isl_space_reset(dim, dst_type); + + dim = isl_space_cow(dim); + if (!dim) + return NULL; + + if (dim->ids) { + isl_id **ids; + enum isl_dim_type t, o = isl_dim_param; + int off; + int s[3]; + ids = isl_calloc_array(dim->ctx, isl_id *, + dim->nparam + dim->n_in + dim->n_out); + if (!ids) + goto error; + off = 0; + s[isl_dim_param - o] = dim->nparam; + s[isl_dim_in - o] = dim->n_in; + s[isl_dim_out - o] = dim->n_out; + for (t = isl_dim_param; t <= isl_dim_out; ++t) { + if (t == dst_type) { + get_ids(dim, t, 0, dst_pos, ids + off); + off += dst_pos; + get_ids(dim, src_type, src_pos, n, ids + off); + off += n; + get_ids(dim, t, dst_pos, s[t - o] - dst_pos, + ids + off); + off += s[t - o] - dst_pos; + } else if (t == src_type) { + get_ids(dim, t, 0, src_pos, ids + off); + off += src_pos; + get_ids(dim, t, src_pos + n, + s[t - o] - src_pos - n, ids + off); + off += s[t - o] - src_pos - n; + } else { + get_ids(dim, t, 0, s[t - o], ids + off); + off += s[t - o]; + } + } + free(dim->ids); + dim->ids = ids; + dim->n_id = dim->nparam + dim->n_in + dim->n_out; + } + + switch (dst_type) { + case isl_dim_param: dim->nparam += n; break; + case isl_dim_in: dim->n_in += n; break; + case isl_dim_out: dim->n_out += n; break; + default: ; + } + + switch (src_type) { + case isl_dim_param: dim->nparam -= n; break; + case isl_dim_in: dim->n_in -= n; break; + case isl_dim_out: dim->n_out -= n; break; + default: ; + } + + if (dst_type != isl_dim_param && src_type != isl_dim_param) + return dim; + + for (i = 0; i < 2; ++i) { + if (!dim->nested[i]) + continue; + dim->nested[i] = isl_space_replace(dim->nested[i], + isl_dim_param, dim); + if (!dim->nested[i]) + goto error; + } + + return dim; +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_join(__isl_take isl_space *left, + __isl_take isl_space *right) +{ + isl_space *dim; + + if (!left || !right) + goto error; + + isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param), + goto error); + isl_assert(left->ctx, + isl_space_tuple_is_equal(left, isl_dim_out, right, isl_dim_in), + goto error); + + dim = isl_space_alloc(left->ctx, left->nparam, left->n_in, right->n_out); + if (!dim) + goto error; + + dim = copy_ids(dim, isl_dim_param, 0, left, isl_dim_param); + dim = copy_ids(dim, isl_dim_in, 0, left, isl_dim_in); + dim = copy_ids(dim, isl_dim_out, 0, right, isl_dim_out); + + if (dim && left->tuple_id[0] && + !(dim->tuple_id[0] = isl_id_copy(left->tuple_id[0]))) + goto error; + if (dim && right->tuple_id[1] && + !(dim->tuple_id[1] = isl_id_copy(right->tuple_id[1]))) + goto error; + if (dim && left->nested[0] && + !(dim->nested[0] = isl_space_copy(left->nested[0]))) + goto error; + if (dim && right->nested[1] && + !(dim->nested[1] = isl_space_copy(right->nested[1]))) + goto error; + + isl_space_free(left); + isl_space_free(right); + + return dim; +error: + isl_space_free(left); + isl_space_free(right); + return NULL; +} + +/* Given two map spaces { A -> C } and { B -> D }, construct the space + * { [A -> B] -> [C -> D] }. + * Given two set spaces { A } and { B }, construct the space { [A -> B] }. + */ +__isl_give isl_space *isl_space_product(__isl_take isl_space *left, + __isl_take isl_space *right) +{ + isl_space *dom1, *dom2, *nest1, *nest2; + int is_set; + + if (!left || !right) + goto error; + + is_set = isl_space_is_set(left); + if (is_set != isl_space_is_set(right)) + isl_die(isl_space_get_ctx(left), isl_error_invalid, + "expecting either two set spaces or two map spaces", + goto error); + if (is_set) + return isl_space_range_product(left, right); + + isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param), + goto error); + + dom1 = isl_space_domain(isl_space_copy(left)); + dom2 = isl_space_domain(isl_space_copy(right)); + nest1 = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2)); + + dom1 = isl_space_range(left); + dom2 = isl_space_range(right); + nest2 = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2)); + + return isl_space_join(isl_space_reverse(nest1), nest2); +error: + isl_space_free(left); + isl_space_free(right); + return NULL; +} + +/* Given two spaces { A -> C } and { B -> C }, construct the space + * { [A -> B] -> C } + */ +__isl_give isl_space *isl_space_domain_product(__isl_take isl_space *left, + __isl_take isl_space *right) +{ + isl_space *ran, *dom1, *dom2, *nest; + + if (!left || !right) + goto error; + + if (!match(left, isl_dim_param, right, isl_dim_param)) + isl_die(left->ctx, isl_error_invalid, + "parameters need to match", goto error); + if (!isl_space_tuple_is_equal(left, isl_dim_out, right, isl_dim_out)) + isl_die(left->ctx, isl_error_invalid, + "ranges need to match", goto error); + + ran = isl_space_range(isl_space_copy(left)); + + dom1 = isl_space_domain(left); + dom2 = isl_space_domain(right); + nest = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2)); + + return isl_space_join(isl_space_reverse(nest), ran); +error: + isl_space_free(left); + isl_space_free(right); + return NULL; +} + +__isl_give isl_space *isl_space_range_product(__isl_take isl_space *left, + __isl_take isl_space *right) +{ + isl_space *dom, *ran1, *ran2, *nest; + + if (!left || !right) + goto error; + + isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param), + goto error); + if (!isl_space_tuple_is_equal(left, isl_dim_in, right, isl_dim_in)) + isl_die(left->ctx, isl_error_invalid, + "domains need to match", goto error); + + dom = isl_space_domain(isl_space_copy(left)); + + ran1 = isl_space_range(left); + ran2 = isl_space_range(right); + nest = isl_space_wrap(isl_space_join(isl_space_reverse(ran1), ran2)); + + return isl_space_join(isl_space_reverse(dom), nest); +error: + isl_space_free(left); + isl_space_free(right); + return NULL; +} + +/* Given a space of the form [A -> B] -> [C -> D], return the space A -> C. + */ +__isl_give isl_space *isl_space_factor_domain(__isl_take isl_space *space) +{ + space = isl_space_domain_factor_domain(space); + space = isl_space_range_factor_domain(space); + return space; +} + +/* Given a space of the form [A -> B] -> C, return the space A -> C. + */ +__isl_give isl_space *isl_space_domain_factor_domain( + __isl_take isl_space *space) +{ + isl_space *nested; + isl_space *domain; + + if (!space) + return NULL; + if (!isl_space_domain_is_wrapping(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "domain not a product", return isl_space_free(space)); + + nested = space->nested[0]; + domain = isl_space_copy(space); + domain = isl_space_drop_dims(domain, isl_dim_in, + nested->n_in, nested->n_out); + if (!domain) + return isl_space_free(space); + if (nested->tuple_id[0]) { + domain->tuple_id[0] = isl_id_copy(nested->tuple_id[0]); + if (!domain->tuple_id[0]) + goto error; + } + if (nested->nested[0]) { + domain->nested[0] = isl_space_copy(nested->nested[0]); + if (!domain->nested[0]) + goto error; + } + + isl_space_free(space); + return domain; +error: + isl_space_free(space); + isl_space_free(domain); + return NULL; +} + +/* Given a space of the form [A -> B] -> C, return the space B -> C. + */ +__isl_give isl_space *isl_space_domain_factor_range( + __isl_take isl_space *space) +{ + isl_space *nested; + isl_space *range; + + if (!space) + return NULL; + if (!isl_space_domain_is_wrapping(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "domain not a product", return isl_space_free(space)); + + nested = space->nested[0]; + range = isl_space_copy(space); + range = isl_space_drop_dims(range, isl_dim_in, 0, nested->n_in); + if (!range) + return isl_space_free(space); + if (nested->tuple_id[1]) { + range->tuple_id[0] = isl_id_copy(nested->tuple_id[1]); + if (!range->tuple_id[0]) + goto error; + } + if (nested->nested[1]) { + range->nested[0] = isl_space_copy(nested->nested[1]); + if (!range->nested[0]) + goto error; + } + + isl_space_free(space); + return range; +error: + isl_space_free(space); + isl_space_free(range); + return NULL; +} + +/* Given a space of the form A -> [B -> C], return the space A -> B. + */ +__isl_give isl_space *isl_space_range_factor_domain( + __isl_take isl_space *space) +{ + isl_space *nested; + isl_space *domain; + + if (!space) + return NULL; + if (!isl_space_range_is_wrapping(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "range not a product", return isl_space_free(space)); + + nested = space->nested[1]; + domain = isl_space_copy(space); + domain = isl_space_drop_dims(domain, isl_dim_out, + nested->n_in, nested->n_out); + if (!domain) + return isl_space_free(space); + if (nested->tuple_id[0]) { + domain->tuple_id[1] = isl_id_copy(nested->tuple_id[0]); + if (!domain->tuple_id[1]) + goto error; + } + if (nested->nested[0]) { + domain->nested[1] = isl_space_copy(nested->nested[0]); + if (!domain->nested[1]) + goto error; + } + + isl_space_free(space); + return domain; +error: + isl_space_free(space); + isl_space_free(domain); + return NULL; +} + +/* Internal function that selects the range of the map that is + * embedded in either a set space or the range of a map space. + * In particular, given a space of the form [A -> B], return the space B. + * Given a space of the form A -> [B -> C], return the space A -> C. + */ +static __isl_give isl_space *range_factor_range(__isl_take isl_space *space) +{ + isl_space *nested; + isl_space *range; + + if (!space) + return NULL; + + nested = space->nested[1]; + range = isl_space_copy(space); + range = isl_space_drop_dims(range, isl_dim_out, 0, nested->n_in); + if (!range) + return isl_space_free(space); + if (nested->tuple_id[1]) { + range->tuple_id[1] = isl_id_copy(nested->tuple_id[1]); + if (!range->tuple_id[1]) + goto error; + } + if (nested->nested[1]) { + range->nested[1] = isl_space_copy(nested->nested[1]); + if (!range->nested[1]) + goto error; + } + + isl_space_free(space); + return range; +error: + isl_space_free(space); + isl_space_free(range); + return NULL; +} + +/* Given a space of the form A -> [B -> C], return the space A -> C. + */ +__isl_give isl_space *isl_space_range_factor_range( + __isl_take isl_space *space) +{ + if (!space) + return NULL; + if (!isl_space_range_is_wrapping(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "range not a product", return isl_space_free(space)); + + return range_factor_range(space); +} + +/* Given a space of the form [A -> B], return the space B. + */ +static __isl_give isl_space *set_factor_range(__isl_take isl_space *space) +{ + if (!space) + return NULL; + if (!isl_space_is_wrapping(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "not a product", return isl_space_free(space)); + + return range_factor_range(space); +} + +/* Given a space of the form [A -> B] -> [C -> D], return the space B -> D. + * Given a space of the form [A -> B], return the space B. + */ +__isl_give isl_space *isl_space_factor_range(__isl_take isl_space *space) +{ + if (!space) + return NULL; + if (isl_space_is_set(space)) + return set_factor_range(space); + space = isl_space_domain_factor_range(space); + space = isl_space_range_factor_range(space); + return space; +} + +__isl_give isl_space *isl_space_map_from_set(__isl_take isl_space *dim) +{ + isl_ctx *ctx; + isl_id **ids = NULL; + + if (!dim) + return NULL; + ctx = isl_space_get_ctx(dim); + if (!isl_space_is_set(dim)) + isl_die(ctx, isl_error_invalid, "not a set space", goto error); + dim = isl_space_cow(dim); + if (!dim) + return NULL; + if (dim->ids) { + ids = isl_calloc_array(dim->ctx, isl_id *, + dim->nparam + dim->n_out + dim->n_out); + if (!ids) + goto error; + get_ids(dim, isl_dim_param, 0, dim->nparam, ids); + get_ids(dim, isl_dim_out, 0, dim->n_out, ids + dim->nparam); + } + dim->n_in = dim->n_out; + if (ids) { + free(dim->ids); + dim->ids = ids; + dim->n_id = dim->nparam + dim->n_out + dim->n_out; + dim = copy_ids(dim, isl_dim_out, 0, dim, isl_dim_in); + } + isl_id_free(dim->tuple_id[0]); + dim->tuple_id[0] = isl_id_copy(dim->tuple_id[1]); + isl_space_free(dim->nested[0]); + dim->nested[0] = isl_space_copy(dim->nested[1]); + return dim; +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_map_from_domain_and_range( + __isl_take isl_space *domain, __isl_take isl_space *range) +{ + if (!domain || !range) + goto error; + if (!isl_space_is_set(domain)) + isl_die(isl_space_get_ctx(domain), isl_error_invalid, + "domain is not a set space", goto error); + if (!isl_space_is_set(range)) + isl_die(isl_space_get_ctx(range), isl_error_invalid, + "range is not a set space", goto error); + return isl_space_join(isl_space_reverse(domain), range); +error: + isl_space_free(domain); + isl_space_free(range); + return NULL; +} + +static __isl_give isl_space *set_ids(__isl_take isl_space *dim, + enum isl_dim_type type, + unsigned first, unsigned n, __isl_take isl_id **ids) +{ + int i; + + for (i = 0; i < n ; ++i) + dim = set_id(dim, type, first + i, ids[i]); + + return dim; +} + +__isl_give isl_space *isl_space_reverse(__isl_take isl_space *dim) +{ + unsigned t; + isl_space *nested; + isl_id **ids = NULL; + isl_id *id; + + if (!dim) + return NULL; + if (match(dim, isl_dim_in, dim, isl_dim_out)) + return dim; + + dim = isl_space_cow(dim); + if (!dim) + return NULL; + + id = dim->tuple_id[0]; + dim->tuple_id[0] = dim->tuple_id[1]; + dim->tuple_id[1] = id; + + nested = dim->nested[0]; + dim->nested[0] = dim->nested[1]; + dim->nested[1] = nested; + + if (dim->ids) { + int n_id = dim->n_in + dim->n_out; + ids = isl_alloc_array(dim->ctx, isl_id *, n_id); + if (n_id && !ids) + goto error; + get_ids(dim, isl_dim_in, 0, dim->n_in, ids); + get_ids(dim, isl_dim_out, 0, dim->n_out, ids + dim->n_in); + } + + t = dim->n_in; + dim->n_in = dim->n_out; + dim->n_out = t; + + if (dim->ids) { + dim = set_ids(dim, isl_dim_out, 0, dim->n_out, ids); + dim = set_ids(dim, isl_dim_in, 0, dim->n_in, ids + dim->n_out); + free(ids); + } + + return dim; +error: + free(ids); + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_drop_dims(__isl_take isl_space *dim, + enum isl_dim_type type, unsigned first, unsigned num) +{ + int i; + + if (!dim) + return NULL; + + if (num == 0) + return isl_space_reset(dim, type); + + if (!valid_dim_type(type)) + isl_die(dim->ctx, isl_error_invalid, + "cannot drop dimensions of specified type", goto error); + + if (first + num > n(dim, type) || first + num < first) + isl_die(isl_space_get_ctx(dim), isl_error_invalid, + "index out of bounds", return isl_space_free(dim)); + dim = isl_space_cow(dim); + if (!dim) + goto error; + if (dim->ids) { + dim = extend_ids(dim); + if (!dim) + goto error; + for (i = 0; i < num; ++i) + isl_id_free(get_id(dim, type, first + i)); + for (i = first+num; i < n(dim, type); ++i) + set_id(dim, type, i - num, get_id(dim, type, i)); + switch (type) { + case isl_dim_param: + get_ids(dim, isl_dim_in, 0, dim->n_in, + dim->ids + offset(dim, isl_dim_in) - num); + case isl_dim_in: + get_ids(dim, isl_dim_out, 0, dim->n_out, + dim->ids + offset(dim, isl_dim_out) - num); + default: + ; + } + dim->n_id -= num; + } + switch (type) { + case isl_dim_param: dim->nparam -= num; break; + case isl_dim_in: dim->n_in -= num; break; + case isl_dim_out: dim->n_out -= num; break; + default: ; + } + dim = isl_space_reset(dim, type); + if (type == isl_dim_param) { + if (dim && dim->nested[0] && + !(dim->nested[0] = isl_space_drop_dims(dim->nested[0], + isl_dim_param, first, num))) + goto error; + if (dim && dim->nested[1] && + !(dim->nested[1] = isl_space_drop_dims(dim->nested[1], + isl_dim_param, first, num))) + goto error; + } + return dim; +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_drop_inputs(__isl_take isl_space *dim, + unsigned first, unsigned n) +{ + if (!dim) + return NULL; + return isl_space_drop_dims(dim, isl_dim_in, first, n); +} + +__isl_give isl_space *isl_space_drop_outputs(__isl_take isl_space *dim, + unsigned first, unsigned n) +{ + if (!dim) + return NULL; + return isl_space_drop_dims(dim, isl_dim_out, first, n); +} + +__isl_give isl_space *isl_space_domain(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + dim = isl_space_drop_outputs(dim, 0, dim->n_out); + dim = isl_space_reverse(dim); + dim = mark_as_set(dim); + return dim; +} + +__isl_give isl_space *isl_space_from_domain(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + if (!isl_space_is_set(dim)) + isl_die(isl_space_get_ctx(dim), isl_error_invalid, + "not a set space", goto error); + dim = isl_space_reverse(dim); + dim = isl_space_reset(dim, isl_dim_out); + return dim; +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_range(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + dim = isl_space_drop_inputs(dim, 0, dim->n_in); + dim = mark_as_set(dim); + return dim; +} + +__isl_give isl_space *isl_space_from_range(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + if (!isl_space_is_set(dim)) + isl_die(isl_space_get_ctx(dim), isl_error_invalid, + "not a set space", goto error); + return isl_space_reset(dim, isl_dim_in); +error: + isl_space_free(dim); + return NULL; +} + +/* Given a map space A -> B, return the map space [A -> B] -> A. + */ +__isl_give isl_space *isl_space_domain_map(__isl_take isl_space *space) +{ + isl_space *domain; + + domain = isl_space_from_range(isl_space_domain(isl_space_copy(space))); + space = isl_space_from_domain(isl_space_wrap(space)); + space = isl_space_join(space, domain); + + return space; +} + +/* Given a map space A -> B, return the map space [A -> B] -> B. + */ +__isl_give isl_space *isl_space_range_map(__isl_take isl_space *space) +{ + isl_space *range; + + range = isl_space_from_range(isl_space_range(isl_space_copy(space))); + space = isl_space_from_domain(isl_space_wrap(space)); + space = isl_space_join(space, range); + + return space; +} + +__isl_give isl_space *isl_space_params(__isl_take isl_space *space) +{ + if (isl_space_is_params(space)) + return space; + space = isl_space_drop_dims(space, + isl_dim_in, 0, isl_space_dim(space, isl_dim_in)); + space = isl_space_drop_dims(space, + isl_dim_out, 0, isl_space_dim(space, isl_dim_out)); + space = mark_as_params(space); + return space; +} + +__isl_give isl_space *isl_space_set_from_params(__isl_take isl_space *space) +{ + if (!space) + return NULL; + if (!isl_space_is_params(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "not a parameter space", goto error); + return isl_space_reset(space, isl_dim_set); +error: + isl_space_free(space); + return NULL; +} + +__isl_give isl_space *isl_space_as_set_space(__isl_take isl_space *dim) +{ + dim = isl_space_cow(dim); + if (!dim) + return NULL; + + dim->n_out += dim->n_in; + dim->n_in = 0; + dim = isl_space_reset(dim, isl_dim_in); + dim = isl_space_reset(dim, isl_dim_out); + + return dim; +} + +__isl_give isl_space *isl_space_underlying(__isl_take isl_space *dim, + unsigned n_div) +{ + int i; + + if (!dim) + return NULL; + if (n_div == 0 && + dim->nparam == 0 && dim->n_in == 0 && dim->n_id == 0) + return isl_space_reset(isl_space_reset(dim, isl_dim_in), isl_dim_out); + dim = isl_space_cow(dim); + if (!dim) + return NULL; + dim->n_out += dim->nparam + dim->n_in + n_div; + dim->nparam = 0; + dim->n_in = 0; + + for (i = 0; i < dim->n_id; ++i) + isl_id_free(get_id(dim, isl_dim_out, i)); + dim->n_id = 0; + dim = isl_space_reset(dim, isl_dim_in); + dim = isl_space_reset(dim, isl_dim_out); + + return dim; +} + +/* Are the two spaces the same, including positions and names of parameters? + */ +isl_bool isl_space_is_equal(__isl_keep isl_space *dim1, + __isl_keep isl_space *dim2) +{ + if (!dim1 || !dim2) + return isl_bool_error; + if (dim1 == dim2) + return isl_bool_true; + return match(dim1, isl_dim_param, dim2, isl_dim_param) && + isl_space_tuple_is_equal(dim1, isl_dim_in, dim2, isl_dim_in) && + isl_space_tuple_is_equal(dim1, isl_dim_out, dim2, isl_dim_out); +} + +/* Is space1 equal to the domain of space2? + * + * In the internal version we also allow space2 to be the space of a set, + * provided space1 is a parameter space. + */ +isl_bool isl_space_is_domain_internal(__isl_keep isl_space *space1, + __isl_keep isl_space *space2) +{ + if (!space1 || !space2) + return isl_bool_error; + if (!isl_space_is_set(space1)) + return isl_bool_false; + return match(space1, isl_dim_param, space2, isl_dim_param) && + isl_space_tuple_is_equal(space1, isl_dim_set, + space2, isl_dim_in); +} + +/* Is space1 equal to the domain of space2? + */ +isl_bool isl_space_is_domain(__isl_keep isl_space *space1, + __isl_keep isl_space *space2) +{ + if (!space2) + return isl_bool_error; + if (!isl_space_is_map(space2)) + return isl_bool_false; + return isl_space_is_domain_internal(space1, space2); +} + +/* Is space1 equal to the range of space2? + * + * In the internal version, space2 is allowed to be the space of a set, + * in which case it should be equal to space1. + */ +isl_bool isl_space_is_range_internal(__isl_keep isl_space *space1, + __isl_keep isl_space *space2) +{ + if (!space1 || !space2) + return isl_bool_error; + if (!isl_space_is_set(space1)) + return isl_bool_false; + return match(space1, isl_dim_param, space2, isl_dim_param) && + isl_space_tuple_is_equal(space1, isl_dim_set, + space2, isl_dim_out); +} + +/* Is space1 equal to the range of space2? + */ +isl_bool isl_space_is_range(__isl_keep isl_space *space1, + __isl_keep isl_space *space2) +{ + if (!space2) + return isl_bool_error; + if (!isl_space_is_map(space2)) + return isl_bool_false; + return isl_space_is_range_internal(space1, space2); +} + +int isl_space_compatible(__isl_keep isl_space *dim1, + __isl_keep isl_space *dim2) +{ + return dim1->nparam == dim2->nparam && + dim1->n_in + dim1->n_out == dim2->n_in + dim2->n_out; +} + +/* Update "hash" by hashing in "space". + * Changes in this function should be reflected in isl_hash_space_domain. + */ +static uint32_t isl_hash_space(uint32_t hash, __isl_keep isl_space *space) +{ + int i; + isl_id *id; + + if (!space) + return hash; + + isl_hash_byte(hash, space->nparam % 256); + isl_hash_byte(hash, space->n_in % 256); + isl_hash_byte(hash, space->n_out % 256); + + for (i = 0; i < space->nparam; ++i) { + id = get_id(space, isl_dim_param, i); + hash = isl_hash_id(hash, id); + } + + id = tuple_id(space, isl_dim_in); + hash = isl_hash_id(hash, id); + id = tuple_id(space, isl_dim_out); + hash = isl_hash_id(hash, id); + + hash = isl_hash_space(hash, space->nested[0]); + hash = isl_hash_space(hash, space->nested[1]); + + return hash; +} + +/* Update "hash" by hashing in the domain of "space". + * The result of this function is equal to the result of applying + * isl_hash_space to the domain of "space". + */ +static uint32_t isl_hash_space_domain(uint32_t hash, + __isl_keep isl_space *space) +{ + int i; + isl_id *id; + + if (!space) + return hash; + + isl_hash_byte(hash, space->nparam % 256); + isl_hash_byte(hash, 0); + isl_hash_byte(hash, space->n_in % 256); + + for (i = 0; i < space->nparam; ++i) { + id = get_id(space, isl_dim_param, i); + hash = isl_hash_id(hash, id); + } + + hash = isl_hash_id(hash, &isl_id_none); + id = tuple_id(space, isl_dim_in); + hash = isl_hash_id(hash, id); + + hash = isl_hash_space(hash, space->nested[0]); + + return hash; +} + +uint32_t isl_space_get_hash(__isl_keep isl_space *dim) +{ + uint32_t hash; + + if (!dim) + return 0; + + hash = isl_hash_init(); + hash = isl_hash_space(hash, dim); + + return hash; +} + +/* Return the hash value of the domain of "space". + * That is, isl_space_get_domain_hash(space) is equal to + * isl_space_get_hash(isl_space_domain(space)). + */ +uint32_t isl_space_get_domain_hash(__isl_keep isl_space *space) +{ + uint32_t hash; + + if (!space) + return 0; + + hash = isl_hash_init(); + hash = isl_hash_space_domain(hash, space); + + return hash; +} + +isl_bool isl_space_is_wrapping(__isl_keep isl_space *dim) +{ + if (!dim) + return isl_bool_error; + + if (!isl_space_is_set(dim)) + return isl_bool_false; + + return dim->nested[1] != NULL; +} + +/* Is "space" the space of a map where the domain is a wrapped map space? + */ +isl_bool isl_space_domain_is_wrapping(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + + if (isl_space_is_set(space)) + return isl_bool_false; + + return space->nested[0] != NULL; +} + +/* Is "space" the space of a map where the range is a wrapped map space? + */ +isl_bool isl_space_range_is_wrapping(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + + if (isl_space_is_set(space)) + return isl_bool_false; + + return space->nested[1] != NULL; +} + +__isl_give isl_space *isl_space_wrap(__isl_take isl_space *dim) +{ + isl_space *wrap; + + if (!dim) + return NULL; + + wrap = isl_space_set_alloc(dim->ctx, + dim->nparam, dim->n_in + dim->n_out); + + wrap = copy_ids(wrap, isl_dim_param, 0, dim, isl_dim_param); + wrap = copy_ids(wrap, isl_dim_set, 0, dim, isl_dim_in); + wrap = copy_ids(wrap, isl_dim_set, dim->n_in, dim, isl_dim_out); + + if (!wrap) + goto error; + + wrap->nested[1] = dim; + + return wrap; +error: + isl_space_free(dim); + return NULL; +} + +__isl_give isl_space *isl_space_unwrap(__isl_take isl_space *dim) +{ + isl_space *unwrap; + + if (!dim) + return NULL; + + if (!isl_space_is_wrapping(dim)) + isl_die(dim->ctx, isl_error_invalid, "not a wrapping space", + goto error); + + unwrap = isl_space_copy(dim->nested[1]); + isl_space_free(dim); + + return unwrap; +error: + isl_space_free(dim); + return NULL; +} + +int isl_space_is_named_or_nested(__isl_keep isl_space *dim, enum isl_dim_type type) +{ + if (type != isl_dim_in && type != isl_dim_out) + return 0; + if (!dim) + return -1; + if (dim->tuple_id[type - isl_dim_in]) + return 1; + if (dim->nested[type - isl_dim_in]) + return 1; + return 0; +} + +int isl_space_may_be_set(__isl_keep isl_space *dim) +{ + if (!dim) + return -1; + if (isl_space_is_set(dim)) + return 1; + if (isl_space_dim(dim, isl_dim_in) != 0) + return 0; + if (isl_space_is_named_or_nested(dim, isl_dim_in)) + return 0; + return 1; +} + +__isl_give isl_space *isl_space_reset(__isl_take isl_space *dim, + enum isl_dim_type type) +{ + if (!isl_space_is_named_or_nested(dim, type)) + return dim; + + dim = isl_space_cow(dim); + if (!dim) + return NULL; + + isl_id_free(dim->tuple_id[type - isl_dim_in]); + dim->tuple_id[type - isl_dim_in] = NULL; + isl_space_free(dim->nested[type - isl_dim_in]); + dim->nested[type - isl_dim_in] = NULL; + + return dim; +} + +__isl_give isl_space *isl_space_flatten(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + if (!dim->nested[0] && !dim->nested[1]) + return dim; + + if (dim->nested[0]) + dim = isl_space_reset(dim, isl_dim_in); + if (dim && dim->nested[1]) + dim = isl_space_reset(dim, isl_dim_out); + + return dim; +} + +__isl_give isl_space *isl_space_flatten_domain(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + if (!dim->nested[0]) + return dim; + + return isl_space_reset(dim, isl_dim_in); +} + +__isl_give isl_space *isl_space_flatten_range(__isl_take isl_space *dim) +{ + if (!dim) + return NULL; + if (!dim->nested[1]) + return dim; + + return isl_space_reset(dim, isl_dim_out); +} + +/* Replace the dimensions of the given type of dst by those of src. + */ +__isl_give isl_space *isl_space_replace(__isl_take isl_space *dst, + enum isl_dim_type type, __isl_keep isl_space *src) +{ + dst = isl_space_cow(dst); + + if (!dst || !src) + goto error; + + dst = isl_space_drop_dims(dst, type, 0, isl_space_dim(dst, type)); + dst = isl_space_add_dims(dst, type, isl_space_dim(src, type)); + dst = copy_ids(dst, type, 0, src, type); + + if (dst && type == isl_dim_param) { + int i; + for (i = 0; i <= 1; ++i) { + if (!dst->nested[i]) + continue; + dst->nested[i] = isl_space_replace(dst->nested[i], + type, src); + if (!dst->nested[i]) + goto error; + } + } + + return dst; +error: + isl_space_free(dst); + return NULL; +} + +/* Given a dimension specification "dim" of a set, create a dimension + * specification for the lift of the set. In particular, the result + * is of the form [dim -> local[..]], with n_local variables in the + * range of the wrapped map. + */ +__isl_give isl_space *isl_space_lift(__isl_take isl_space *dim, unsigned n_local) +{ + isl_space *local_dim; + + if (!dim) + return NULL; + + local_dim = isl_space_dup(dim); + local_dim = isl_space_drop_dims(local_dim, isl_dim_set, 0, dim->n_out); + local_dim = isl_space_add_dims(local_dim, isl_dim_set, n_local); + local_dim = isl_space_set_tuple_name(local_dim, isl_dim_set, "local"); + dim = isl_space_join(isl_space_from_domain(dim), + isl_space_from_range(local_dim)); + dim = isl_space_wrap(dim); + dim = isl_space_set_tuple_name(dim, isl_dim_set, "lifted"); + + return dim; +} + +isl_bool isl_space_can_zip(__isl_keep isl_space *dim) +{ + if (!dim) + return isl_bool_error; + + return dim->nested[0] && dim->nested[1]; +} + +__isl_give isl_space *isl_space_zip(__isl_take isl_space *dim) +{ + isl_space *dom, *ran; + isl_space *dom_dom, *dom_ran, *ran_dom, *ran_ran; + + if (!isl_space_can_zip(dim)) + isl_die(dim->ctx, isl_error_invalid, "dim cannot be zipped", + goto error); + + if (!dim) + return NULL; + dom = isl_space_unwrap(isl_space_domain(isl_space_copy(dim))); + ran = isl_space_unwrap(isl_space_range(dim)); + dom_dom = isl_space_domain(isl_space_copy(dom)); + dom_ran = isl_space_range(dom); + ran_dom = isl_space_domain(isl_space_copy(ran)); + ran_ran = isl_space_range(ran); + dom = isl_space_join(isl_space_from_domain(dom_dom), + isl_space_from_range(ran_dom)); + ran = isl_space_join(isl_space_from_domain(dom_ran), + isl_space_from_range(ran_ran)); + return isl_space_join(isl_space_from_domain(isl_space_wrap(dom)), + isl_space_from_range(isl_space_wrap(ran))); +error: + isl_space_free(dim); + return NULL; +} + +/* Can we apply isl_space_curry to "space"? + * That is, does it have a nested relation in its domain? + */ +isl_bool isl_space_can_curry(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + + return !!space->nested[0]; +} + +/* Given a space (A -> B) -> C, return the corresponding space + * A -> (B -> C). + */ +__isl_give isl_space *isl_space_curry(__isl_take isl_space *space) +{ + isl_space *dom, *ran; + isl_space *dom_dom, *dom_ran; + + if (!space) + return NULL; + + if (!isl_space_can_curry(space)) + isl_die(space->ctx, isl_error_invalid, + "space cannot be curried", goto error); + + dom = isl_space_unwrap(isl_space_domain(isl_space_copy(space))); + ran = isl_space_range(space); + dom_dom = isl_space_domain(isl_space_copy(dom)); + dom_ran = isl_space_range(dom); + ran = isl_space_join(isl_space_from_domain(dom_ran), + isl_space_from_range(ran)); + return isl_space_join(isl_space_from_domain(dom_dom), + isl_space_from_range(isl_space_wrap(ran))); +error: + isl_space_free(space); + return NULL; +} + +/* Can isl_space_range_curry be applied to "space"? + * That is, does it have a nested relation in its range, + * the domain of which is itself a nested relation? + */ +isl_bool isl_space_can_range_curry(__isl_keep isl_space *space) +{ + isl_bool can; + + if (!space) + return isl_bool_error; + can = isl_space_range_is_wrapping(space); + if (can < 0 || !can) + return can; + return isl_space_can_curry(space->nested[1]); +} + +/* Given a space A -> ((B -> C) -> D), return the corresponding space + * A -> (B -> (C -> D)). + */ +__isl_give isl_space *isl_space_range_curry(__isl_take isl_space *space) +{ + if (!space) + return NULL; + + if (!isl_space_can_range_curry(space)) + isl_die(isl_space_get_ctx(space), isl_error_invalid, + "space range cannot be curried", + return isl_space_free(space)); + + space = isl_space_cow(space); + if (!space) + return NULL; + space->nested[1] = isl_space_curry(space->nested[1]); + if (!space->nested[1]) + return isl_space_free(space); + + return space; +} + +/* Can we apply isl_space_uncurry to "space"? + * That is, does it have a nested relation in its range? + */ +isl_bool isl_space_can_uncurry(__isl_keep isl_space *space) +{ + if (!space) + return isl_bool_error; + + return !!space->nested[1]; +} + +/* Given a space A -> (B -> C), return the corresponding space + * (A -> B) -> C. + */ +__isl_give isl_space *isl_space_uncurry(__isl_take isl_space *space) +{ + isl_space *dom, *ran; + isl_space *ran_dom, *ran_ran; + + if (!space) + return NULL; + + if (!isl_space_can_uncurry(space)) + isl_die(space->ctx, isl_error_invalid, + "space cannot be uncurried", + return isl_space_free(space)); + + dom = isl_space_domain(isl_space_copy(space)); + ran = isl_space_unwrap(isl_space_range(space)); + ran_dom = isl_space_domain(isl_space_copy(ran)); + ran_ran = isl_space_range(ran); + dom = isl_space_join(isl_space_from_domain(dom), + isl_space_from_range(ran_dom)); + return isl_space_join(isl_space_from_domain(isl_space_wrap(dom)), + isl_space_from_range(ran_ran)); +} + +int isl_space_has_named_params(__isl_keep isl_space *dim) +{ + int i; + unsigned off; + + if (!dim) + return -1; + if (dim->nparam == 0) + return 1; + off = isl_space_offset(dim, isl_dim_param); + if (off + dim->nparam > dim->n_id) + return 0; + for (i = 0; i < dim->nparam; ++i) + if (!dim->ids[off + i]) + return 0; + return 1; +} + +/* Align the initial parameters of dim1 to match the order in dim2. + */ +__isl_give isl_space *isl_space_align_params(__isl_take isl_space *dim1, + __isl_take isl_space *dim2) +{ + isl_reordering *exp; + + if (!isl_space_has_named_params(dim1) || !isl_space_has_named_params(dim2)) + isl_die(isl_space_get_ctx(dim1), isl_error_invalid, + "parameter alignment requires named parameters", + goto error); + + dim2 = isl_space_params(dim2); + exp = isl_parameter_alignment_reordering(dim1, dim2); + exp = isl_reordering_extend_space(exp, dim1); + isl_space_free(dim2); + if (!exp) + return NULL; + dim1 = isl_space_copy(exp->dim); + isl_reordering_free(exp); + return dim1; +error: + isl_space_free(dim1); + isl_space_free(dim2); + return NULL; +} + +/* Given the space of set (domain), construct a space for a map + * with as domain the given space and as range the range of "model". + */ +__isl_give isl_space *isl_space_extend_domain_with_range( + __isl_take isl_space *space, __isl_take isl_space *model) +{ + if (!model) + goto error; + + space = isl_space_from_domain(space); + space = isl_space_add_dims(space, isl_dim_out, + isl_space_dim(model, isl_dim_out)); + if (isl_space_has_tuple_id(model, isl_dim_out)) + space = isl_space_set_tuple_id(space, isl_dim_out, + isl_space_get_tuple_id(model, isl_dim_out)); + if (!space) + goto error; + if (model->nested[1]) { + isl_space *nested = isl_space_copy(model->nested[1]); + int n_nested, n_space; + nested = isl_space_align_params(nested, isl_space_copy(space)); + n_nested = isl_space_dim(nested, isl_dim_param); + n_space = isl_space_dim(space, isl_dim_param); + if (n_nested > n_space) + nested = isl_space_drop_dims(nested, isl_dim_param, + n_space, n_nested - n_space); + if (!nested) + goto error; + space->nested[1] = nested; + } + isl_space_free(model); + return space; +error: + isl_space_free(model); + isl_space_free(space); + return NULL; +} + +/* Compare the "type" dimensions of two isl_spaces. + * + * The order is fairly arbitrary. + */ +static int isl_space_cmp_type(__isl_keep isl_space *space1, + __isl_keep isl_space *space2, enum isl_dim_type type) +{ + int cmp; + isl_space *nested1, *nested2; + + if (isl_space_dim(space1, type) != isl_space_dim(space2, type)) + return isl_space_dim(space1, type) - + isl_space_dim(space2, type); + + cmp = isl_id_cmp(tuple_id(space1, type), tuple_id(space2, type)); + if (cmp != 0) + return cmp; + + nested1 = nested(space1, type); + nested2 = nested(space2, type); + if (!nested1 != !nested2) + return !nested1 - !nested2; + + if (nested1) + return isl_space_cmp(nested1, nested2); + + return 0; +} + +/* Compare two isl_spaces. + * + * The order is fairly arbitrary. + */ +int isl_space_cmp(__isl_keep isl_space *space1, __isl_keep isl_space *space2) +{ + int i; + int cmp; + + if (space1 == space2) + return 0; + if (!space1) + return -1; + if (!space2) + return 1; + + cmp = isl_space_cmp_type(space1, space2, isl_dim_param); + if (cmp != 0) + return cmp; + cmp = isl_space_cmp_type(space1, space2, isl_dim_in); + if (cmp != 0) + return cmp; + cmp = isl_space_cmp_type(space1, space2, isl_dim_out); + if (cmp != 0) + return cmp; + + if (!space1->ids && !space2->ids) + return 0; + + for (i = 0; i < n(space1, isl_dim_param); ++i) { + cmp = isl_id_cmp(get_id(space1, isl_dim_param, i), + get_id(space2, isl_dim_param, i)); + if (cmp != 0) + return cmp; + } + + return 0; +} Index: lib/Analysis/isl/isl_space_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_space_private.h @@ -0,0 +1,61 @@ +#ifndef ISL_SPACE_PRIVATE +#define ISL_SPACE_PRIVATE + +#include +#include +#include + +struct isl_name; +struct isl_space { + int ref; + + struct isl_ctx *ctx; + + unsigned nparam; + unsigned n_in; /* zero for sets */ + unsigned n_out; /* dim for sets */ + + isl_id *tuple_id[2]; + isl_space *nested[2]; + + unsigned n_id; + isl_id **ids; +}; + +__isl_give isl_space *isl_space_cow(__isl_take isl_space *dim); + +__isl_give isl_space *isl_space_underlying(__isl_take isl_space *dim, + unsigned n_div); + +uint32_t isl_space_get_hash(__isl_keep isl_space *dim); +uint32_t isl_space_get_domain_hash(__isl_keep isl_space *space); + +isl_bool isl_space_is_domain_internal(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); +isl_bool isl_space_is_range_internal(__isl_keep isl_space *space1, + __isl_keep isl_space *space2); + +__isl_give isl_space *isl_space_as_set_space(__isl_take isl_space *dim); + +unsigned isl_space_offset(__isl_keep isl_space *dim, enum isl_dim_type type); + +int isl_space_may_be_set(__isl_keep isl_space *dim); +int isl_space_is_named_or_nested(__isl_keep isl_space *dim, enum isl_dim_type type); +int isl_space_has_named_params(__isl_keep isl_space *dim); +__isl_give isl_space *isl_space_reset(__isl_take isl_space *dim, + enum isl_dim_type type); +__isl_give isl_space *isl_space_flatten(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_flatten_domain(__isl_take isl_space *dim); +__isl_give isl_space *isl_space_flatten_range(__isl_take isl_space *dim); + +__isl_give isl_space *isl_space_replace(__isl_take isl_space *dst, + enum isl_dim_type type, __isl_keep isl_space *src); + +__isl_give isl_space *isl_space_lift(__isl_take isl_space *dim, unsigned n_local); + +__isl_give isl_space *isl_space_extend_domain_with_range( + __isl_take isl_space *domain, __isl_take isl_space *model); + +int isl_space_cmp(__isl_keep isl_space *space1, __isl_keep isl_space *space2); + +#endif Index: lib/Analysis/isl/isl_stream.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_stream.c @@ -0,0 +1,1170 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include +#include +#include + +struct isl_keyword { + char *name; + enum isl_token_type type; +}; + +static int same_name(const void *entry, const void *val) +{ + const struct isl_keyword *keyword = (const struct isl_keyword *)entry; + + return !strcmp(keyword->name, val); +} + +enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s, + const char *name) +{ + struct isl_hash_table_entry *entry; + struct isl_keyword *keyword; + uint32_t name_hash; + + if (!s->keywords) { + s->keywords = isl_hash_table_alloc(s->ctx, 10); + if (!s->keywords) + return ISL_TOKEN_ERROR; + s->next_type = ISL_TOKEN_LAST; + } + + name_hash = isl_hash_string(isl_hash_init(), name); + + entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, + same_name, name, 1); + if (!entry) + return ISL_TOKEN_ERROR; + if (entry->data) { + keyword = entry->data; + return keyword->type; + } + + keyword = isl_calloc_type(s->ctx, struct isl_keyword); + if (!keyword) + return ISL_TOKEN_ERROR; + keyword->type = s->next_type++; + keyword->name = strdup(name); + if (!keyword->name) { + free(keyword); + return ISL_TOKEN_ERROR; + } + entry->data = keyword; + + return keyword->type; +} + +struct isl_token *isl_token_new(isl_ctx *ctx, + int line, int col, unsigned on_new_line) +{ + struct isl_token *tok = isl_alloc_type(ctx, struct isl_token); + if (!tok) + return NULL; + tok->line = line; + tok->col = col; + tok->on_new_line = on_new_line; + tok->is_keyword = 0; + tok->u.s = NULL; + return tok; +} + +/* Return the type of "tok". + */ +int isl_token_get_type(struct isl_token *tok) +{ + return tok ? tok->type : ISL_TOKEN_ERROR; +} + +/* Given a token of type ISL_TOKEN_VALUE, return the value it represents. + */ +__isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok) +{ + if (!tok) + return NULL; + if (tok->type != ISL_TOKEN_VALUE) + isl_die(ctx, isl_error_invalid, "not a value token", + return NULL); + + return isl_val_int_from_isl_int(ctx, tok->u.v); +} + +/* Given a token with a string representation, return a copy of this string. + */ +__isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok) +{ + if (!tok) + return NULL; + if (!tok->u.s) + isl_die(ctx, isl_error_invalid, + "token does not have a string representation", + return NULL); + + return strdup(tok->u.s); +} + +void isl_token_free(struct isl_token *tok) +{ + if (!tok) + return; + if (tok->type == ISL_TOKEN_VALUE) + isl_int_clear(tok->u.v); + else if (tok->type == ISL_TOKEN_MAP) + isl_map_free(tok->u.map); + else if (tok->type == ISL_TOKEN_AFF) + isl_pw_aff_free(tok->u.pwaff); + else + free(tok->u.s); + free(tok); +} + +void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok, + char *msg) +{ + int line = tok ? tok->line : s->line; + int col = tok ? tok->col : s->col; + fprintf(stderr, "syntax error (%d, %d): %s\n", line, col, msg); + if (tok) { + if (tok->type < 256) + fprintf(stderr, "got '%c'\n", tok->type); + else if (tok->type == ISL_TOKEN_IDENT) + fprintf(stderr, "got ident '%s'\n", tok->u.s); + else if (tok->is_keyword) + fprintf(stderr, "got keyword '%s'\n", tok->u.s); + else if (tok->type == ISL_TOKEN_VALUE) { + fprintf(stderr, "got value '"); + isl_int_print(stderr, tok->u.v, 0); + fprintf(stderr, "'\n"); + } else if (tok->type == ISL_TOKEN_MAP) { + isl_printer *p; + fprintf(stderr, "got map '"); + p = isl_printer_to_file(s->ctx, stderr); + p = isl_printer_print_map(p, tok->u.map); + isl_printer_free(p); + fprintf(stderr, "'\n"); + } else if (tok->type == ISL_TOKEN_AFF) { + isl_printer *p; + fprintf(stderr, "got affine expression '"); + p = isl_printer_to_file(s->ctx, stderr); + p = isl_printer_print_pw_aff(p, tok->u.pwaff); + isl_printer_free(p); + fprintf(stderr, "'\n"); + } else if (tok->u.s) + fprintf(stderr, "got token '%s'\n", tok->u.s); + else + fprintf(stderr, "got token type %d\n", tok->type); + } +} + +static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx) +{ + int i; + isl_stream *s = isl_calloc_type(ctx, struct isl_stream); + if (!s) + return NULL; + s->ctx = ctx; + isl_ctx_ref(s->ctx); + s->file = NULL; + s->str = NULL; + s->len = 0; + s->line = 1; + s->col = 1; + s->eof = 0; + s->last_line = 0; + s->c = -1; + s->n_un = 0; + for (i = 0; i < 5; ++i) + s->tokens[i] = NULL; + s->n_token = 0; + s->keywords = NULL; + s->size = 256; + s->buffer = isl_alloc_array(ctx, char, s->size); + if (!s->buffer) + goto error; + return s; +error: + isl_stream_free(s); + return NULL; +} + +__isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file) +{ + isl_stream *s = isl_stream_new(ctx); + if (!s) + return NULL; + s->file = file; + return s; +} + +__isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str) +{ + isl_stream *s; + if (!str) + return NULL; + s = isl_stream_new(ctx); + if (!s) + return NULL; + s->str = str; + return s; +} + +/* Read a character from the stream and advance s->line and s->col + * to point to the next character. + */ +static int stream_getc(__isl_keep isl_stream *s) +{ + int c; + if (s->eof) + return -1; + if (s->n_un) + return s->c = s->un[--s->n_un]; + if (s->file) + c = fgetc(s->file); + else { + c = *s->str++; + if (c == '\0') + c = -1; + } + if (c == -1) + s->eof = 1; + else if (c == '\n') { + s->line++; + s->col = 1; + } else + s->col++; + s->c = c; + return c; +} + +static void isl_stream_ungetc(__isl_keep isl_stream *s, int c) +{ + isl_assert(s->ctx, s->n_un < 5, return); + s->un[s->n_un++] = c; + s->c = -1; +} + +/* Read a character from the stream, skipping pairs of '\\' and '\n'. + * Set s->start_line and s->start_col to the line and column + * of the returned character. + */ +static int isl_stream_getc(__isl_keep isl_stream *s) +{ + int c; + + do { + s->start_line = s->line; + s->start_col = s->col; + c = stream_getc(s); + if (c != '\\') + return c; + c = stream_getc(s); + } while (c == '\n'); + + isl_stream_ungetc(s, c); + + return '\\'; +} + +static int isl_stream_push_char(__isl_keep isl_stream *s, int c) +{ + if (s->len >= s->size) { + char *buffer; + s->size = (3*s->size)/2; + buffer = isl_realloc_array(s->ctx, s->buffer, char, s->size); + if (!buffer) + return -1; + s->buffer = buffer; + } + s->buffer[s->len++] = c; + return 0; +} + +void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok) +{ + isl_assert(s->ctx, s->n_token < 5, return); + s->tokens[s->n_token++] = tok; +} + +static enum isl_token_type check_keywords(__isl_keep isl_stream *s) +{ + struct isl_hash_table_entry *entry; + struct isl_keyword *keyword; + uint32_t name_hash; + + if (!strcasecmp(s->buffer, "exists")) + return ISL_TOKEN_EXISTS; + if (!strcasecmp(s->buffer, "and")) + return ISL_TOKEN_AND; + if (!strcasecmp(s->buffer, "or")) + return ISL_TOKEN_OR; + if (!strcasecmp(s->buffer, "implies")) + return ISL_TOKEN_IMPLIES; + if (!strcasecmp(s->buffer, "not")) + return ISL_TOKEN_NOT; + if (!strcasecmp(s->buffer, "infty")) + return ISL_TOKEN_INFTY; + if (!strcasecmp(s->buffer, "infinity")) + return ISL_TOKEN_INFTY; + if (!strcasecmp(s->buffer, "NaN")) + return ISL_TOKEN_NAN; + if (!strcasecmp(s->buffer, "min")) + return ISL_TOKEN_MIN; + if (!strcasecmp(s->buffer, "max")) + return ISL_TOKEN_MAX; + if (!strcasecmp(s->buffer, "rat")) + return ISL_TOKEN_RAT; + if (!strcasecmp(s->buffer, "true")) + return ISL_TOKEN_TRUE; + if (!strcasecmp(s->buffer, "false")) + return ISL_TOKEN_FALSE; + if (!strcasecmp(s->buffer, "ceild")) + return ISL_TOKEN_CEILD; + if (!strcasecmp(s->buffer, "floord")) + return ISL_TOKEN_FLOORD; + if (!strcasecmp(s->buffer, "mod")) + return ISL_TOKEN_MOD; + if (!strcasecmp(s->buffer, "ceil")) + return ISL_TOKEN_CEIL; + if (!strcasecmp(s->buffer, "floor")) + return ISL_TOKEN_FLOOR; + + if (!s->keywords) + return ISL_TOKEN_IDENT; + + name_hash = isl_hash_string(isl_hash_init(), s->buffer); + entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, same_name, + s->buffer, 0); + if (entry) { + keyword = entry->data; + return keyword->type; + } + + return ISL_TOKEN_IDENT; +} + +int isl_stream_skip_line(__isl_keep isl_stream *s) +{ + int c; + + while ((c = isl_stream_getc(s)) != -1 && c != '\n') + /* nothing */ + ; + + return c == -1 ? -1 : 0; +} + +static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line) +{ + int c; + struct isl_token *tok = NULL; + int line, col; + int old_line = s->last_line; + + if (s->n_token) { + if (same_line && s->tokens[s->n_token - 1]->on_new_line) + return NULL; + return s->tokens[--s->n_token]; + } + + if (same_line && s->c == '\n') + return NULL; + + s->len = 0; + + /* skip spaces and comment lines */ + while ((c = isl_stream_getc(s)) != -1) { + if (c == '#') { + if (isl_stream_skip_line(s) < 0) + break; + c = '\n'; + if (same_line) + break; + } else if (!isspace(c) || (same_line && c == '\n')) + break; + } + + line = s->start_line; + col = s->start_col; + + if (c == -1 || (same_line && c == '\n')) + return NULL; + s->last_line = line; + + if (c == '(' || + c == ')' || + c == '+' || + c == '*' || + c == '%' || + c == '?' || + c == '^' || + c == '@' || + c == '$' || + c == ',' || + c == '.' || + c == ';' || + c == '[' || + c == ']' || + c == '{' || + c == '}') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = (enum isl_token_type)c; + return tok; + } + if (c == '-') { + int c; + if ((c = isl_stream_getc(s)) == '>') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->u.s = strdup("->"); + tok->type = ISL_TOKEN_TO; + return tok; + } + if (c != -1) + isl_stream_ungetc(s, c); + if (!isdigit(c)) { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = (enum isl_token_type) '-'; + return tok; + } + } + if (c == '-' || isdigit(c)) { + int minus = c == '-'; + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = ISL_TOKEN_VALUE; + isl_int_init(tok->u.v); + if (isl_stream_push_char(s, c)) + goto error; + while ((c = isl_stream_getc(s)) != -1 && isdigit(c)) + if (isl_stream_push_char(s, c)) + goto error; + if (c != -1) + isl_stream_ungetc(s, c); + isl_stream_push_char(s, '\0'); + isl_int_read(tok->u.v, s->buffer); + if (minus && isl_int_is_zero(tok->u.v)) { + tok->col++; + tok->on_new_line = 0; + isl_stream_push_token(s, tok); + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = (enum isl_token_type) '-'; + } + return tok; + } + if (isalpha(c) || c == '_') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + isl_stream_push_char(s, c); + while ((c = isl_stream_getc(s)) != -1 && + (isalnum(c) || c == '_')) + isl_stream_push_char(s, c); + if (c != -1) + isl_stream_ungetc(s, c); + while ((c = isl_stream_getc(s)) != -1 && c == '\'') + isl_stream_push_char(s, c); + if (c != -1) + isl_stream_ungetc(s, c); + isl_stream_push_char(s, '\0'); + tok->type = check_keywords(s); + if (tok->type != ISL_TOKEN_IDENT) + tok->is_keyword = 1; + tok->u.s = strdup(s->buffer); + if (!tok->u.s) + goto error; + return tok; + } + if (c == '"') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = ISL_TOKEN_STRING; + tok->u.s = NULL; + while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n') + isl_stream_push_char(s, c); + if (c != '"') { + isl_stream_error(s, NULL, "unterminated string"); + goto error; + } + isl_stream_push_char(s, '\0'); + tok->u.s = strdup(s->buffer); + return tok; + } + if (c == '=') { + int c; + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup("=="); + tok->type = ISL_TOKEN_EQ_EQ; + return tok; + } + if (c != -1) + isl_stream_ungetc(s, c); + tok->type = (enum isl_token_type) '='; + return tok; + } + if (c == ':') { + int c; + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup(":="); + tok->type = ISL_TOKEN_DEF; + return tok; + } + if (c != -1) + isl_stream_ungetc(s, c); + tok->type = (enum isl_token_type) ':'; + return tok; + } + if (c == '>') { + int c; + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup(">="); + tok->type = ISL_TOKEN_GE; + return tok; + } else if (c == '>') { + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup(">>="); + tok->type = ISL_TOKEN_LEX_GE; + return tok; + } + tok->u.s = strdup(">>"); + tok->type = ISL_TOKEN_LEX_GT; + } else { + tok->u.s = strdup(">"); + tok->type = ISL_TOKEN_GT; + } + if (c != -1) + isl_stream_ungetc(s, c); + return tok; + } + if (c == '<') { + int c; + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup("<="); + tok->type = ISL_TOKEN_LE; + return tok; + } else if (c == '<') { + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup("<<="); + tok->type = ISL_TOKEN_LEX_LE; + return tok; + } + tok->u.s = strdup("<<"); + tok->type = ISL_TOKEN_LEX_LT; + } else { + tok->u.s = strdup("<"); + tok->type = ISL_TOKEN_LT; + } + if (c != -1) + isl_stream_ungetc(s, c); + return tok; + } + if (c == '&') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = ISL_TOKEN_AND; + if ((c = isl_stream_getc(s)) != '&' && c != -1) { + tok->u.s = strdup("&"); + isl_stream_ungetc(s, c); + } else + tok->u.s = strdup("&&"); + return tok; + } + if (c == '|') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = ISL_TOKEN_OR; + if ((c = isl_stream_getc(s)) != '|' && c != -1) { + tok->u.s = strdup("|"); + isl_stream_ungetc(s, c); + } else + tok->u.s = strdup("||"); + return tok; + } + if (c == '/') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) != '\\' && c != -1) { + tok->type = (enum isl_token_type) '/'; + isl_stream_ungetc(s, c); + } else { + tok->u.s = strdup("/\\"); + tok->type = ISL_TOKEN_AND; + } + return tok; + } + if (c == '\\') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) != '/' && c != -1) { + tok->type = (enum isl_token_type) '\\'; + isl_stream_ungetc(s, c); + } else { + tok->u.s = strdup("\\/"); + tok->type = ISL_TOKEN_OR; + } + return tok; + } + if (c == '!') { + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + if ((c = isl_stream_getc(s)) == '=') { + tok->u.s = strdup("!="); + tok->type = ISL_TOKEN_NE; + return tok; + } else { + tok->type = ISL_TOKEN_NOT; + tok->u.s = strdup("!"); + } + if (c != -1) + isl_stream_ungetc(s, c); + return tok; + } + + tok = isl_token_new(s->ctx, line, col, old_line != line); + if (!tok) + return NULL; + tok->type = ISL_TOKEN_UNKNOWN; + return tok; +error: + isl_token_free(tok); + return NULL; +} + +struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s) +{ + return next_token(s, 0); +} + +struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s) +{ + return next_token(s, 1); +} + +int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + if (tok->type == type) { + isl_token_free(tok); + return 1; + } + isl_stream_push_token(s, tok); + return 0; +} + +int isl_stream_next_token_is(__isl_keep isl_stream *s, int type) +{ + struct isl_token *tok; + int r; + + tok = isl_stream_next_token(s); + if (!tok) + return 0; + r = tok->type == type; + isl_stream_push_token(s, tok); + return r; +} + +char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return NULL; + if (tok->type == ISL_TOKEN_IDENT) { + char *ident = strdup(tok->u.s); + isl_token_free(tok); + return ident; + } + isl_stream_push_token(s, tok); + return NULL; +} + +int isl_stream_eat(__isl_keep isl_stream *s, int type) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + if (!tok) + return -1; + if (tok->type == type) { + isl_token_free(tok); + return 0; + } + isl_stream_error(s, tok, "expecting other token"); + isl_stream_push_token(s, tok); + return -1; +} + +int isl_stream_is_empty(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + + tok = isl_stream_next_token(s); + + if (!tok) + return 1; + + isl_stream_push_token(s, tok); + return 0; +} + +static isl_stat free_keyword(void **p, void *user) +{ + struct isl_keyword *keyword = *p; + + free(keyword->name); + free(keyword); + + return isl_stat_ok; +} + +void isl_stream_flush_tokens(__isl_keep isl_stream *s) +{ + int i; + + if (!s) + return; + for (i = 0; i < s->n_token; ++i) + isl_token_free(s->tokens[i]); + s->n_token = 0; +} + +isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s) +{ + return s ? s->ctx : NULL; +} + +void isl_stream_free(__isl_take isl_stream *s) +{ + if (!s) + return; + free(s->buffer); + if (s->n_token != 0) { + struct isl_token *tok = isl_stream_next_token(s); + isl_stream_error(s, tok, "unexpected token"); + isl_token_free(tok); + } + if (s->keywords) { + isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL); + isl_hash_table_free(s->ctx, s->keywords); + } + free(s->yaml_state); + free(s->yaml_indent); + isl_ctx_deref(s->ctx); + free(s); +} + +/* Push "state" onto the stack of currently active YAML elements. + * The caller is responsible for setting the corresponding indentation. + * Return 0 on success and -1 on failure. + */ +static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state) +{ + if (s->yaml_size < s->yaml_depth + 1) { + int *indent; + enum isl_yaml_state *state; + + state = isl_realloc_array(s->ctx, s->yaml_state, + enum isl_yaml_state, s->yaml_depth + 1); + if (!state) + return -1; + s->yaml_state = state; + + indent = isl_realloc_array(s->ctx, s->yaml_indent, + int, s->yaml_depth + 1); + if (!indent) + return -1; + s->yaml_indent = indent; + + s->yaml_size = s->yaml_depth + 1; + } + + s->yaml_state[s->yaml_depth] = state; + s->yaml_depth++; + + return 0; +} + +/* Remove the innermost active YAML element from the stack. + * Return 0 on success and -1 on failure. + */ +static int pop_state(__isl_keep isl_stream *s) +{ + if (!s) + return -1; + if (s->yaml_depth < 1) + isl_die(isl_stream_get_ctx(s), isl_error_invalid, + "not in YAML construct", return -1); + + s->yaml_depth--; + + return 0; +} + +/* Set the state of the innermost active YAML element to "state". + * Return 0 on success and -1 on failure. + */ +static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state) +{ + if (!s) + return -1; + if (s->yaml_depth < 1) + isl_die(isl_stream_get_ctx(s), isl_error_invalid, + "not in YAML construct", return -1); + + s->yaml_state[s->yaml_depth - 1] = state; + + return 0; +} + +/* Return the state of the innermost active YAML element. + * Return isl_yaml_none if we are not inside any YAML element. + */ +static enum isl_yaml_state current_state(__isl_keep isl_stream *s) +{ + if (!s) + return isl_yaml_none; + if (s->yaml_depth < 1) + return isl_yaml_none; + return s->yaml_state[s->yaml_depth - 1]; +} + +/* Set the indentation of the innermost active YAML element to "indent". + * If "indent" is equal to ISL_YAML_INDENT_FLOW, then this means + * that the current elemient is in flow format. + */ +static int set_yaml_indent(__isl_keep isl_stream *s, int indent) +{ + if (s->yaml_depth < 1) + isl_die(s->ctx, isl_error_internal, + "not in YAML element", return -1); + + s->yaml_indent[s->yaml_depth - 1] = indent; + + return 0; +} + +/* Return the indentation of the innermost active YAML element + * of -1 on error. + */ +static int get_yaml_indent(__isl_keep isl_stream *s) +{ + if (s->yaml_depth < 1) + isl_die(s->ctx, isl_error_internal, + "not in YAML element", return -1); + + return s->yaml_indent[s->yaml_depth - 1]; +} + +/* Move to the next state at the innermost level. + * Return 1 if successful. + * Return 0 if we are at the end of the innermost level. + * Return -1 on error. + * + * If we are in state isl_yaml_mapping_key_start, then we have just + * started a mapping and we are expecting a key. If the mapping started + * with a '{', then we check if the next token is a '}'. If so, + * then the mapping is empty and there is no next state at this level. + * Otherwise, we assume that there is at least one key (the one from + * which we derived the indentation in isl_stream_yaml_read_start_mapping. + * + * If we are in state isl_yaml_mapping_key, then the we expect a colon + * followed by a value, so there is always a next state unless + * some error occurs. + * + * If we are in state isl_yaml_mapping_val, then there may or may + * not be a subsequent key in the same mapping. + * In flow format, the next key is preceded by a comma. + * In block format, the next key has the same indentation as the first key. + * If the first token has a smaller indentation, then we have reached + * the end of the current mapping. + * + * If we are in state isl_yaml_sequence_start, then we have just + * started a sequence. If the sequence started with a '[', + * then we check if the next token is a ']'. If so, then the sequence + * is empty and there is no next state at this level. + * Otherwise, we assume that there is at least one element in the sequence + * (the one from which we derived the indentation in + * isl_stream_yaml_read_start_sequence. + * + * If we are in state isl_yaml_sequence, then there may or may + * not be a subsequent element in the same sequence. + * In flow format, the next element is preceded by a comma. + * In block format, the next element is introduced by a dash with + * the same indentation as that of the first element. + * If the first token is not a dash or if it has a smaller indentation, + * then we have reached the end of the current sequence. + */ +int isl_stream_yaml_next(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + enum isl_yaml_state state; + int indent; + + state = current_state(s); + if (state == isl_yaml_none) + isl_die(s->ctx, isl_error_invalid, + "not in YAML element", return -1); + switch (state) { + case isl_yaml_mapping_key_start: + if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW && + isl_stream_next_token_is(s, '}')) + return 0; + if (update_state(s, isl_yaml_mapping_key) < 0) + return -1; + return 1; + case isl_yaml_mapping_key: + tok = isl_stream_next_token(s); + if (!tok) { + if (s->eof) + isl_stream_error(s, NULL, "unexpected EOF"); + return -1; + } + if (tok->type == ':') { + isl_token_free(tok); + if (update_state(s, isl_yaml_mapping_val) < 0) + return -1; + return 1; + } + isl_stream_error(s, tok, "expecting ':'"); + isl_stream_push_token(s, tok); + return -1; + case isl_yaml_mapping_val: + if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { + if (!isl_stream_eat_if_available(s, ',')) + return 0; + if (update_state(s, isl_yaml_mapping_key) < 0) + return -1; + return 1; + } + tok = isl_stream_next_token(s); + if (!tok) + return 0; + indent = tok->col - 1; + isl_stream_push_token(s, tok); + if (indent < get_yaml_indent(s)) + return 0; + if (update_state(s, isl_yaml_mapping_key) < 0) + return -1; + return 1; + case isl_yaml_sequence_start: + if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { + if (isl_stream_next_token_is(s, ']')) + return 0; + if (update_state(s, isl_yaml_sequence) < 0) + return -1; + return 1; + } + tok = isl_stream_next_token(s); + if (!tok) { + if (s->eof) + isl_stream_error(s, NULL, "unexpected EOF"); + return -1; + } + if (tok->type == '-') { + isl_token_free(tok); + if (update_state(s, isl_yaml_sequence) < 0) + return -1; + return 1; + } + isl_stream_error(s, tok, "expecting '-'"); + isl_stream_push_token(s, tok); + return 0; + case isl_yaml_sequence: + if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) + return isl_stream_eat_if_available(s, ','); + tok = isl_stream_next_token(s); + if (!tok) + return 0; + indent = tok->col - 1; + if (indent < get_yaml_indent(s) || tok->type != '-') { + isl_stream_push_token(s, tok); + return 0; + } + isl_token_free(tok); + return 1; + default: + isl_die(s->ctx, isl_error_internal, + "unexpected state", return 0); + } +} + +/* Start reading a YAML mapping. + * Return 0 on success and -1 on error. + * + * If the first token on the stream is a '{' then we remove this token + * from the stream and keep track of the fact that the mapping + * is given in flow format. + * Otherwise, we assume the first token is the first key of the mapping and + * keep track of its indentation, but keep the token on the stream. + * In both cases, the next token we expect is the first key of the mapping. + */ +int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + int indent; + + if (push_state(s, isl_yaml_mapping_key_start) < 0) + return -1; + + tok = isl_stream_next_token(s); + if (!tok) { + if (s->eof) + isl_stream_error(s, NULL, "unexpected EOF"); + return -1; + } + if (isl_token_get_type(tok) == '{') { + isl_token_free(tok); + return set_yaml_indent(s, ISL_YAML_INDENT_FLOW); + } + indent = tok->col - 1; + isl_stream_push_token(s, tok); + + return set_yaml_indent(s, indent); +} + +/* Finish reading a YAML mapping. + * Return 0 on success and -1 on error. + * + * If the mapping started with a '{', then we expect a '}' to close + * the mapping. + * Otherwise, we double-check that the next token (if any) + * has a smaller indentation than that of the current mapping. + */ +int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + int indent; + + if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { + if (isl_stream_eat(s, '}') < 0) + return -1; + return pop_state(s); + } + + tok = isl_stream_next_token(s); + if (!tok) + return pop_state(s); + + indent = tok->col - 1; + isl_stream_push_token(s, tok); + + if (indent >= get_yaml_indent(s)) + isl_die(isl_stream_get_ctx(s), isl_error_invalid, + "mapping not finished", return -1); + + return pop_state(s); +} + +/* Start reading a YAML sequence. + * Return 0 on success and -1 on error. + * + * If the first token on the stream is a '[' then we remove this token + * from the stream and keep track of the fact that the sequence + * is given in flow format. + * Otherwise, we assume the first token is the dash that introduces + * the first element of the sequence and keep track of its indentation, + * but keep the token on the stream. + * In both cases, the next token we expect is the first element + * of the sequence. + */ +int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + int indent; + + if (push_state(s, isl_yaml_sequence_start) < 0) + return -1; + + tok = isl_stream_next_token(s); + if (!tok) { + if (s->eof) + isl_stream_error(s, NULL, "unexpected EOF"); + return -1; + } + if (isl_token_get_type(tok) == '[') { + isl_token_free(tok); + return set_yaml_indent(s, ISL_YAML_INDENT_FLOW); + } + indent = tok->col - 1; + isl_stream_push_token(s, tok); + + return set_yaml_indent(s, indent); +} + +/* Finish reading a YAML sequence. + * Return 0 on success and -1 on error. + * + * If the sequence started with a '[', then we expect a ']' to close + * the sequence. + * Otherwise, we double-check that the next token (if any) + * is not a dash or that it has a smaller indentation than + * that of the current sequence. + */ +int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s) +{ + struct isl_token *tok; + int indent; + int dash; + + if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { + if (isl_stream_eat(s, ']') < 0) + return -1; + return pop_state(s); + } + + tok = isl_stream_next_token(s); + if (!tok) + return pop_state(s); + + indent = tok->col - 1; + dash = tok->type == '-'; + isl_stream_push_token(s, tok); + + if (indent >= get_yaml_indent(s) && dash) + isl_die(isl_stream_get_ctx(s), isl_error_invalid, + "sequence not finished", return -1); + + return pop_state(s); +} Index: lib/Analysis/isl/isl_stream_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_stream_private.h @@ -0,0 +1,69 @@ +#include +#include +#include + +struct isl_token { + int type; + + unsigned int on_new_line : 1; + unsigned is_keyword : 1; + int line; + int col; + + union { + isl_int v; + char *s; + isl_map *map; + isl_pw_aff *pwaff; + } u; +}; + +struct isl_token *isl_token_new(isl_ctx *ctx, + int line, int col, unsigned on_new_line); + +/* An input stream that may be either a file or a string. + * + * line and col are the line and column number of the next character (1-based). + * start_line and start_col are set by isl_stream_getc to point + * to the position of the returned character. + * last_line is the line number of the previous token. + * + * yaml_state and yaml_indent keep track of the currently active YAML + * elements. yaml_size is the size of these arrays, while yaml_depth + * is the number of elements currently in use. + * yaml_state and yaml_indent may be NULL if no YAML parsing is being + * performed. + * yaml_state keeps track of what is expected next at each level. + * yaml_indent keeps track of the indentation at each level, with + * ISL_YAML_INDENT_FLOW meaning that the element is in flow format + * (such that the indentation is not relevant). + */ +struct isl_stream { + struct isl_ctx *ctx; + FILE *file; + const char *str; + int line; + int col; + int start_line; + int start_col; + int last_line; + int eof; + + char *buffer; + size_t size; + size_t len; + int c; + int un[5]; + int n_un; + + struct isl_token *tokens[5]; + int n_token; + + struct isl_hash_table *keywords; + enum isl_token_type next_type; + + int yaml_depth; + int yaml_size; + enum isl_yaml_state *yaml_state; + int *yaml_indent; +}; Index: lib/Analysis/isl/isl_tab.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_tab.h @@ -0,0 +1,320 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#ifndef ISL_TAB_H +#define ISL_TAB_H + +#include +#include +#include +#include +#include + +struct isl_tab_var { + int index; + unsigned is_row : 1; + unsigned is_nonneg : 1; + unsigned is_zero : 1; + unsigned is_redundant : 1; + unsigned marked : 1; + unsigned frozen : 1; + unsigned negated : 1; +}; + +enum isl_tab_undo_type { + isl_tab_undo_bottom, + isl_tab_undo_rational, + isl_tab_undo_empty, + isl_tab_undo_nonneg, + isl_tab_undo_redundant, + isl_tab_undo_freeze, + isl_tab_undo_zero, + isl_tab_undo_allocate, + isl_tab_undo_relax, + isl_tab_undo_unrestrict, + isl_tab_undo_bmap_ineq, + isl_tab_undo_bmap_eq, + isl_tab_undo_bmap_div, + isl_tab_undo_saved_basis, + isl_tab_undo_drop_sample, + isl_tab_undo_saved_samples, + isl_tab_undo_callback, +}; + +struct isl_tab_callback { + int (*run)(struct isl_tab_callback *cb); +}; + +union isl_tab_undo_val { + int var_index; + int *col_var; + int n; + struct isl_tab_callback *callback; +}; + +struct isl_tab_undo { + enum isl_tab_undo_type type; + union isl_tab_undo_val u; + struct isl_tab_undo *next; +}; + +/* The tableau maintains equality relations. + * Each column and each row is associated to a variable or a constraint. + * The "value" of an inequality constraint is the value of the corresponding + * slack variable. + * The "row_var" and "col_var" arrays map column and row indices + * to indices in the "var" and "con" arrays. The elements of these + * arrays maintain extra information about the variables and the constraints. + * Each row expresses the corresponding row variable as an affine expression + * of the column variables. + * The first two columns in the matrix contain the common denominator of + * the row and the numerator of the constant term. + * If "M" is set, then the third column represents the "big parameter". + * The third (M = 0) or fourth (M = 1) column + * in the matrix is called column 0 with respect to the col_var array. + * The sample value of the tableau is the value that assigns zero + * to all the column variables and the constant term of each affine + * expression to the corresponding row variable. + * The operations on the tableau maintain the property that the sample + * value satisfies the non-negativity constraints (usually on the slack + * variables). + * + * The big parameter represents an arbitrarily big (and divisible) + * positive number. If present, then the sign of a row is determined + * lexicographically, with the sign of the big parameter coefficient + * considered first. The big parameter is only used while + * solving PILP problems. + * + * The first n_dead column variables have their values fixed to zero. + * The corresponding tab_vars are flagged "is_zero". + * Some of the rows that have have zero coefficients in all but + * the dead columns are also flagged "is_zero". + * + * The first n_redundant rows correspond to inequality constraints + * that are always satisfied for any value satisfying the non-redundant + * rows. The corresponding tab_vars are flagged "is_redundant". + * A row variable that is flagged "is_zero" is also flagged "is_redundant" + * since the constraint has been reduced to 0 = 0 and is therefore always + * satisfied. + * + * There are "n_var" variables in total. The first "n_param" of these + * are called parameters and the last "n_div" of these are called divs. + * The basic tableau operations makes no distinction between different + * kinds of variables. These special variables are only used while + * solving PILP problems. + * + * Dead columns and redundant rows are detected on the fly. + * However, the basic operations do not ensure that all dead columns + * or all redundant rows are detected. + * isl_tab_detect_implicit_equalities and isl_tab_detect_redundant can be used + * to perform an exhaustive search for dead columns and redundant rows. + * + * The samples matrix contains "n_sample" integer points that have at some + * point been elements satisfying the tableau. The first "n_outside" + * of them no longer satisfy the tableau. They are kept because they + * can be reinstated during rollback when the constraint that cut them + * out is removed. These samples are only maintained for the context + * tableau while solving PILP problems. + * + * If "preserve" is set, then we want to keep all constraints in the + * tableau, even if they turn out to be redundant. + */ +enum isl_tab_row_sign { + isl_tab_row_unknown = 0, + isl_tab_row_pos, + isl_tab_row_neg, + isl_tab_row_any, +}; +struct isl_tab { + struct isl_mat *mat; + + unsigned n_row; + unsigned n_col; + unsigned n_dead; + unsigned n_redundant; + + unsigned n_var; + unsigned n_param; + unsigned n_div; + unsigned max_var; + unsigned n_con; + unsigned n_eq; + unsigned max_con; + struct isl_tab_var *var; + struct isl_tab_var *con; + int *row_var; /* v >= 0 -> var v; v < 0 -> con ~v */ + int *col_var; /* v >= 0 -> var v; v < 0 -> con ~v */ + enum isl_tab_row_sign *row_sign; + + struct isl_tab_undo bottom; + struct isl_tab_undo *top; + + struct isl_vec *dual; + struct isl_basic_map *bmap; + + unsigned n_sample; + unsigned n_outside; + int *sample_index; + struct isl_mat *samples; + + int n_zero; + int n_unbounded; + struct isl_mat *basis; + + int (*conflict)(int con, void *user); + void *conflict_user; + + unsigned strict_redundant : 1; + unsigned need_undo : 1; + unsigned preserve : 1; + unsigned rational : 1; + unsigned empty : 1; + unsigned in_undo : 1; + unsigned M : 1; + unsigned cone : 1; +}; + +struct isl_tab *isl_tab_alloc(struct isl_ctx *ctx, + unsigned n_row, unsigned n_var, unsigned M); +void isl_tab_free(struct isl_tab *tab); + +isl_ctx *isl_tab_get_ctx(struct isl_tab *tab); + +__isl_give struct isl_tab *isl_tab_from_basic_map( + __isl_keep isl_basic_map *bmap, int track); +__isl_give struct isl_tab *isl_tab_from_basic_set( + __isl_keep isl_basic_set *bset, int track); +struct isl_tab *isl_tab_from_recession_cone(struct isl_basic_set *bset, + int parametric); +int isl_tab_cone_is_bounded(struct isl_tab *tab); +struct isl_basic_map *isl_basic_map_update_from_tab(struct isl_basic_map *bmap, + struct isl_tab *tab); +struct isl_basic_set *isl_basic_set_update_from_tab(struct isl_basic_set *bset, + struct isl_tab *tab); +int isl_tab_detect_implicit_equalities(struct isl_tab *tab) WARN_UNUSED; +__isl_give isl_basic_map *isl_tab_make_equalities_explicit(struct isl_tab *tab, + __isl_take isl_basic_map *bmap); +int isl_tab_detect_redundant(struct isl_tab *tab) WARN_UNUSED; +#define ISL_TAB_SAVE_DUAL (1 << 0) +enum isl_lp_result isl_tab_min(struct isl_tab *tab, + isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom, + unsigned flags) WARN_UNUSED; + +int isl_tab_add_ineq(struct isl_tab *tab, isl_int *ineq) WARN_UNUSED; +int isl_tab_add_eq(struct isl_tab *tab, isl_int *eq) WARN_UNUSED; +int isl_tab_add_valid_eq(struct isl_tab *tab, isl_int *eq) WARN_UNUSED; + +int isl_tab_freeze_constraint(struct isl_tab *tab, int con) WARN_UNUSED; + +int isl_tab_track_bmap(struct isl_tab *tab, __isl_take isl_basic_map *bmap) WARN_UNUSED; +int isl_tab_track_bset(struct isl_tab *tab, __isl_take isl_basic_set *bset) WARN_UNUSED; +__isl_keep isl_basic_set *isl_tab_peek_bset(struct isl_tab *tab); + +int isl_tab_is_equality(struct isl_tab *tab, int con); +int isl_tab_is_redundant(struct isl_tab *tab, int con); + +int isl_tab_sample_is_integer(struct isl_tab *tab); +struct isl_vec *isl_tab_get_sample_value(struct isl_tab *tab); + +enum isl_ineq_type { + isl_ineq_error = -1, + isl_ineq_redundant, + isl_ineq_separate, + isl_ineq_cut, + isl_ineq_adj_eq, + isl_ineq_adj_ineq, +}; + +enum isl_ineq_type isl_tab_ineq_type(struct isl_tab *tab, isl_int *ineq); + +struct isl_tab_undo *isl_tab_snap(struct isl_tab *tab); +int isl_tab_rollback(struct isl_tab *tab, struct isl_tab_undo *snap) WARN_UNUSED; + +int isl_tab_relax(struct isl_tab *tab, int con) WARN_UNUSED; +int isl_tab_select_facet(struct isl_tab *tab, int con) WARN_UNUSED; +int isl_tab_unrestrict(struct isl_tab *tab, int con) WARN_UNUSED; + +void isl_tab_dump(__isl_keep struct isl_tab *tab); + +struct isl_map *isl_tab_basic_map_partial_lexopt( + struct isl_basic_map *bmap, struct isl_basic_set *dom, + struct isl_set **empty, int max); +__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexopt_pw_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max); + +/* An isl_region represents a sequence of consecutive variables. + * pos is the location (starting at 0) of the first variable in the sequence. + */ +struct isl_region { + int pos; + int len; +}; + +__isl_give isl_vec *isl_tab_basic_set_non_trivial_lexmin( + __isl_take isl_basic_set *bset, int n_op, int n_region, + struct isl_region *region, + int (*conflict)(int con, void *user), void *user); +__isl_give isl_vec *isl_tab_basic_set_non_neg_lexmin( + __isl_take isl_basic_set *bset); + +struct isl_tab_lexmin; +typedef struct isl_tab_lexmin isl_tab_lexmin; + +__isl_give isl_tab_lexmin *isl_tab_lexmin_from_basic_set( + __isl_take isl_basic_set *bset); +int isl_tab_lexmin_dim(__isl_keep isl_tab_lexmin *tl); +__isl_give isl_tab_lexmin *isl_tab_lexmin_add_eq(__isl_take isl_tab_lexmin *tl, + isl_int *eq); +__isl_give isl_vec *isl_tab_lexmin_get_solution(__isl_keep isl_tab_lexmin *tl); +__isl_null isl_tab_lexmin *isl_tab_lexmin_free(__isl_take isl_tab_lexmin *tl); + +/* private */ + +struct isl_tab_var *isl_tab_var_from_row(struct isl_tab *tab, int i); +int isl_tab_mark_redundant(struct isl_tab *tab, int row) WARN_UNUSED; +int isl_tab_mark_rational(struct isl_tab *tab) WARN_UNUSED; +int isl_tab_mark_empty(struct isl_tab *tab) WARN_UNUSED; +struct isl_tab *isl_tab_dup(struct isl_tab *tab); +struct isl_tab *isl_tab_product(struct isl_tab *tab1, struct isl_tab *tab2); +int isl_tab_extend_cons(struct isl_tab *tab, unsigned n_new) WARN_UNUSED; +int isl_tab_allocate_con(struct isl_tab *tab) WARN_UNUSED; +int isl_tab_extend_vars(struct isl_tab *tab, unsigned n_new) WARN_UNUSED; +int isl_tab_allocate_var(struct isl_tab *tab) WARN_UNUSED; +int isl_tab_insert_var(struct isl_tab *tab, int pos) WARN_UNUSED; +int isl_tab_pivot(struct isl_tab *tab, int row, int col) WARN_UNUSED; +int isl_tab_add_row(struct isl_tab *tab, isl_int *line) WARN_UNUSED; +int isl_tab_row_is_redundant(struct isl_tab *tab, int row); +int isl_tab_min_at_most_neg_one(struct isl_tab *tab, struct isl_tab_var *var); +int isl_tab_sign_of_max(struct isl_tab *tab, int con); +int isl_tab_kill_col(struct isl_tab *tab, int col) WARN_UNUSED; + +int isl_tab_push(struct isl_tab *tab, enum isl_tab_undo_type type) WARN_UNUSED; +int isl_tab_push_var(struct isl_tab *tab, + enum isl_tab_undo_type type, struct isl_tab_var *var) WARN_UNUSED; +int isl_tab_push_basis(struct isl_tab *tab) WARN_UNUSED; + +struct isl_tab *isl_tab_init_samples(struct isl_tab *tab) WARN_UNUSED; +int isl_tab_add_sample(struct isl_tab *tab, + __isl_take isl_vec *sample) WARN_UNUSED; +struct isl_tab *isl_tab_drop_sample(struct isl_tab *tab, int s); +int isl_tab_save_samples(struct isl_tab *tab) WARN_UNUSED; + +struct isl_tab *isl_tab_detect_equalities(struct isl_tab *tab, + struct isl_tab *tab_cone) WARN_UNUSED; + +int isl_tab_push_callback(struct isl_tab *tab, + struct isl_tab_callback *callback) WARN_UNUSED; + +int isl_tab_add_div(struct isl_tab *tab, __isl_keep isl_vec *div, + int (*add_ineq)(void *user, isl_int *), void *user); + +int isl_tab_shift_var(struct isl_tab *tab, int pos, isl_int shift) WARN_UNUSED; + +#endif Index: lib/Analysis/isl/isl_tab.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_tab.c @@ -0,0 +1,3721 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2013 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include "isl_map_private.h" +#include "isl_tab.h" +#include +#include + +/* + * The implementation of tableaus in this file was inspired by Section 8 + * of David Detlefs, Greg Nelson and James B. Saxe, "Simplify: a theorem + * prover for program checking". + */ + +struct isl_tab *isl_tab_alloc(struct isl_ctx *ctx, + unsigned n_row, unsigned n_var, unsigned M) +{ + int i; + struct isl_tab *tab; + unsigned off = 2 + M; + + tab = isl_calloc_type(ctx, struct isl_tab); + if (!tab) + return NULL; + tab->mat = isl_mat_alloc(ctx, n_row, off + n_var); + if (!tab->mat) + goto error; + tab->var = isl_alloc_array(ctx, struct isl_tab_var, n_var); + if (n_var && !tab->var) + goto error; + tab->con = isl_alloc_array(ctx, struct isl_tab_var, n_row); + if (n_row && !tab->con) + goto error; + tab->col_var = isl_alloc_array(ctx, int, n_var); + if (n_var && !tab->col_var) + goto error; + tab->row_var = isl_alloc_array(ctx, int, n_row); + if (n_row && !tab->row_var) + goto error; + for (i = 0; i < n_var; ++i) { + tab->var[i].index = i; + tab->var[i].is_row = 0; + tab->var[i].is_nonneg = 0; + tab->var[i].is_zero = 0; + tab->var[i].is_redundant = 0; + tab->var[i].frozen = 0; + tab->var[i].negated = 0; + tab->col_var[i] = i; + } + tab->n_row = 0; + tab->n_con = 0; + tab->n_eq = 0; + tab->max_con = n_row; + tab->n_col = n_var; + tab->n_var = n_var; + tab->max_var = n_var; + tab->n_param = 0; + tab->n_div = 0; + tab->n_dead = 0; + tab->n_redundant = 0; + tab->strict_redundant = 0; + tab->need_undo = 0; + tab->rational = 0; + tab->empty = 0; + tab->in_undo = 0; + tab->M = M; + tab->cone = 0; + tab->bottom.type = isl_tab_undo_bottom; + tab->bottom.next = NULL; + tab->top = &tab->bottom; + + tab->n_zero = 0; + tab->n_unbounded = 0; + tab->basis = NULL; + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +isl_ctx *isl_tab_get_ctx(struct isl_tab *tab) +{ + return tab ? isl_mat_get_ctx(tab->mat) : NULL; +} + +int isl_tab_extend_cons(struct isl_tab *tab, unsigned n_new) +{ + unsigned off; + + if (!tab) + return -1; + + off = 2 + tab->M; + + if (tab->max_con < tab->n_con + n_new) { + struct isl_tab_var *con; + + con = isl_realloc_array(tab->mat->ctx, tab->con, + struct isl_tab_var, tab->max_con + n_new); + if (!con) + return -1; + tab->con = con; + tab->max_con += n_new; + } + if (tab->mat->n_row < tab->n_row + n_new) { + int *row_var; + + tab->mat = isl_mat_extend(tab->mat, + tab->n_row + n_new, off + tab->n_col); + if (!tab->mat) + return -1; + row_var = isl_realloc_array(tab->mat->ctx, tab->row_var, + int, tab->mat->n_row); + if (!row_var) + return -1; + tab->row_var = row_var; + if (tab->row_sign) { + enum isl_tab_row_sign *s; + s = isl_realloc_array(tab->mat->ctx, tab->row_sign, + enum isl_tab_row_sign, tab->mat->n_row); + if (!s) + return -1; + tab->row_sign = s; + } + } + return 0; +} + +/* Make room for at least n_new extra variables. + * Return -1 if anything went wrong. + */ +int isl_tab_extend_vars(struct isl_tab *tab, unsigned n_new) +{ + struct isl_tab_var *var; + unsigned off = 2 + tab->M; + + if (tab->max_var < tab->n_var + n_new) { + var = isl_realloc_array(tab->mat->ctx, tab->var, + struct isl_tab_var, tab->n_var + n_new); + if (!var) + return -1; + tab->var = var; + tab->max_var = tab->n_var + n_new; + } + + if (tab->mat->n_col < off + tab->n_col + n_new) { + int *p; + + tab->mat = isl_mat_extend(tab->mat, + tab->mat->n_row, off + tab->n_col + n_new); + if (!tab->mat) + return -1; + p = isl_realloc_array(tab->mat->ctx, tab->col_var, + int, tab->n_col + n_new); + if (!p) + return -1; + tab->col_var = p; + } + + return 0; +} + +static void free_undo_record(struct isl_tab_undo *undo) +{ + switch (undo->type) { + case isl_tab_undo_saved_basis: + free(undo->u.col_var); + break; + default:; + } + free(undo); +} + +static void free_undo(struct isl_tab *tab) +{ + struct isl_tab_undo *undo, *next; + + for (undo = tab->top; undo && undo != &tab->bottom; undo = next) { + next = undo->next; + free_undo_record(undo); + } + tab->top = undo; +} + +void isl_tab_free(struct isl_tab *tab) +{ + if (!tab) + return; + free_undo(tab); + isl_mat_free(tab->mat); + isl_vec_free(tab->dual); + isl_basic_map_free(tab->bmap); + free(tab->var); + free(tab->con); + free(tab->row_var); + free(tab->col_var); + free(tab->row_sign); + isl_mat_free(tab->samples); + free(tab->sample_index); + isl_mat_free(tab->basis); + free(tab); +} + +struct isl_tab *isl_tab_dup(struct isl_tab *tab) +{ + int i; + struct isl_tab *dup; + unsigned off; + + if (!tab) + return NULL; + + off = 2 + tab->M; + dup = isl_calloc_type(tab->mat->ctx, struct isl_tab); + if (!dup) + return NULL; + dup->mat = isl_mat_dup(tab->mat); + if (!dup->mat) + goto error; + dup->var = isl_alloc_array(tab->mat->ctx, struct isl_tab_var, tab->max_var); + if (tab->max_var && !dup->var) + goto error; + for (i = 0; i < tab->n_var; ++i) + dup->var[i] = tab->var[i]; + dup->con = isl_alloc_array(tab->mat->ctx, struct isl_tab_var, tab->max_con); + if (tab->max_con && !dup->con) + goto error; + for (i = 0; i < tab->n_con; ++i) + dup->con[i] = tab->con[i]; + dup->col_var = isl_alloc_array(tab->mat->ctx, int, tab->mat->n_col - off); + if ((tab->mat->n_col - off) && !dup->col_var) + goto error; + for (i = 0; i < tab->n_col; ++i) + dup->col_var[i] = tab->col_var[i]; + dup->row_var = isl_alloc_array(tab->mat->ctx, int, tab->mat->n_row); + if (tab->mat->n_row && !dup->row_var) + goto error; + for (i = 0; i < tab->n_row; ++i) + dup->row_var[i] = tab->row_var[i]; + if (tab->row_sign) { + dup->row_sign = isl_alloc_array(tab->mat->ctx, enum isl_tab_row_sign, + tab->mat->n_row); + if (tab->mat->n_row && !dup->row_sign) + goto error; + for (i = 0; i < tab->n_row; ++i) + dup->row_sign[i] = tab->row_sign[i]; + } + if (tab->samples) { + dup->samples = isl_mat_dup(tab->samples); + if (!dup->samples) + goto error; + dup->sample_index = isl_alloc_array(tab->mat->ctx, int, + tab->samples->n_row); + if (tab->samples->n_row && !dup->sample_index) + goto error; + dup->n_sample = tab->n_sample; + dup->n_outside = tab->n_outside; + } + dup->n_row = tab->n_row; + dup->n_con = tab->n_con; + dup->n_eq = tab->n_eq; + dup->max_con = tab->max_con; + dup->n_col = tab->n_col; + dup->n_var = tab->n_var; + dup->max_var = tab->max_var; + dup->n_param = tab->n_param; + dup->n_div = tab->n_div; + dup->n_dead = tab->n_dead; + dup->n_redundant = tab->n_redundant; + dup->rational = tab->rational; + dup->empty = tab->empty; + dup->strict_redundant = 0; + dup->need_undo = 0; + dup->in_undo = 0; + dup->M = tab->M; + tab->cone = tab->cone; + dup->bottom.type = isl_tab_undo_bottom; + dup->bottom.next = NULL; + dup->top = &dup->bottom; + + dup->n_zero = tab->n_zero; + dup->n_unbounded = tab->n_unbounded; + dup->basis = isl_mat_dup(tab->basis); + + return dup; +error: + isl_tab_free(dup); + return NULL; +} + +/* Construct the coefficient matrix of the product tableau + * of two tableaus. + * mat{1,2} is the coefficient matrix of tableau {1,2} + * row{1,2} is the number of rows in tableau {1,2} + * col{1,2} is the number of columns in tableau {1,2} + * off is the offset to the coefficient column (skipping the + * denominator, the constant term and the big parameter if any) + * r{1,2} is the number of redundant rows in tableau {1,2} + * d{1,2} is the number of dead columns in tableau {1,2} + * + * The order of the rows and columns in the result is as explained + * in isl_tab_product. + */ +static struct isl_mat *tab_mat_product(struct isl_mat *mat1, + struct isl_mat *mat2, unsigned row1, unsigned row2, + unsigned col1, unsigned col2, + unsigned off, unsigned r1, unsigned r2, unsigned d1, unsigned d2) +{ + int i; + struct isl_mat *prod; + unsigned n; + + prod = isl_mat_alloc(mat1->ctx, mat1->n_row + mat2->n_row, + off + col1 + col2); + if (!prod) + return NULL; + + n = 0; + for (i = 0; i < r1; ++i) { + isl_seq_cpy(prod->row[n + i], mat1->row[i], off + d1); + isl_seq_clr(prod->row[n + i] + off + d1, d2); + isl_seq_cpy(prod->row[n + i] + off + d1 + d2, + mat1->row[i] + off + d1, col1 - d1); + isl_seq_clr(prod->row[n + i] + off + col1 + d1, col2 - d2); + } + + n += r1; + for (i = 0; i < r2; ++i) { + isl_seq_cpy(prod->row[n + i], mat2->row[i], off); + isl_seq_clr(prod->row[n + i] + off, d1); + isl_seq_cpy(prod->row[n + i] + off + d1, + mat2->row[i] + off, d2); + isl_seq_clr(prod->row[n + i] + off + d1 + d2, col1 - d1); + isl_seq_cpy(prod->row[n + i] + off + col1 + d1, + mat2->row[i] + off + d2, col2 - d2); + } + + n += r2; + for (i = 0; i < row1 - r1; ++i) { + isl_seq_cpy(prod->row[n + i], mat1->row[r1 + i], off + d1); + isl_seq_clr(prod->row[n + i] + off + d1, d2); + isl_seq_cpy(prod->row[n + i] + off + d1 + d2, + mat1->row[r1 + i] + off + d1, col1 - d1); + isl_seq_clr(prod->row[n + i] + off + col1 + d1, col2 - d2); + } + + n += row1 - r1; + for (i = 0; i < row2 - r2; ++i) { + isl_seq_cpy(prod->row[n + i], mat2->row[r2 + i], off); + isl_seq_clr(prod->row[n + i] + off, d1); + isl_seq_cpy(prod->row[n + i] + off + d1, + mat2->row[r2 + i] + off, d2); + isl_seq_clr(prod->row[n + i] + off + d1 + d2, col1 - d1); + isl_seq_cpy(prod->row[n + i] + off + col1 + d1, + mat2->row[r2 + i] + off + d2, col2 - d2); + } + + return prod; +} + +/* Update the row or column index of a variable that corresponds + * to a variable in the first input tableau. + */ +static void update_index1(struct isl_tab_var *var, + unsigned r1, unsigned r2, unsigned d1, unsigned d2) +{ + if (var->index == -1) + return; + if (var->is_row && var->index >= r1) + var->index += r2; + if (!var->is_row && var->index >= d1) + var->index += d2; +} + +/* Update the row or column index of a variable that corresponds + * to a variable in the second input tableau. + */ +static void update_index2(struct isl_tab_var *var, + unsigned row1, unsigned col1, + unsigned r1, unsigned r2, unsigned d1, unsigned d2) +{ + if (var->index == -1) + return; + if (var->is_row) { + if (var->index < r2) + var->index += r1; + else + var->index += row1; + } else { + if (var->index < d2) + var->index += d1; + else + var->index += col1; + } +} + +/* Create a tableau that represents the Cartesian product of the sets + * represented by tableaus tab1 and tab2. + * The order of the rows in the product is + * - redundant rows of tab1 + * - redundant rows of tab2 + * - non-redundant rows of tab1 + * - non-redundant rows of tab2 + * The order of the columns is + * - denominator + * - constant term + * - coefficient of big parameter, if any + * - dead columns of tab1 + * - dead columns of tab2 + * - live columns of tab1 + * - live columns of tab2 + * The order of the variables and the constraints is a concatenation + * of order in the two input tableaus. + */ +struct isl_tab *isl_tab_product(struct isl_tab *tab1, struct isl_tab *tab2) +{ + int i; + struct isl_tab *prod; + unsigned off; + unsigned r1, r2, d1, d2; + + if (!tab1 || !tab2) + return NULL; + + isl_assert(tab1->mat->ctx, tab1->M == tab2->M, return NULL); + isl_assert(tab1->mat->ctx, tab1->rational == tab2->rational, return NULL); + isl_assert(tab1->mat->ctx, tab1->cone == tab2->cone, return NULL); + isl_assert(tab1->mat->ctx, !tab1->row_sign, return NULL); + isl_assert(tab1->mat->ctx, !tab2->row_sign, return NULL); + isl_assert(tab1->mat->ctx, tab1->n_param == 0, return NULL); + isl_assert(tab1->mat->ctx, tab2->n_param == 0, return NULL); + isl_assert(tab1->mat->ctx, tab1->n_div == 0, return NULL); + isl_assert(tab1->mat->ctx, tab2->n_div == 0, return NULL); + + off = 2 + tab1->M; + r1 = tab1->n_redundant; + r2 = tab2->n_redundant; + d1 = tab1->n_dead; + d2 = tab2->n_dead; + prod = isl_calloc_type(tab1->mat->ctx, struct isl_tab); + if (!prod) + return NULL; + prod->mat = tab_mat_product(tab1->mat, tab2->mat, + tab1->n_row, tab2->n_row, + tab1->n_col, tab2->n_col, off, r1, r2, d1, d2); + if (!prod->mat) + goto error; + prod->var = isl_alloc_array(tab1->mat->ctx, struct isl_tab_var, + tab1->max_var + tab2->max_var); + if ((tab1->max_var + tab2->max_var) && !prod->var) + goto error; + for (i = 0; i < tab1->n_var; ++i) { + prod->var[i] = tab1->var[i]; + update_index1(&prod->var[i], r1, r2, d1, d2); + } + for (i = 0; i < tab2->n_var; ++i) { + prod->var[tab1->n_var + i] = tab2->var[i]; + update_index2(&prod->var[tab1->n_var + i], + tab1->n_row, tab1->n_col, + r1, r2, d1, d2); + } + prod->con = isl_alloc_array(tab1->mat->ctx, struct isl_tab_var, + tab1->max_con + tab2->max_con); + if ((tab1->max_con + tab2->max_con) && !prod->con) + goto error; + for (i = 0; i < tab1->n_con; ++i) { + prod->con[i] = tab1->con[i]; + update_index1(&prod->con[i], r1, r2, d1, d2); + } + for (i = 0; i < tab2->n_con; ++i) { + prod->con[tab1->n_con + i] = tab2->con[i]; + update_index2(&prod->con[tab1->n_con + i], + tab1->n_row, tab1->n_col, + r1, r2, d1, d2); + } + prod->col_var = isl_alloc_array(tab1->mat->ctx, int, + tab1->n_col + tab2->n_col); + if ((tab1->n_col + tab2->n_col) && !prod->col_var) + goto error; + for (i = 0; i < tab1->n_col; ++i) { + int pos = i < d1 ? i : i + d2; + prod->col_var[pos] = tab1->col_var[i]; + } + for (i = 0; i < tab2->n_col; ++i) { + int pos = i < d2 ? d1 + i : tab1->n_col + i; + int t = tab2->col_var[i]; + if (t >= 0) + t += tab1->n_var; + else + t -= tab1->n_con; + prod->col_var[pos] = t; + } + prod->row_var = isl_alloc_array(tab1->mat->ctx, int, + tab1->mat->n_row + tab2->mat->n_row); + if ((tab1->mat->n_row + tab2->mat->n_row) && !prod->row_var) + goto error; + for (i = 0; i < tab1->n_row; ++i) { + int pos = i < r1 ? i : i + r2; + prod->row_var[pos] = tab1->row_var[i]; + } + for (i = 0; i < tab2->n_row; ++i) { + int pos = i < r2 ? r1 + i : tab1->n_row + i; + int t = tab2->row_var[i]; + if (t >= 0) + t += tab1->n_var; + else + t -= tab1->n_con; + prod->row_var[pos] = t; + } + prod->samples = NULL; + prod->sample_index = NULL; + prod->n_row = tab1->n_row + tab2->n_row; + prod->n_con = tab1->n_con + tab2->n_con; + prod->n_eq = 0; + prod->max_con = tab1->max_con + tab2->max_con; + prod->n_col = tab1->n_col + tab2->n_col; + prod->n_var = tab1->n_var + tab2->n_var; + prod->max_var = tab1->max_var + tab2->max_var; + prod->n_param = 0; + prod->n_div = 0; + prod->n_dead = tab1->n_dead + tab2->n_dead; + prod->n_redundant = tab1->n_redundant + tab2->n_redundant; + prod->rational = tab1->rational; + prod->empty = tab1->empty || tab2->empty; + prod->strict_redundant = tab1->strict_redundant || tab2->strict_redundant; + prod->need_undo = 0; + prod->in_undo = 0; + prod->M = tab1->M; + prod->cone = tab1->cone; + prod->bottom.type = isl_tab_undo_bottom; + prod->bottom.next = NULL; + prod->top = &prod->bottom; + + prod->n_zero = 0; + prod->n_unbounded = 0; + prod->basis = NULL; + + return prod; +error: + isl_tab_free(prod); + return NULL; +} + +static struct isl_tab_var *var_from_index(struct isl_tab *tab, int i) +{ + if (i >= 0) + return &tab->var[i]; + else + return &tab->con[~i]; +} + +struct isl_tab_var *isl_tab_var_from_row(struct isl_tab *tab, int i) +{ + return var_from_index(tab, tab->row_var[i]); +} + +static struct isl_tab_var *var_from_col(struct isl_tab *tab, int i) +{ + return var_from_index(tab, tab->col_var[i]); +} + +/* Check if there are any upper bounds on column variable "var", + * i.e., non-negative rows where var appears with a negative coefficient. + * Return 1 if there are no such bounds. + */ +static int max_is_manifestly_unbounded(struct isl_tab *tab, + struct isl_tab_var *var) +{ + int i; + unsigned off = 2 + tab->M; + + if (var->is_row) + return 0; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + if (!isl_int_is_neg(tab->mat->row[i][off + var->index])) + continue; + if (isl_tab_var_from_row(tab, i)->is_nonneg) + return 0; + } + return 1; +} + +/* Check if there are any lower bounds on column variable "var", + * i.e., non-negative rows where var appears with a positive coefficient. + * Return 1 if there are no such bounds. + */ +static int min_is_manifestly_unbounded(struct isl_tab *tab, + struct isl_tab_var *var) +{ + int i; + unsigned off = 2 + tab->M; + + if (var->is_row) + return 0; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + if (!isl_int_is_pos(tab->mat->row[i][off + var->index])) + continue; + if (isl_tab_var_from_row(tab, i)->is_nonneg) + return 0; + } + return 1; +} + +static int row_cmp(struct isl_tab *tab, int r1, int r2, int c, isl_int *t) +{ + unsigned off = 2 + tab->M; + + if (tab->M) { + int s; + isl_int_mul(*t, tab->mat->row[r1][2], tab->mat->row[r2][off+c]); + isl_int_submul(*t, tab->mat->row[r2][2], tab->mat->row[r1][off+c]); + s = isl_int_sgn(*t); + if (s) + return s; + } + isl_int_mul(*t, tab->mat->row[r1][1], tab->mat->row[r2][off + c]); + isl_int_submul(*t, tab->mat->row[r2][1], tab->mat->row[r1][off + c]); + return isl_int_sgn(*t); +} + +/* Given the index of a column "c", return the index of a row + * that can be used to pivot the column in, with either an increase + * (sgn > 0) or a decrease (sgn < 0) of the corresponding variable. + * If "var" is not NULL, then the row returned will be different from + * the one associated with "var". + * + * Each row in the tableau is of the form + * + * x_r = a_r0 + \sum_i a_ri x_i + * + * Only rows with x_r >= 0 and with the sign of a_ri opposite to "sgn" + * impose any limit on the increase or decrease in the value of x_c + * and this bound is equal to a_r0 / |a_rc|. We are therefore looking + * for the row with the smallest (most stringent) such bound. + * Note that the common denominator of each row drops out of the fraction. + * To check if row j has a smaller bound than row r, i.e., + * a_j0 / |a_jc| < a_r0 / |a_rc| or a_j0 |a_rc| < a_r0 |a_jc|, + * we check if -sign(a_jc) (a_j0 a_rc - a_r0 a_jc) < 0, + * where -sign(a_jc) is equal to "sgn". + */ +static int pivot_row(struct isl_tab *tab, + struct isl_tab_var *var, int sgn, int c) +{ + int j, r, tsgn; + isl_int t; + unsigned off = 2 + tab->M; + + isl_int_init(t); + r = -1; + for (j = tab->n_redundant; j < tab->n_row; ++j) { + if (var && j == var->index) + continue; + if (!isl_tab_var_from_row(tab, j)->is_nonneg) + continue; + if (sgn * isl_int_sgn(tab->mat->row[j][off + c]) >= 0) + continue; + if (r < 0) { + r = j; + continue; + } + tsgn = sgn * row_cmp(tab, r, j, c, &t); + if (tsgn < 0 || (tsgn == 0 && + tab->row_var[j] < tab->row_var[r])) + r = j; + } + isl_int_clear(t); + return r; +} + +/* Find a pivot (row and col) that will increase (sgn > 0) or decrease + * (sgn < 0) the value of row variable var. + * If not NULL, then skip_var is a row variable that should be ignored + * while looking for a pivot row. It is usually equal to var. + * + * As the given row in the tableau is of the form + * + * x_r = a_r0 + \sum_i a_ri x_i + * + * we need to find a column such that the sign of a_ri is equal to "sgn" + * (such that an increase in x_i will have the desired effect) or a + * column with a variable that may attain negative values. + * If a_ri is positive, then we need to move x_i in the same direction + * to obtain the desired effect. Otherwise, x_i has to move in the + * opposite direction. + */ +static void find_pivot(struct isl_tab *tab, + struct isl_tab_var *var, struct isl_tab_var *skip_var, + int sgn, int *row, int *col) +{ + int j, r, c; + isl_int *tr; + + *row = *col = -1; + + isl_assert(tab->mat->ctx, var->is_row, return); + tr = tab->mat->row[var->index] + 2 + tab->M; + + c = -1; + for (j = tab->n_dead; j < tab->n_col; ++j) { + if (isl_int_is_zero(tr[j])) + continue; + if (isl_int_sgn(tr[j]) != sgn && + var_from_col(tab, j)->is_nonneg) + continue; + if (c < 0 || tab->col_var[j] < tab->col_var[c]) + c = j; + } + if (c < 0) + return; + + sgn *= isl_int_sgn(tr[c]); + r = pivot_row(tab, skip_var, sgn, c); + *row = r < 0 ? var->index : r; + *col = c; +} + +/* Return 1 if row "row" represents an obviously redundant inequality. + * This means + * - it represents an inequality or a variable + * - that is the sum of a non-negative sample value and a positive + * combination of zero or more non-negative constraints. + */ +int isl_tab_row_is_redundant(struct isl_tab *tab, int row) +{ + int i; + unsigned off = 2 + tab->M; + + if (tab->row_var[row] < 0 && !isl_tab_var_from_row(tab, row)->is_nonneg) + return 0; + + if (isl_int_is_neg(tab->mat->row[row][1])) + return 0; + if (tab->strict_redundant && isl_int_is_zero(tab->mat->row[row][1])) + return 0; + if (tab->M && isl_int_is_neg(tab->mat->row[row][2])) + return 0; + + for (i = tab->n_dead; i < tab->n_col; ++i) { + if (isl_int_is_zero(tab->mat->row[row][off + i])) + continue; + if (tab->col_var[i] >= 0) + return 0; + if (isl_int_is_neg(tab->mat->row[row][off + i])) + return 0; + if (!var_from_col(tab, i)->is_nonneg) + return 0; + } + return 1; +} + +static void swap_rows(struct isl_tab *tab, int row1, int row2) +{ + int t; + enum isl_tab_row_sign s; + + t = tab->row_var[row1]; + tab->row_var[row1] = tab->row_var[row2]; + tab->row_var[row2] = t; + isl_tab_var_from_row(tab, row1)->index = row1; + isl_tab_var_from_row(tab, row2)->index = row2; + tab->mat = isl_mat_swap_rows(tab->mat, row1, row2); + + if (!tab->row_sign) + return; + s = tab->row_sign[row1]; + tab->row_sign[row1] = tab->row_sign[row2]; + tab->row_sign[row2] = s; +} + +static int push_union(struct isl_tab *tab, + enum isl_tab_undo_type type, union isl_tab_undo_val u) WARN_UNUSED; +static int push_union(struct isl_tab *tab, + enum isl_tab_undo_type type, union isl_tab_undo_val u) +{ + struct isl_tab_undo *undo; + + if (!tab) + return -1; + if (!tab->need_undo) + return 0; + + undo = isl_alloc_type(tab->mat->ctx, struct isl_tab_undo); + if (!undo) + return -1; + undo->type = type; + undo->u = u; + undo->next = tab->top; + tab->top = undo; + + return 0; +} + +int isl_tab_push_var(struct isl_tab *tab, + enum isl_tab_undo_type type, struct isl_tab_var *var) +{ + union isl_tab_undo_val u; + if (var->is_row) + u.var_index = tab->row_var[var->index]; + else + u.var_index = tab->col_var[var->index]; + return push_union(tab, type, u); +} + +int isl_tab_push(struct isl_tab *tab, enum isl_tab_undo_type type) +{ + union isl_tab_undo_val u = { 0 }; + return push_union(tab, type, u); +} + +/* Push a record on the undo stack describing the current basic + * variables, so that the this state can be restored during rollback. + */ +int isl_tab_push_basis(struct isl_tab *tab) +{ + int i; + union isl_tab_undo_val u; + + u.col_var = isl_alloc_array(tab->mat->ctx, int, tab->n_col); + if (tab->n_col && !u.col_var) + return -1; + for (i = 0; i < tab->n_col; ++i) + u.col_var[i] = tab->col_var[i]; + return push_union(tab, isl_tab_undo_saved_basis, u); +} + +int isl_tab_push_callback(struct isl_tab *tab, struct isl_tab_callback *callback) +{ + union isl_tab_undo_val u; + u.callback = callback; + return push_union(tab, isl_tab_undo_callback, u); +} + +struct isl_tab *isl_tab_init_samples(struct isl_tab *tab) +{ + if (!tab) + return NULL; + + tab->n_sample = 0; + tab->n_outside = 0; + tab->samples = isl_mat_alloc(tab->mat->ctx, 1, 1 + tab->n_var); + if (!tab->samples) + goto error; + tab->sample_index = isl_alloc_array(tab->mat->ctx, int, 1); + if (!tab->sample_index) + goto error; + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +int isl_tab_add_sample(struct isl_tab *tab, __isl_take isl_vec *sample) +{ + if (!tab || !sample) + goto error; + + if (tab->n_sample + 1 > tab->samples->n_row) { + int *t = isl_realloc_array(tab->mat->ctx, + tab->sample_index, int, tab->n_sample + 1); + if (!t) + goto error; + tab->sample_index = t; + } + + tab->samples = isl_mat_extend(tab->samples, + tab->n_sample + 1, tab->samples->n_col); + if (!tab->samples) + goto error; + + isl_seq_cpy(tab->samples->row[tab->n_sample], sample->el, sample->size); + isl_vec_free(sample); + tab->sample_index[tab->n_sample] = tab->n_sample; + tab->n_sample++; + + return 0; +error: + isl_vec_free(sample); + return -1; +} + +struct isl_tab *isl_tab_drop_sample(struct isl_tab *tab, int s) +{ + if (s != tab->n_outside) { + int t = tab->sample_index[tab->n_outside]; + tab->sample_index[tab->n_outside] = tab->sample_index[s]; + tab->sample_index[s] = t; + isl_mat_swap_rows(tab->samples, tab->n_outside, s); + } + tab->n_outside++; + if (isl_tab_push(tab, isl_tab_undo_drop_sample) < 0) { + isl_tab_free(tab); + return NULL; + } + + return tab; +} + +/* Record the current number of samples so that we can remove newer + * samples during a rollback. + */ +int isl_tab_save_samples(struct isl_tab *tab) +{ + union isl_tab_undo_val u; + + if (!tab) + return -1; + + u.n = tab->n_sample; + return push_union(tab, isl_tab_undo_saved_samples, u); +} + +/* Mark row with index "row" as being redundant. + * If we may need to undo the operation or if the row represents + * a variable of the original problem, the row is kept, + * but no longer considered when looking for a pivot row. + * Otherwise, the row is simply removed. + * + * The row may be interchanged with some other row. If it + * is interchanged with a later row, return 1. Otherwise return 0. + * If the rows are checked in order in the calling function, + * then a return value of 1 means that the row with the given + * row number may now contain a different row that hasn't been checked yet. + */ +int isl_tab_mark_redundant(struct isl_tab *tab, int row) +{ + struct isl_tab_var *var = isl_tab_var_from_row(tab, row); + var->is_redundant = 1; + isl_assert(tab->mat->ctx, row >= tab->n_redundant, return -1); + if (tab->preserve || tab->need_undo || tab->row_var[row] >= 0) { + if (tab->row_var[row] >= 0 && !var->is_nonneg) { + var->is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, var) < 0) + return -1; + } + if (row != tab->n_redundant) + swap_rows(tab, row, tab->n_redundant); + tab->n_redundant++; + return isl_tab_push_var(tab, isl_tab_undo_redundant, var); + } else { + if (row != tab->n_row - 1) + swap_rows(tab, row, tab->n_row - 1); + isl_tab_var_from_row(tab, tab->n_row - 1)->index = -1; + tab->n_row--; + return 1; + } +} + +/* Mark "tab" as a rational tableau. + * If it wasn't marked as a rational tableau already and if we may + * need to undo changes, then arrange for the marking to be undone + * during the undo. + */ +int isl_tab_mark_rational(struct isl_tab *tab) +{ + if (!tab) + return -1; + if (!tab->rational && tab->need_undo) + if (isl_tab_push(tab, isl_tab_undo_rational) < 0) + return -1; + tab->rational = 1; + return 0; +} + +int isl_tab_mark_empty(struct isl_tab *tab) +{ + if (!tab) + return -1; + if (!tab->empty && tab->need_undo) + if (isl_tab_push(tab, isl_tab_undo_empty) < 0) + return -1; + tab->empty = 1; + return 0; +} + +int isl_tab_freeze_constraint(struct isl_tab *tab, int con) +{ + struct isl_tab_var *var; + + if (!tab) + return -1; + + var = &tab->con[con]; + if (var->frozen) + return 0; + if (var->index < 0) + return 0; + var->frozen = 1; + + if (tab->need_undo) + return isl_tab_push_var(tab, isl_tab_undo_freeze, var); + + return 0; +} + +/* Update the rows signs after a pivot of "row" and "col", with "row_sgn" + * the original sign of the pivot element. + * We only keep track of row signs during PILP solving and in this case + * we only pivot a row with negative sign (meaning the value is always + * non-positive) using a positive pivot element. + * + * For each row j, the new value of the parametric constant is equal to + * + * a_j0 - a_jc a_r0/a_rc + * + * where a_j0 is the original parametric constant, a_rc is the pivot element, + * a_r0 is the parametric constant of the pivot row and a_jc is the + * pivot column entry of the row j. + * Since a_r0 is non-positive and a_rc is positive, the sign of row j + * remains the same if a_jc has the same sign as the row j or if + * a_jc is zero. In all other cases, we reset the sign to "unknown". + */ +static void update_row_sign(struct isl_tab *tab, int row, int col, int row_sgn) +{ + int i; + struct isl_mat *mat = tab->mat; + unsigned off = 2 + tab->M; + + if (!tab->row_sign) + return; + + if (tab->row_sign[row] == 0) + return; + isl_assert(mat->ctx, row_sgn > 0, return); + isl_assert(mat->ctx, tab->row_sign[row] == isl_tab_row_neg, return); + tab->row_sign[row] = isl_tab_row_pos; + for (i = 0; i < tab->n_row; ++i) { + int s; + if (i == row) + continue; + s = isl_int_sgn(mat->row[i][off + col]); + if (!s) + continue; + if (!tab->row_sign[i]) + continue; + if (s < 0 && tab->row_sign[i] == isl_tab_row_neg) + continue; + if (s > 0 && tab->row_sign[i] == isl_tab_row_pos) + continue; + tab->row_sign[i] = isl_tab_row_unknown; + } +} + +/* Given a row number "row" and a column number "col", pivot the tableau + * such that the associated variables are interchanged. + * The given row in the tableau expresses + * + * x_r = a_r0 + \sum_i a_ri x_i + * + * or + * + * x_c = 1/a_rc x_r - a_r0/a_rc + sum_{i \ne r} -a_ri/a_rc + * + * Substituting this equality into the other rows + * + * x_j = a_j0 + \sum_i a_ji x_i + * + * with a_jc \ne 0, we obtain + * + * x_j = a_jc/a_rc x_r + a_j0 - a_jc a_r0/a_rc + sum a_ji - a_jc a_ri/a_rc + * + * The tableau + * + * n_rc/d_r n_ri/d_r + * n_jc/d_j n_ji/d_j + * + * where i is any other column and j is any other row, + * is therefore transformed into + * + * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc| + * s(n_rc)d_r n_jc/(|n_rc| d_j) (n_ji |n_rc| - s(n_rc)n_jc n_ri)/(|n_rc| d_j) + * + * The transformation is performed along the following steps + * + * d_r/n_rc n_ri/n_rc + * n_jc/d_j n_ji/d_j + * + * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc| + * n_jc/d_j n_ji/d_j + * + * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc| + * n_jc/(|n_rc| d_j) n_ji/(|n_rc| d_j) + * + * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc| + * n_jc/(|n_rc| d_j) (n_ji |n_rc|)/(|n_rc| d_j) + * + * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc| + * n_jc/(|n_rc| d_j) (n_ji |n_rc| - s(n_rc)n_jc n_ri)/(|n_rc| d_j) + * + * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc| + * s(n_rc)d_r n_jc/(|n_rc| d_j) (n_ji |n_rc| - s(n_rc)n_jc n_ri)/(|n_rc| d_j) + * + */ +int isl_tab_pivot(struct isl_tab *tab, int row, int col) +{ + int i, j; + int sgn; + int t; + isl_ctx *ctx; + struct isl_mat *mat = tab->mat; + struct isl_tab_var *var; + unsigned off = 2 + tab->M; + + ctx = isl_tab_get_ctx(tab); + if (isl_ctx_next_operation(ctx) < 0) + return -1; + + isl_int_swap(mat->row[row][0], mat->row[row][off + col]); + sgn = isl_int_sgn(mat->row[row][0]); + if (sgn < 0) { + isl_int_neg(mat->row[row][0], mat->row[row][0]); + isl_int_neg(mat->row[row][off + col], mat->row[row][off + col]); + } else + for (j = 0; j < off - 1 + tab->n_col; ++j) { + if (j == off - 1 + col) + continue; + isl_int_neg(mat->row[row][1 + j], mat->row[row][1 + j]); + } + if (!isl_int_is_one(mat->row[row][0])) + isl_seq_normalize(mat->ctx, mat->row[row], off + tab->n_col); + for (i = 0; i < tab->n_row; ++i) { + if (i == row) + continue; + if (isl_int_is_zero(mat->row[i][off + col])) + continue; + isl_int_mul(mat->row[i][0], mat->row[i][0], mat->row[row][0]); + for (j = 0; j < off - 1 + tab->n_col; ++j) { + if (j == off - 1 + col) + continue; + isl_int_mul(mat->row[i][1 + j], + mat->row[i][1 + j], mat->row[row][0]); + isl_int_addmul(mat->row[i][1 + j], + mat->row[i][off + col], mat->row[row][1 + j]); + } + isl_int_mul(mat->row[i][off + col], + mat->row[i][off + col], mat->row[row][off + col]); + if (!isl_int_is_one(mat->row[i][0])) + isl_seq_normalize(mat->ctx, mat->row[i], off + tab->n_col); + } + t = tab->row_var[row]; + tab->row_var[row] = tab->col_var[col]; + tab->col_var[col] = t; + var = isl_tab_var_from_row(tab, row); + var->is_row = 1; + var->index = row; + var = var_from_col(tab, col); + var->is_row = 0; + var->index = col; + update_row_sign(tab, row, col, sgn); + if (tab->in_undo) + return 0; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + if (isl_int_is_zero(mat->row[i][off + col])) + continue; + if (!isl_tab_var_from_row(tab, i)->frozen && + isl_tab_row_is_redundant(tab, i)) { + int redo = isl_tab_mark_redundant(tab, i); + if (redo < 0) + return -1; + if (redo) + --i; + } + } + return 0; +} + +/* If "var" represents a column variable, then pivot is up (sgn > 0) + * or down (sgn < 0) to a row. The variable is assumed not to be + * unbounded in the specified direction. + * If sgn = 0, then the variable is unbounded in both directions, + * and we pivot with any row we can find. + */ +static int to_row(struct isl_tab *tab, struct isl_tab_var *var, int sign) WARN_UNUSED; +static int to_row(struct isl_tab *tab, struct isl_tab_var *var, int sign) +{ + int r; + unsigned off = 2 + tab->M; + + if (var->is_row) + return 0; + + if (sign == 0) { + for (r = tab->n_redundant; r < tab->n_row; ++r) + if (!isl_int_is_zero(tab->mat->row[r][off+var->index])) + break; + isl_assert(tab->mat->ctx, r < tab->n_row, return -1); + } else { + r = pivot_row(tab, NULL, sign, var->index); + isl_assert(tab->mat->ctx, r >= 0, return -1); + } + + return isl_tab_pivot(tab, r, var->index); +} + +/* Check whether all variables that are marked as non-negative + * also have a non-negative sample value. This function is not + * called from the current code but is useful during debugging. + */ +static void check_table(struct isl_tab *tab) __attribute__ ((unused)); +static void check_table(struct isl_tab *tab) +{ + int i; + + if (tab->empty) + return; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + struct isl_tab_var *var; + var = isl_tab_var_from_row(tab, i); + if (!var->is_nonneg) + continue; + if (tab->M) { + isl_assert(tab->mat->ctx, + !isl_int_is_neg(tab->mat->row[i][2]), abort()); + if (isl_int_is_pos(tab->mat->row[i][2])) + continue; + } + isl_assert(tab->mat->ctx, !isl_int_is_neg(tab->mat->row[i][1]), + abort()); + } +} + +/* Return the sign of the maximal value of "var". + * If the sign is not negative, then on return from this function, + * the sample value will also be non-negative. + * + * If "var" is manifestly unbounded wrt positive values, we are done. + * Otherwise, we pivot the variable up to a row if needed + * Then we continue pivoting down until either + * - no more down pivots can be performed + * - the sample value is positive + * - the variable is pivoted into a manifestly unbounded column + */ +static int sign_of_max(struct isl_tab *tab, struct isl_tab_var *var) +{ + int row, col; + + if (max_is_manifestly_unbounded(tab, var)) + return 1; + if (to_row(tab, var, 1) < 0) + return -2; + while (!isl_int_is_pos(tab->mat->row[var->index][1])) { + find_pivot(tab, var, var, 1, &row, &col); + if (row == -1) + return isl_int_sgn(tab->mat->row[var->index][1]); + if (isl_tab_pivot(tab, row, col) < 0) + return -2; + if (!var->is_row) /* manifestly unbounded */ + return 1; + } + return 1; +} + +int isl_tab_sign_of_max(struct isl_tab *tab, int con) +{ + struct isl_tab_var *var; + + if (!tab) + return -2; + + var = &tab->con[con]; + isl_assert(tab->mat->ctx, !var->is_redundant, return -2); + isl_assert(tab->mat->ctx, !var->is_zero, return -2); + + return sign_of_max(tab, var); +} + +static int row_is_neg(struct isl_tab *tab, int row) +{ + if (!tab->M) + return isl_int_is_neg(tab->mat->row[row][1]); + if (isl_int_is_pos(tab->mat->row[row][2])) + return 0; + if (isl_int_is_neg(tab->mat->row[row][2])) + return 1; + return isl_int_is_neg(tab->mat->row[row][1]); +} + +static int row_sgn(struct isl_tab *tab, int row) +{ + if (!tab->M) + return isl_int_sgn(tab->mat->row[row][1]); + if (!isl_int_is_zero(tab->mat->row[row][2])) + return isl_int_sgn(tab->mat->row[row][2]); + else + return isl_int_sgn(tab->mat->row[row][1]); +} + +/* Perform pivots until the row variable "var" has a non-negative + * sample value or until no more upward pivots can be performed. + * Return the sign of the sample value after the pivots have been + * performed. + */ +static int restore_row(struct isl_tab *tab, struct isl_tab_var *var) +{ + int row, col; + + while (row_is_neg(tab, var->index)) { + find_pivot(tab, var, var, 1, &row, &col); + if (row == -1) + break; + if (isl_tab_pivot(tab, row, col) < 0) + return -2; + if (!var->is_row) /* manifestly unbounded */ + return 1; + } + return row_sgn(tab, var->index); +} + +/* Perform pivots until we are sure that the row variable "var" + * can attain non-negative values. After return from this + * function, "var" is still a row variable, but its sample + * value may not be non-negative, even if the function returns 1. + */ +static int at_least_zero(struct isl_tab *tab, struct isl_tab_var *var) +{ + int row, col; + + while (isl_int_is_neg(tab->mat->row[var->index][1])) { + find_pivot(tab, var, var, 1, &row, &col); + if (row == -1) + break; + if (row == var->index) /* manifestly unbounded */ + return 1; + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + } + return !isl_int_is_neg(tab->mat->row[var->index][1]); +} + +/* Return a negative value if "var" can attain negative values. + * Return a non-negative value otherwise. + * + * If "var" is manifestly unbounded wrt negative values, we are done. + * Otherwise, if var is in a column, we can pivot it down to a row. + * Then we continue pivoting down until either + * - the pivot would result in a manifestly unbounded column + * => we don't perform the pivot, but simply return -1 + * - no more down pivots can be performed + * - the sample value is negative + * If the sample value becomes negative and the variable is supposed + * to be nonnegative, then we undo the last pivot. + * However, if the last pivot has made the pivoting variable + * obviously redundant, then it may have moved to another row. + * In that case we look for upward pivots until we reach a non-negative + * value again. + */ +static int sign_of_min(struct isl_tab *tab, struct isl_tab_var *var) +{ + int row, col; + struct isl_tab_var *pivot_var = NULL; + + if (min_is_manifestly_unbounded(tab, var)) + return -1; + if (!var->is_row) { + col = var->index; + row = pivot_row(tab, NULL, -1, col); + pivot_var = var_from_col(tab, col); + if (isl_tab_pivot(tab, row, col) < 0) + return -2; + if (var->is_redundant) + return 0; + if (isl_int_is_neg(tab->mat->row[var->index][1])) { + if (var->is_nonneg) { + if (!pivot_var->is_redundant && + pivot_var->index == row) { + if (isl_tab_pivot(tab, row, col) < 0) + return -2; + } else + if (restore_row(tab, var) < -1) + return -2; + } + return -1; + } + } + if (var->is_redundant) + return 0; + while (!isl_int_is_neg(tab->mat->row[var->index][1])) { + find_pivot(tab, var, var, -1, &row, &col); + if (row == var->index) + return -1; + if (row == -1) + return isl_int_sgn(tab->mat->row[var->index][1]); + pivot_var = var_from_col(tab, col); + if (isl_tab_pivot(tab, row, col) < 0) + return -2; + if (var->is_redundant) + return 0; + } + if (pivot_var && var->is_nonneg) { + /* pivot back to non-negative value */ + if (!pivot_var->is_redundant && pivot_var->index == row) { + if (isl_tab_pivot(tab, row, col) < 0) + return -2; + } else + if (restore_row(tab, var) < -1) + return -2; + } + return -1; +} + +static int row_at_most_neg_one(struct isl_tab *tab, int row) +{ + if (tab->M) { + if (isl_int_is_pos(tab->mat->row[row][2])) + return 0; + if (isl_int_is_neg(tab->mat->row[row][2])) + return 1; + } + return isl_int_is_neg(tab->mat->row[row][1]) && + isl_int_abs_ge(tab->mat->row[row][1], + tab->mat->row[row][0]); +} + +/* Return 1 if "var" can attain values <= -1. + * Return 0 otherwise. + * + * If the variable "var" is supposed to be non-negative (is_nonneg is set), + * then the sample value of "var" is assumed to be non-negative when the + * the function is called. If 1 is returned then the constraint + * is not redundant and the sample value is made non-negative again before + * the function returns. + */ +int isl_tab_min_at_most_neg_one(struct isl_tab *tab, struct isl_tab_var *var) +{ + int row, col; + struct isl_tab_var *pivot_var; + + if (min_is_manifestly_unbounded(tab, var)) + return 1; + if (!var->is_row) { + col = var->index; + row = pivot_row(tab, NULL, -1, col); + pivot_var = var_from_col(tab, col); + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + if (var->is_redundant) + return 0; + if (row_at_most_neg_one(tab, var->index)) { + if (var->is_nonneg) { + if (!pivot_var->is_redundant && + pivot_var->index == row) { + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + } else + if (restore_row(tab, var) < -1) + return -1; + } + return 1; + } + } + if (var->is_redundant) + return 0; + do { + find_pivot(tab, var, var, -1, &row, &col); + if (row == var->index) { + if (var->is_nonneg && restore_row(tab, var) < -1) + return -1; + return 1; + } + if (row == -1) + return 0; + pivot_var = var_from_col(tab, col); + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + if (var->is_redundant) + return 0; + } while (!row_at_most_neg_one(tab, var->index)); + if (var->is_nonneg) { + /* pivot back to non-negative value */ + if (!pivot_var->is_redundant && pivot_var->index == row) + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + if (restore_row(tab, var) < -1) + return -1; + } + return 1; +} + +/* Return 1 if "var" can attain values >= 1. + * Return 0 otherwise. + */ +static int at_least_one(struct isl_tab *tab, struct isl_tab_var *var) +{ + int row, col; + isl_int *r; + + if (max_is_manifestly_unbounded(tab, var)) + return 1; + if (to_row(tab, var, 1) < 0) + return -1; + r = tab->mat->row[var->index]; + while (isl_int_lt(r[1], r[0])) { + find_pivot(tab, var, var, 1, &row, &col); + if (row == -1) + return isl_int_ge(r[1], r[0]); + if (row == var->index) /* manifestly unbounded */ + return 1; + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + } + return 1; +} + +static void swap_cols(struct isl_tab *tab, int col1, int col2) +{ + int t; + unsigned off = 2 + tab->M; + t = tab->col_var[col1]; + tab->col_var[col1] = tab->col_var[col2]; + tab->col_var[col2] = t; + var_from_col(tab, col1)->index = col1; + var_from_col(tab, col2)->index = col2; + tab->mat = isl_mat_swap_cols(tab->mat, off + col1, off + col2); +} + +/* Mark column with index "col" as representing a zero variable. + * If we may need to undo the operation the column is kept, + * but no longer considered. + * Otherwise, the column is simply removed. + * + * The column may be interchanged with some other column. If it + * is interchanged with a later column, return 1. Otherwise return 0. + * If the columns are checked in order in the calling function, + * then a return value of 1 means that the column with the given + * column number may now contain a different column that + * hasn't been checked yet. + */ +int isl_tab_kill_col(struct isl_tab *tab, int col) +{ + var_from_col(tab, col)->is_zero = 1; + if (tab->need_undo) { + if (isl_tab_push_var(tab, isl_tab_undo_zero, + var_from_col(tab, col)) < 0) + return -1; + if (col != tab->n_dead) + swap_cols(tab, col, tab->n_dead); + tab->n_dead++; + return 0; + } else { + if (col != tab->n_col - 1) + swap_cols(tab, col, tab->n_col - 1); + var_from_col(tab, tab->n_col - 1)->index = -1; + tab->n_col--; + return 1; + } +} + +static int row_is_manifestly_non_integral(struct isl_tab *tab, int row) +{ + unsigned off = 2 + tab->M; + + if (tab->M && !isl_int_eq(tab->mat->row[row][2], + tab->mat->row[row][0])) + return 0; + if (isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead, + tab->n_col - tab->n_dead) != -1) + return 0; + + return !isl_int_is_divisible_by(tab->mat->row[row][1], + tab->mat->row[row][0]); +} + +/* For integer tableaus, check if any of the coordinates are stuck + * at a non-integral value. + */ +static int tab_is_manifestly_empty(struct isl_tab *tab) +{ + int i; + + if (tab->empty) + return 1; + if (tab->rational) + return 0; + + for (i = 0; i < tab->n_var; ++i) { + if (!tab->var[i].is_row) + continue; + if (row_is_manifestly_non_integral(tab, tab->var[i].index)) + return 1; + } + + return 0; +} + +/* Row variable "var" is non-negative and cannot attain any values + * larger than zero. This means that the coefficients of the unrestricted + * column variables are zero and that the coefficients of the non-negative + * column variables are zero or negative. + * Each of the non-negative variables with a negative coefficient can + * then also be written as the negative sum of non-negative variables + * and must therefore also be zero. + */ +static int close_row(struct isl_tab *tab, struct isl_tab_var *var) WARN_UNUSED; +static int close_row(struct isl_tab *tab, struct isl_tab_var *var) +{ + int j; + struct isl_mat *mat = tab->mat; + unsigned off = 2 + tab->M; + + isl_assert(tab->mat->ctx, var->is_nonneg, return -1); + var->is_zero = 1; + if (tab->need_undo) + if (isl_tab_push_var(tab, isl_tab_undo_zero, var) < 0) + return -1; + for (j = tab->n_dead; j < tab->n_col; ++j) { + int recheck; + if (isl_int_is_zero(mat->row[var->index][off + j])) + continue; + isl_assert(tab->mat->ctx, + isl_int_is_neg(mat->row[var->index][off + j]), return -1); + recheck = isl_tab_kill_col(tab, j); + if (recheck < 0) + return -1; + if (recheck) + --j; + } + if (isl_tab_mark_redundant(tab, var->index) < 0) + return -1; + if (tab_is_manifestly_empty(tab) && isl_tab_mark_empty(tab) < 0) + return -1; + return 0; +} + +/* Add a constraint to the tableau and allocate a row for it. + * Return the index into the constraint array "con". + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +int isl_tab_allocate_con(struct isl_tab *tab) +{ + int r; + + isl_assert(tab->mat->ctx, tab->n_row < tab->mat->n_row, return -1); + isl_assert(tab->mat->ctx, tab->n_con < tab->max_con, return -1); + + r = tab->n_con; + tab->con[r].index = tab->n_row; + tab->con[r].is_row = 1; + tab->con[r].is_nonneg = 0; + tab->con[r].is_zero = 0; + tab->con[r].is_redundant = 0; + tab->con[r].frozen = 0; + tab->con[r].negated = 0; + tab->row_var[tab->n_row] = ~r; + + tab->n_row++; + tab->n_con++; + if (isl_tab_push_var(tab, isl_tab_undo_allocate, &tab->con[r]) < 0) + return -1; + + return r; +} + +/* Move the entries in tab->var up one position, starting at "first", + * creating room for an extra entry at position "first". + * Since some of the entries of tab->row_var and tab->col_var contain + * indices into this array, they have to be updated accordingly. + */ +static int var_insert_entry(struct isl_tab *tab, int first) +{ + int i; + + if (tab->n_var >= tab->max_var) + isl_die(isl_tab_get_ctx(tab), isl_error_internal, + "not enough room for new variable", return -1); + if (first > tab->n_var) + isl_die(isl_tab_get_ctx(tab), isl_error_internal, + "invalid initial position", return -1); + + for (i = tab->n_var - 1; i >= first; --i) { + tab->var[i + 1] = tab->var[i]; + if (tab->var[i + 1].is_row) + tab->row_var[tab->var[i + 1].index]++; + else + tab->col_var[tab->var[i + 1].index]++; + } + + tab->n_var++; + + return 0; +} + +/* Drop the entry at position "first" in tab->var, moving all + * subsequent entries down. + * Since some of the entries of tab->row_var and tab->col_var contain + * indices into this array, they have to be updated accordingly. + */ +static int var_drop_entry(struct isl_tab *tab, int first) +{ + int i; + + if (first >= tab->n_var) + isl_die(isl_tab_get_ctx(tab), isl_error_internal, + "invalid initial position", return -1); + + tab->n_var--; + + for (i = first; i < tab->n_var; ++i) { + tab->var[i] = tab->var[i + 1]; + if (tab->var[i + 1].is_row) + tab->row_var[tab->var[i].index]--; + else + tab->col_var[tab->var[i].index]--; + } + + return 0; +} + +/* Add a variable to the tableau at position "r" and allocate a column for it. + * Return the index into the variable array "var", i.e., "r", + * or -1 on error. + */ +int isl_tab_insert_var(struct isl_tab *tab, int r) +{ + int i; + unsigned off = 2 + tab->M; + + isl_assert(tab->mat->ctx, tab->n_col < tab->mat->n_col, return -1); + + if (var_insert_entry(tab, r) < 0) + return -1; + + tab->var[r].index = tab->n_col; + tab->var[r].is_row = 0; + tab->var[r].is_nonneg = 0; + tab->var[r].is_zero = 0; + tab->var[r].is_redundant = 0; + tab->var[r].frozen = 0; + tab->var[r].negated = 0; + tab->col_var[tab->n_col] = r; + + for (i = 0; i < tab->n_row; ++i) + isl_int_set_si(tab->mat->row[i][off + tab->n_col], 0); + + tab->n_col++; + if (isl_tab_push_var(tab, isl_tab_undo_allocate, &tab->var[r]) < 0) + return -1; + + return r; +} + +/* Add a variable to the tableau and allocate a column for it. + * Return the index into the variable array "var". + */ +int isl_tab_allocate_var(struct isl_tab *tab) +{ + if (!tab) + return -1; + + return isl_tab_insert_var(tab, tab->n_var); +} + +/* Add a row to the tableau. The row is given as an affine combination + * of the original variables and needs to be expressed in terms of the + * column variables. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + * + * We add each term in turn. + * If r = n/d_r is the current sum and we need to add k x, then + * if x is a column variable, we increase the numerator of + * this column by k d_r + * if x = f/d_x is a row variable, then the new representation of r is + * + * n k f d_x/g n + d_r/g k f m/d_r n + m/d_g k f + * --- + --- = ------------------- = ------------------- + * d_r d_r d_r d_x/g m + * + * with g the gcd of d_r and d_x and m the lcm of d_r and d_x. + * + * If tab->M is set, then, internally, each variable x is represented + * as x' - M. We then also need no subtract k d_r from the coefficient of M. + */ +int isl_tab_add_row(struct isl_tab *tab, isl_int *line) +{ + int i; + int r; + isl_int *row; + isl_int a, b; + unsigned off = 2 + tab->M; + + r = isl_tab_allocate_con(tab); + if (r < 0) + return -1; + + isl_int_init(a); + isl_int_init(b); + row = tab->mat->row[tab->con[r].index]; + isl_int_set_si(row[0], 1); + isl_int_set(row[1], line[0]); + isl_seq_clr(row + 2, tab->M + tab->n_col); + for (i = 0; i < tab->n_var; ++i) { + if (tab->var[i].is_zero) + continue; + if (tab->var[i].is_row) { + isl_int_lcm(a, + row[0], tab->mat->row[tab->var[i].index][0]); + isl_int_swap(a, row[0]); + isl_int_divexact(a, row[0], a); + isl_int_divexact(b, + row[0], tab->mat->row[tab->var[i].index][0]); + isl_int_mul(b, b, line[1 + i]); + isl_seq_combine(row + 1, a, row + 1, + b, tab->mat->row[tab->var[i].index] + 1, + 1 + tab->M + tab->n_col); + } else + isl_int_addmul(row[off + tab->var[i].index], + line[1 + i], row[0]); + if (tab->M && i >= tab->n_param && i < tab->n_var - tab->n_div) + isl_int_submul(row[2], line[1 + i], row[0]); + } + isl_seq_normalize(tab->mat->ctx, row, off + tab->n_col); + isl_int_clear(a); + isl_int_clear(b); + + if (tab->row_sign) + tab->row_sign[tab->con[r].index] = isl_tab_row_unknown; + + return r; +} + +static int drop_row(struct isl_tab *tab, int row) +{ + isl_assert(tab->mat->ctx, ~tab->row_var[row] == tab->n_con - 1, return -1); + if (row != tab->n_row - 1) + swap_rows(tab, row, tab->n_row - 1); + tab->n_row--; + tab->n_con--; + return 0; +} + +/* Drop the variable in column "col" along with the column. + * The column is removed first because it may need to be moved + * into the last position and this process requires + * the contents of the col_var array in a state + * before the removal of the variable. + */ +static int drop_col(struct isl_tab *tab, int col) +{ + int var; + + var = tab->col_var[col]; + if (col != tab->n_col - 1) + swap_cols(tab, col, tab->n_col - 1); + tab->n_col--; + if (var_drop_entry(tab, var) < 0) + return -1; + return 0; +} + +/* Add inequality "ineq" and check if it conflicts with the + * previously added constraints or if it is obviously redundant. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +int isl_tab_add_ineq(struct isl_tab *tab, isl_int *ineq) +{ + int r; + int sgn; + isl_int cst; + + if (!tab) + return -1; + if (tab->bmap) { + struct isl_basic_map *bmap = tab->bmap; + + isl_assert(tab->mat->ctx, tab->n_eq == bmap->n_eq, return -1); + isl_assert(tab->mat->ctx, + tab->n_con == bmap->n_eq + bmap->n_ineq, return -1); + tab->bmap = isl_basic_map_add_ineq(tab->bmap, ineq); + if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0) + return -1; + if (!tab->bmap) + return -1; + } + if (tab->cone) { + isl_int_init(cst); + isl_int_set_si(cst, 0); + isl_int_swap(ineq[0], cst); + } + r = isl_tab_add_row(tab, ineq); + if (tab->cone) { + isl_int_swap(ineq[0], cst); + isl_int_clear(cst); + } + if (r < 0) + return -1; + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + return -1; + if (isl_tab_row_is_redundant(tab, tab->con[r].index)) { + if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0) + return -1; + return 0; + } + + sgn = restore_row(tab, &tab->con[r]); + if (sgn < -1) + return -1; + if (sgn < 0) + return isl_tab_mark_empty(tab); + if (tab->con[r].is_row && isl_tab_row_is_redundant(tab, tab->con[r].index)) + if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0) + return -1; + return 0; +} + +/* Pivot a non-negative variable down until it reaches the value zero + * and then pivot the variable into a column position. + */ +static int to_col(struct isl_tab *tab, struct isl_tab_var *var) WARN_UNUSED; +static int to_col(struct isl_tab *tab, struct isl_tab_var *var) +{ + int i; + int row, col; + unsigned off = 2 + tab->M; + + if (!var->is_row) + return 0; + + while (isl_int_is_pos(tab->mat->row[var->index][1])) { + find_pivot(tab, var, NULL, -1, &row, &col); + isl_assert(tab->mat->ctx, row != -1, return -1); + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + if (!var->is_row) + return 0; + } + + for (i = tab->n_dead; i < tab->n_col; ++i) + if (!isl_int_is_zero(tab->mat->row[var->index][off + i])) + break; + + isl_assert(tab->mat->ctx, i < tab->n_col, return -1); + if (isl_tab_pivot(tab, var->index, i) < 0) + return -1; + + return 0; +} + +/* We assume Gaussian elimination has been performed on the equalities. + * The equalities can therefore never conflict. + * Adding the equalities is currently only really useful for a later call + * to isl_tab_ineq_type. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +static struct isl_tab *add_eq(struct isl_tab *tab, isl_int *eq) +{ + int i; + int r; + + if (!tab) + return NULL; + r = isl_tab_add_row(tab, eq); + if (r < 0) + goto error; + + r = tab->con[r].index; + i = isl_seq_first_non_zero(tab->mat->row[r] + 2 + tab->M + tab->n_dead, + tab->n_col - tab->n_dead); + isl_assert(tab->mat->ctx, i >= 0, goto error); + i += tab->n_dead; + if (isl_tab_pivot(tab, r, i) < 0) + goto error; + if (isl_tab_kill_col(tab, i) < 0) + goto error; + tab->n_eq++; + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +static int row_is_manifestly_zero(struct isl_tab *tab, int row) +{ + unsigned off = 2 + tab->M; + + if (!isl_int_is_zero(tab->mat->row[row][1])) + return 0; + if (tab->M && !isl_int_is_zero(tab->mat->row[row][2])) + return 0; + return isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead, + tab->n_col - tab->n_dead) == -1; +} + +/* Add an equality that is known to be valid for the given tableau. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +int isl_tab_add_valid_eq(struct isl_tab *tab, isl_int *eq) +{ + struct isl_tab_var *var; + int r; + + if (!tab) + return -1; + r = isl_tab_add_row(tab, eq); + if (r < 0) + return -1; + + var = &tab->con[r]; + r = var->index; + if (row_is_manifestly_zero(tab, r)) { + var->is_zero = 1; + if (isl_tab_mark_redundant(tab, r) < 0) + return -1; + return 0; + } + + if (isl_int_is_neg(tab->mat->row[r][1])) { + isl_seq_neg(tab->mat->row[r] + 1, tab->mat->row[r] + 1, + 1 + tab->n_col); + var->negated = 1; + } + var->is_nonneg = 1; + if (to_col(tab, var) < 0) + return -1; + var->is_nonneg = 0; + if (isl_tab_kill_col(tab, var->index) < 0) + return -1; + + return 0; +} + +/* Add a zero row to "tab" and return the corresponding index + * in the constraint array. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +static int add_zero_row(struct isl_tab *tab) +{ + int r; + isl_int *row; + + r = isl_tab_allocate_con(tab); + if (r < 0) + return -1; + + row = tab->mat->row[tab->con[r].index]; + isl_seq_clr(row + 1, 1 + tab->M + tab->n_col); + isl_int_set_si(row[0], 1); + + return r; +} + +/* Add equality "eq" and check if it conflicts with the + * previously added constraints or if it is obviously redundant. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + * If tab->bmap is set, then two rows are needed instead of one. + */ +int isl_tab_add_eq(struct isl_tab *tab, isl_int *eq) +{ + struct isl_tab_undo *snap = NULL; + struct isl_tab_var *var; + int r; + int row; + int sgn; + isl_int cst; + + if (!tab) + return -1; + isl_assert(tab->mat->ctx, !tab->M, return -1); + + if (tab->need_undo) + snap = isl_tab_snap(tab); + + if (tab->cone) { + isl_int_init(cst); + isl_int_set_si(cst, 0); + isl_int_swap(eq[0], cst); + } + r = isl_tab_add_row(tab, eq); + if (tab->cone) { + isl_int_swap(eq[0], cst); + isl_int_clear(cst); + } + if (r < 0) + return -1; + + var = &tab->con[r]; + row = var->index; + if (row_is_manifestly_zero(tab, row)) { + if (snap) + return isl_tab_rollback(tab, snap); + return drop_row(tab, row); + } + + if (tab->bmap) { + tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq); + if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0) + return -1; + isl_seq_neg(eq, eq, 1 + tab->n_var); + tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq); + isl_seq_neg(eq, eq, 1 + tab->n_var); + if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0) + return -1; + if (!tab->bmap) + return -1; + if (add_zero_row(tab) < 0) + return -1; + } + + sgn = isl_int_sgn(tab->mat->row[row][1]); + + if (sgn > 0) { + isl_seq_neg(tab->mat->row[row] + 1, tab->mat->row[row] + 1, + 1 + tab->n_col); + var->negated = 1; + sgn = -1; + } + + if (sgn < 0) { + sgn = sign_of_max(tab, var); + if (sgn < -1) + return -1; + if (sgn < 0) { + if (isl_tab_mark_empty(tab) < 0) + return -1; + return 0; + } + } + + var->is_nonneg = 1; + if (to_col(tab, var) < 0) + return -1; + var->is_nonneg = 0; + if (isl_tab_kill_col(tab, var->index) < 0) + return -1; + + return 0; +} + +/* Construct and return an inequality that expresses an upper bound + * on the given div. + * In particular, if the div is given by + * + * d = floor(e/m) + * + * then the inequality expresses + * + * m d <= e + */ +static struct isl_vec *ineq_for_div(struct isl_basic_map *bmap, unsigned div) +{ + unsigned total; + unsigned div_pos; + struct isl_vec *ineq; + + if (!bmap) + return NULL; + + total = isl_basic_map_total_dim(bmap); + div_pos = 1 + total - bmap->n_div + div; + + ineq = isl_vec_alloc(bmap->ctx, 1 + total); + if (!ineq) + return NULL; + + isl_seq_cpy(ineq->el, bmap->div[div] + 1, 1 + total); + isl_int_neg(ineq->el[div_pos], bmap->div[div][0]); + return ineq; +} + +/* For a div d = floor(f/m), add the constraints + * + * f - m d >= 0 + * -(f-(m-1)) + m d >= 0 + * + * Note that the second constraint is the negation of + * + * f - m d >= m + * + * If add_ineq is not NULL, then this function is used + * instead of isl_tab_add_ineq to effectively add the inequalities. + * + * This function assumes that at least two more rows and at least + * two more elements in the constraint array are available in the tableau. + */ +static int add_div_constraints(struct isl_tab *tab, unsigned div, + int (*add_ineq)(void *user, isl_int *), void *user) +{ + unsigned total; + unsigned div_pos; + struct isl_vec *ineq; + + total = isl_basic_map_total_dim(tab->bmap); + div_pos = 1 + total - tab->bmap->n_div + div; + + ineq = ineq_for_div(tab->bmap, div); + if (!ineq) + goto error; + + if (add_ineq) { + if (add_ineq(user, ineq->el) < 0) + goto error; + } else { + if (isl_tab_add_ineq(tab, ineq->el) < 0) + goto error; + } + + isl_seq_neg(ineq->el, tab->bmap->div[div] + 1, 1 + total); + isl_int_set(ineq->el[div_pos], tab->bmap->div[div][0]); + isl_int_add(ineq->el[0], ineq->el[0], ineq->el[div_pos]); + isl_int_sub_ui(ineq->el[0], ineq->el[0], 1); + + if (add_ineq) { + if (add_ineq(user, ineq->el) < 0) + goto error; + } else { + if (isl_tab_add_ineq(tab, ineq->el) < 0) + goto error; + } + + isl_vec_free(ineq); + + return 0; +error: + isl_vec_free(ineq); + return -1; +} + +/* Check whether the div described by "div" is obviously non-negative. + * If we are using a big parameter, then we will encode the div + * as div' = M + div, which is always non-negative. + * Otherwise, we check whether div is a non-negative affine combination + * of non-negative variables. + */ +static int div_is_nonneg(struct isl_tab *tab, __isl_keep isl_vec *div) +{ + int i; + + if (tab->M) + return 1; + + if (isl_int_is_neg(div->el[1])) + return 0; + + for (i = 0; i < tab->n_var; ++i) { + if (isl_int_is_neg(div->el[2 + i])) + return 0; + if (isl_int_is_zero(div->el[2 + i])) + continue; + if (!tab->var[i].is_nonneg) + return 0; + } + + return 1; +} + +/* Add an extra div, prescribed by "div" to the tableau and + * the associated bmap (which is assumed to be non-NULL). + * + * If add_ineq is not NULL, then this function is used instead + * of isl_tab_add_ineq to add the div constraints. + * This complication is needed because the code in isl_tab_pip + * wants to perform some extra processing when an inequality + * is added to the tableau. + */ +int isl_tab_add_div(struct isl_tab *tab, __isl_keep isl_vec *div, + int (*add_ineq)(void *user, isl_int *), void *user) +{ + int r; + int k; + int nonneg; + + if (!tab || !div) + return -1; + + isl_assert(tab->mat->ctx, tab->bmap, return -1); + + nonneg = div_is_nonneg(tab, div); + + if (isl_tab_extend_cons(tab, 3) < 0) + return -1; + if (isl_tab_extend_vars(tab, 1) < 0) + return -1; + r = isl_tab_allocate_var(tab); + if (r < 0) + return -1; + + if (nonneg) + tab->var[r].is_nonneg = 1; + + tab->bmap = isl_basic_map_extend_space(tab->bmap, + isl_basic_map_get_space(tab->bmap), 1, 0, 2); + k = isl_basic_map_alloc_div(tab->bmap); + if (k < 0) + return -1; + isl_seq_cpy(tab->bmap->div[k], div->el, div->size); + if (isl_tab_push(tab, isl_tab_undo_bmap_div) < 0) + return -1; + + if (add_div_constraints(tab, k, add_ineq, user) < 0) + return -1; + + return r; +} + +/* If "track" is set, then we want to keep track of all constraints in tab + * in its bmap field. This field is initialized from a copy of "bmap", + * so we need to make sure that all constraints in "bmap" also appear + * in the constructed tab. + */ +__isl_give struct isl_tab *isl_tab_from_basic_map( + __isl_keep isl_basic_map *bmap, int track) +{ + int i; + struct isl_tab *tab; + + if (!bmap) + return NULL; + tab = isl_tab_alloc(bmap->ctx, + isl_basic_map_total_dim(bmap) + bmap->n_ineq + 1, + isl_basic_map_total_dim(bmap), 0); + if (!tab) + return NULL; + tab->preserve = track; + tab->rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) { + if (isl_tab_mark_empty(tab) < 0) + goto error; + goto done; + } + for (i = 0; i < bmap->n_eq; ++i) { + tab = add_eq(tab, bmap->eq[i]); + if (!tab) + return tab; + } + for (i = 0; i < bmap->n_ineq; ++i) { + if (isl_tab_add_ineq(tab, bmap->ineq[i]) < 0) + goto error; + if (tab->empty) + goto done; + } +done: + if (track && isl_tab_track_bmap(tab, isl_basic_map_copy(bmap)) < 0) + goto error; + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +__isl_give struct isl_tab *isl_tab_from_basic_set( + __isl_keep isl_basic_set *bset, int track) +{ + return isl_tab_from_basic_map(bset, track); +} + +/* Construct a tableau corresponding to the recession cone of "bset". + */ +struct isl_tab *isl_tab_from_recession_cone(__isl_keep isl_basic_set *bset, + int parametric) +{ + isl_int cst; + int i; + struct isl_tab *tab; + unsigned offset = 0; + + if (!bset) + return NULL; + if (parametric) + offset = isl_basic_set_dim(bset, isl_dim_param); + tab = isl_tab_alloc(bset->ctx, bset->n_eq + bset->n_ineq, + isl_basic_set_total_dim(bset) - offset, 0); + if (!tab) + return NULL; + tab->rational = ISL_F_ISSET(bset, ISL_BASIC_SET_RATIONAL); + tab->cone = 1; + + isl_int_init(cst); + isl_int_set_si(cst, 0); + for (i = 0; i < bset->n_eq; ++i) { + isl_int_swap(bset->eq[i][offset], cst); + if (offset > 0) { + if (isl_tab_add_eq(tab, bset->eq[i] + offset) < 0) + goto error; + } else + tab = add_eq(tab, bset->eq[i]); + isl_int_swap(bset->eq[i][offset], cst); + if (!tab) + goto done; + } + for (i = 0; i < bset->n_ineq; ++i) { + int r; + isl_int_swap(bset->ineq[i][offset], cst); + r = isl_tab_add_row(tab, bset->ineq[i] + offset); + isl_int_swap(bset->ineq[i][offset], cst); + if (r < 0) + goto error; + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + goto error; + } +done: + isl_int_clear(cst); + return tab; +error: + isl_int_clear(cst); + isl_tab_free(tab); + return NULL; +} + +/* Assuming "tab" is the tableau of a cone, check if the cone is + * bounded, i.e., if it is empty or only contains the origin. + */ +int isl_tab_cone_is_bounded(struct isl_tab *tab) +{ + int i; + + if (!tab) + return -1; + if (tab->empty) + return 1; + if (tab->n_dead == tab->n_col) + return 1; + + for (;;) { + for (i = tab->n_redundant; i < tab->n_row; ++i) { + struct isl_tab_var *var; + int sgn; + var = isl_tab_var_from_row(tab, i); + if (!var->is_nonneg) + continue; + sgn = sign_of_max(tab, var); + if (sgn < -1) + return -1; + if (sgn != 0) + return 0; + if (close_row(tab, var) < 0) + return -1; + break; + } + if (tab->n_dead == tab->n_col) + return 1; + if (i == tab->n_row) + return 0; + } +} + +int isl_tab_sample_is_integer(struct isl_tab *tab) +{ + int i; + + if (!tab) + return -1; + + for (i = 0; i < tab->n_var; ++i) { + int row; + if (!tab->var[i].is_row) + continue; + row = tab->var[i].index; + if (!isl_int_is_divisible_by(tab->mat->row[row][1], + tab->mat->row[row][0])) + return 0; + } + return 1; +} + +static struct isl_vec *extract_integer_sample(struct isl_tab *tab) +{ + int i; + struct isl_vec *vec; + + vec = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var); + if (!vec) + return NULL; + + isl_int_set_si(vec->block.data[0], 1); + for (i = 0; i < tab->n_var; ++i) { + if (!tab->var[i].is_row) + isl_int_set_si(vec->block.data[1 + i], 0); + else { + int row = tab->var[i].index; + isl_int_divexact(vec->block.data[1 + i], + tab->mat->row[row][1], tab->mat->row[row][0]); + } + } + + return vec; +} + +struct isl_vec *isl_tab_get_sample_value(struct isl_tab *tab) +{ + int i; + struct isl_vec *vec; + isl_int m; + + if (!tab) + return NULL; + + vec = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var); + if (!vec) + return NULL; + + isl_int_init(m); + + isl_int_set_si(vec->block.data[0], 1); + for (i = 0; i < tab->n_var; ++i) { + int row; + if (!tab->var[i].is_row) { + isl_int_set_si(vec->block.data[1 + i], 0); + continue; + } + row = tab->var[i].index; + isl_int_gcd(m, vec->block.data[0], tab->mat->row[row][0]); + isl_int_divexact(m, tab->mat->row[row][0], m); + isl_seq_scale(vec->block.data, vec->block.data, m, 1 + i); + isl_int_divexact(m, vec->block.data[0], tab->mat->row[row][0]); + isl_int_mul(vec->block.data[1 + i], m, tab->mat->row[row][1]); + } + vec = isl_vec_normalize(vec); + + isl_int_clear(m); + return vec; +} + +/* Update "bmap" based on the results of the tableau "tab". + * In particular, implicit equalities are made explicit, redundant constraints + * are removed and if the sample value happens to be integer, it is stored + * in "bmap" (unless "bmap" already had an integer sample). + * + * The tableau is assumed to have been created from "bmap" using + * isl_tab_from_basic_map. + */ +struct isl_basic_map *isl_basic_map_update_from_tab(struct isl_basic_map *bmap, + struct isl_tab *tab) +{ + int i; + unsigned n_eq; + + if (!bmap) + return NULL; + if (!tab) + return bmap; + + n_eq = tab->n_eq; + if (tab->empty) + bmap = isl_basic_map_set_to_empty(bmap); + else + for (i = bmap->n_ineq - 1; i >= 0; --i) { + if (isl_tab_is_equality(tab, n_eq + i)) + isl_basic_map_inequality_to_equality(bmap, i); + else if (isl_tab_is_redundant(tab, n_eq + i)) + isl_basic_map_drop_inequality(bmap, i); + } + if (bmap->n_eq != n_eq) + bmap = isl_basic_map_gauss(bmap, NULL); + if (!tab->rational && + bmap && !bmap->sample && isl_tab_sample_is_integer(tab)) + bmap->sample = extract_integer_sample(tab); + return bmap; +} + +struct isl_basic_set *isl_basic_set_update_from_tab(struct isl_basic_set *bset, + struct isl_tab *tab) +{ + return (struct isl_basic_set *)isl_basic_map_update_from_tab( + (struct isl_basic_map *)bset, tab); +} + +/* Given a non-negative variable "var", add a new non-negative variable + * that is the opposite of "var", ensuring that var can only attain the + * value zero. + * If var = n/d is a row variable, then the new variable = -n/d. + * If var is a column variables, then the new variable = -var. + * If the new variable cannot attain non-negative values, then + * the resulting tableau is empty. + * Otherwise, we know the value will be zero and we close the row. + */ +static int cut_to_hyperplane(struct isl_tab *tab, struct isl_tab_var *var) +{ + unsigned r; + isl_int *row; + int sgn; + unsigned off = 2 + tab->M; + + if (var->is_zero) + return 0; + isl_assert(tab->mat->ctx, !var->is_redundant, return -1); + isl_assert(tab->mat->ctx, var->is_nonneg, return -1); + + if (isl_tab_extend_cons(tab, 1) < 0) + return -1; + + r = tab->n_con; + tab->con[r].index = tab->n_row; + tab->con[r].is_row = 1; + tab->con[r].is_nonneg = 0; + tab->con[r].is_zero = 0; + tab->con[r].is_redundant = 0; + tab->con[r].frozen = 0; + tab->con[r].negated = 0; + tab->row_var[tab->n_row] = ~r; + row = tab->mat->row[tab->n_row]; + + if (var->is_row) { + isl_int_set(row[0], tab->mat->row[var->index][0]); + isl_seq_neg(row + 1, + tab->mat->row[var->index] + 1, 1 + tab->n_col); + } else { + isl_int_set_si(row[0], 1); + isl_seq_clr(row + 1, 1 + tab->n_col); + isl_int_set_si(row[off + var->index], -1); + } + + tab->n_row++; + tab->n_con++; + if (isl_tab_push_var(tab, isl_tab_undo_allocate, &tab->con[r]) < 0) + return -1; + + sgn = sign_of_max(tab, &tab->con[r]); + if (sgn < -1) + return -1; + if (sgn < 0) { + if (isl_tab_mark_empty(tab) < 0) + return -1; + return 0; + } + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + return -1; + /* sgn == 0 */ + if (close_row(tab, &tab->con[r]) < 0) + return -1; + + return 0; +} + +/* Given a tableau "tab" and an inequality constraint "con" of the tableau, + * relax the inequality by one. That is, the inequality r >= 0 is replaced + * by r' = r + 1 >= 0. + * If r is a row variable, we simply increase the constant term by one + * (taking into account the denominator). + * If r is a column variable, then we need to modify each row that + * refers to r = r' - 1 by substituting this equality, effectively + * subtracting the coefficient of the column from the constant. + * We should only do this if the minimum is manifestly unbounded, + * however. Otherwise, we may end up with negative sample values + * for non-negative variables. + * So, if r is a column variable with a minimum that is not + * manifestly unbounded, then we need to move it to a row. + * However, the sample value of this row may be negative, + * even after the relaxation, so we need to restore it. + * We therefore prefer to pivot a column up to a row, if possible. + */ +int isl_tab_relax(struct isl_tab *tab, int con) +{ + struct isl_tab_var *var; + + if (!tab) + return -1; + + var = &tab->con[con]; + + if (var->is_row && (var->index < 0 || var->index < tab->n_redundant)) + isl_die(tab->mat->ctx, isl_error_invalid, + "cannot relax redundant constraint", return -1); + if (!var->is_row && (var->index < 0 || var->index < tab->n_dead)) + isl_die(tab->mat->ctx, isl_error_invalid, + "cannot relax dead constraint", return -1); + + if (!var->is_row && !max_is_manifestly_unbounded(tab, var)) + if (to_row(tab, var, 1) < 0) + return -1; + if (!var->is_row && !min_is_manifestly_unbounded(tab, var)) + if (to_row(tab, var, -1) < 0) + return -1; + + if (var->is_row) { + isl_int_add(tab->mat->row[var->index][1], + tab->mat->row[var->index][1], tab->mat->row[var->index][0]); + if (restore_row(tab, var) < 0) + return -1; + } else { + int i; + unsigned off = 2 + tab->M; + + for (i = 0; i < tab->n_row; ++i) { + if (isl_int_is_zero(tab->mat->row[i][off + var->index])) + continue; + isl_int_sub(tab->mat->row[i][1], tab->mat->row[i][1], + tab->mat->row[i][off + var->index]); + } + + } + + if (isl_tab_push_var(tab, isl_tab_undo_relax, var) < 0) + return -1; + + return 0; +} + +/* Replace the variable v at position "pos" in the tableau "tab" + * by v' = v + shift. + * + * If the variable is in a column, then we first check if we can + * simply plug in v = v' - shift. The effect on a row with + * coefficient f/d for variable v is that the constant term c/d + * is replaced by (c - f * shift)/d. If shift is positive and + * f is negative for each row that needs to remain non-negative, + * then this is clearly safe. In other words, if the minimum of v + * is manifestly unbounded, then we can keep v in a column position. + * Otherwise, we can pivot it down to a row. + * Similarly, if shift is negative, we need to check if the maximum + * of is manifestly unbounded. + * + * If the variable is in a row (from the start or after pivoting), + * then the constant term c/d is replaced by (c + d * shift)/d. + */ +int isl_tab_shift_var(struct isl_tab *tab, int pos, isl_int shift) +{ + struct isl_tab_var *var; + + if (!tab) + return -1; + if (isl_int_is_zero(shift)) + return 0; + + var = &tab->var[pos]; + if (!var->is_row) { + if (isl_int_is_neg(shift)) { + if (!max_is_manifestly_unbounded(tab, var)) + if (to_row(tab, var, 1) < 0) + return -1; + } else { + if (!min_is_manifestly_unbounded(tab, var)) + if (to_row(tab, var, -1) < 0) + return -1; + } + } + + if (var->is_row) { + isl_int_addmul(tab->mat->row[var->index][1], + shift, tab->mat->row[var->index][0]); + } else { + int i; + unsigned off = 2 + tab->M; + + for (i = 0; i < tab->n_row; ++i) { + if (isl_int_is_zero(tab->mat->row[i][off + var->index])) + continue; + isl_int_submul(tab->mat->row[i][1], + shift, tab->mat->row[i][off + var->index]); + } + + } + + return 0; +} + +/* Remove the sign constraint from constraint "con". + * + * If the constraint variable was originally marked non-negative, + * then we make sure we mark it non-negative again during rollback. + */ +int isl_tab_unrestrict(struct isl_tab *tab, int con) +{ + struct isl_tab_var *var; + + if (!tab) + return -1; + + var = &tab->con[con]; + if (!var->is_nonneg) + return 0; + + var->is_nonneg = 0; + if (isl_tab_push_var(tab, isl_tab_undo_unrestrict, var) < 0) + return -1; + + return 0; +} + +int isl_tab_select_facet(struct isl_tab *tab, int con) +{ + if (!tab) + return -1; + + return cut_to_hyperplane(tab, &tab->con[con]); +} + +static int may_be_equality(struct isl_tab *tab, int row) +{ + return tab->rational ? isl_int_is_zero(tab->mat->row[row][1]) + : isl_int_lt(tab->mat->row[row][1], + tab->mat->row[row][0]); +} + +/* Check for (near) equalities among the constraints. + * A constraint is an equality if it is non-negative and if + * its maximal value is either + * - zero (in case of rational tableaus), or + * - strictly less than 1 (in case of integer tableaus) + * + * We first mark all non-redundant and non-dead variables that + * are not frozen and not obviously not an equality. + * Then we iterate over all marked variables if they can attain + * any values larger than zero or at least one. + * If the maximal value is zero, we mark any column variables + * that appear in the row as being zero and mark the row as being redundant. + * Otherwise, if the maximal value is strictly less than one (and the + * tableau is integer), then we restrict the value to being zero + * by adding an opposite non-negative variable. + */ +int isl_tab_detect_implicit_equalities(struct isl_tab *tab) +{ + int i; + unsigned n_marked; + + if (!tab) + return -1; + if (tab->empty) + return 0; + if (tab->n_dead == tab->n_col) + return 0; + + n_marked = 0; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + struct isl_tab_var *var = isl_tab_var_from_row(tab, i); + var->marked = !var->frozen && var->is_nonneg && + may_be_equality(tab, i); + if (var->marked) + n_marked++; + } + for (i = tab->n_dead; i < tab->n_col; ++i) { + struct isl_tab_var *var = var_from_col(tab, i); + var->marked = !var->frozen && var->is_nonneg; + if (var->marked) + n_marked++; + } + while (n_marked) { + struct isl_tab_var *var; + int sgn; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + var = isl_tab_var_from_row(tab, i); + if (var->marked) + break; + } + if (i == tab->n_row) { + for (i = tab->n_dead; i < tab->n_col; ++i) { + var = var_from_col(tab, i); + if (var->marked) + break; + } + if (i == tab->n_col) + break; + } + var->marked = 0; + n_marked--; + sgn = sign_of_max(tab, var); + if (sgn < 0) + return -1; + if (sgn == 0) { + if (close_row(tab, var) < 0) + return -1; + } else if (!tab->rational && !at_least_one(tab, var)) { + if (cut_to_hyperplane(tab, var) < 0) + return -1; + return isl_tab_detect_implicit_equalities(tab); + } + for (i = tab->n_redundant; i < tab->n_row; ++i) { + var = isl_tab_var_from_row(tab, i); + if (!var->marked) + continue; + if (may_be_equality(tab, i)) + continue; + var->marked = 0; + n_marked--; + } + } + + return 0; +} + +/* Update the element of row_var or col_var that corresponds to + * constraint tab->con[i] to a move from position "old" to position "i". + */ +static int update_con_after_move(struct isl_tab *tab, int i, int old) +{ + int *p; + int index; + + index = tab->con[i].index; + if (index == -1) + return 0; + p = tab->con[i].is_row ? tab->row_var : tab->col_var; + if (p[index] != ~old) + isl_die(tab->mat->ctx, isl_error_internal, + "broken internal state", return -1); + p[index] = ~i; + + return 0; +} + +/* Rotate the "n" constraints starting at "first" to the right, + * putting the last constraint in the position of the first constraint. + */ +static int rotate_constraints(struct isl_tab *tab, int first, int n) +{ + int i, last; + struct isl_tab_var var; + + if (n <= 1) + return 0; + + last = first + n - 1; + var = tab->con[last]; + for (i = last; i > first; --i) { + tab->con[i] = tab->con[i - 1]; + if (update_con_after_move(tab, i, i - 1) < 0) + return -1; + } + tab->con[first] = var; + if (update_con_after_move(tab, first, last) < 0) + return -1; + + return 0; +} + +/* Make the equalities that are implicit in "bmap" but that have been + * detected in the corresponding "tab" explicit in "bmap" and update + * "tab" to reflect the new order of the constraints. + * + * In particular, if inequality i is an implicit equality then + * isl_basic_map_inequality_to_equality will move the inequality + * in front of the other equality and it will move the last inequality + * in the position of inequality i. + * In the tableau, the inequalities of "bmap" are stored after the equalities + * and so the original order + * + * E E E E E A A A I B B B B L + * + * is changed into + * + * I E E E E E A A A L B B B B + * + * where I is the implicit equality, the E are equalities, + * the A inequalities before I, the B inequalities after I and + * L the last inequality. + * We therefore need to rotate to the right two sets of constraints, + * those up to and including I and those after I. + * + * If "tab" contains any constraints that are not in "bmap" then they + * appear after those in "bmap" and they should be left untouched. + * + * Note that this function leaves "bmap" in a temporary state + * as it does not call isl_basic_map_gauss. Calling this function + * is the responsibility of the caller. + */ +__isl_give isl_basic_map *isl_tab_make_equalities_explicit(struct isl_tab *tab, + __isl_take isl_basic_map *bmap) +{ + int i; + + if (!tab || !bmap) + return isl_basic_map_free(bmap); + if (tab->empty) + return bmap; + + for (i = bmap->n_ineq - 1; i >= 0; --i) { + if (!isl_tab_is_equality(tab, bmap->n_eq + i)) + continue; + isl_basic_map_inequality_to_equality(bmap, i); + if (rotate_constraints(tab, 0, tab->n_eq + i + 1) < 0) + return isl_basic_map_free(bmap); + if (rotate_constraints(tab, tab->n_eq + i + 1, + bmap->n_ineq - i) < 0) + return isl_basic_map_free(bmap); + tab->n_eq++; + } + + return bmap; +} + +static int con_is_redundant(struct isl_tab *tab, struct isl_tab_var *var) +{ + if (!tab) + return -1; + if (tab->rational) { + int sgn = sign_of_min(tab, var); + if (sgn < -1) + return -1; + return sgn >= 0; + } else { + int irred = isl_tab_min_at_most_neg_one(tab, var); + if (irred < 0) + return -1; + return !irred; + } +} + +/* Return an isl_tab_var that has been marked or NULL if no such + * variable can be found. + * The marked field has only been set for variables that + * appear in non-redundant rows or non-dead columns. + * + * Pick the last constraint variable that is marked and + * that appears in either a non-redundant row or a non-dead columns. + * Since the returned variable is tested for being a redundant constraint, + * there is no need to return any tab variable that corresponds to a variable. + */ +static struct isl_tab_var *select_marked(struct isl_tab *tab) +{ + int i; + struct isl_tab_var *var; + + for (i = tab->n_con - 1; i >= 0; --i) { + var = &tab->con[i]; + if (var->index < 0) + continue; + if (var->is_row && var->index < tab->n_redundant) + continue; + if (!var->is_row && var->index < tab->n_dead) + continue; + if (var->marked) + return var; + } + + return NULL; +} + +/* Check for (near) redundant constraints. + * A constraint is redundant if it is non-negative and if + * its minimal value (temporarily ignoring the non-negativity) is either + * - zero (in case of rational tableaus), or + * - strictly larger than -1 (in case of integer tableaus) + * + * We first mark all non-redundant and non-dead variables that + * are not frozen and not obviously negatively unbounded. + * Then we iterate over all marked variables if they can attain + * any values smaller than zero or at most negative one. + * If not, we mark the row as being redundant (assuming it hasn't + * been detected as being obviously redundant in the mean time). + */ +int isl_tab_detect_redundant(struct isl_tab *tab) +{ + int i; + unsigned n_marked; + + if (!tab) + return -1; + if (tab->empty) + return 0; + if (tab->n_redundant == tab->n_row) + return 0; + + n_marked = 0; + for (i = tab->n_redundant; i < tab->n_row; ++i) { + struct isl_tab_var *var = isl_tab_var_from_row(tab, i); + var->marked = !var->frozen && var->is_nonneg; + if (var->marked) + n_marked++; + } + for (i = tab->n_dead; i < tab->n_col; ++i) { + struct isl_tab_var *var = var_from_col(tab, i); + var->marked = !var->frozen && var->is_nonneg && + !min_is_manifestly_unbounded(tab, var); + if (var->marked) + n_marked++; + } + while (n_marked) { + struct isl_tab_var *var; + int red; + var = select_marked(tab); + if (!var) + break; + var->marked = 0; + n_marked--; + red = con_is_redundant(tab, var); + if (red < 0) + return -1; + if (red && !var->is_redundant) + if (isl_tab_mark_redundant(tab, var->index) < 0) + return -1; + for (i = tab->n_dead; i < tab->n_col; ++i) { + var = var_from_col(tab, i); + if (!var->marked) + continue; + if (!min_is_manifestly_unbounded(tab, var)) + continue; + var->marked = 0; + n_marked--; + } + } + + return 0; +} + +int isl_tab_is_equality(struct isl_tab *tab, int con) +{ + int row; + unsigned off; + + if (!tab) + return -1; + if (tab->con[con].is_zero) + return 1; + if (tab->con[con].is_redundant) + return 0; + if (!tab->con[con].is_row) + return tab->con[con].index < tab->n_dead; + + row = tab->con[con].index; + + off = 2 + tab->M; + return isl_int_is_zero(tab->mat->row[row][1]) && + (!tab->M || isl_int_is_zero(tab->mat->row[row][2])) && + isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead, + tab->n_col - tab->n_dead) == -1; +} + +/* Return the minimal value of the affine expression "f" with denominator + * "denom" in *opt, *opt_denom, assuming the tableau is not empty and + * the expression cannot attain arbitrarily small values. + * If opt_denom is NULL, then *opt is rounded up to the nearest integer. + * The return value reflects the nature of the result (empty, unbounded, + * minimal value returned in *opt). + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +enum isl_lp_result isl_tab_min(struct isl_tab *tab, + isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom, + unsigned flags) +{ + int r; + enum isl_lp_result res = isl_lp_ok; + struct isl_tab_var *var; + struct isl_tab_undo *snap; + + if (!tab) + return isl_lp_error; + + if (tab->empty) + return isl_lp_empty; + + snap = isl_tab_snap(tab); + r = isl_tab_add_row(tab, f); + if (r < 0) + return isl_lp_error; + var = &tab->con[r]; + for (;;) { + int row, col; + find_pivot(tab, var, var, -1, &row, &col); + if (row == var->index) { + res = isl_lp_unbounded; + break; + } + if (row == -1) + break; + if (isl_tab_pivot(tab, row, col) < 0) + return isl_lp_error; + } + isl_int_mul(tab->mat->row[var->index][0], + tab->mat->row[var->index][0], denom); + if (ISL_FL_ISSET(flags, ISL_TAB_SAVE_DUAL)) { + int i; + + isl_vec_free(tab->dual); + tab->dual = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_con); + if (!tab->dual) + return isl_lp_error; + isl_int_set(tab->dual->el[0], tab->mat->row[var->index][0]); + for (i = 0; i < tab->n_con; ++i) { + int pos; + if (tab->con[i].is_row) { + isl_int_set_si(tab->dual->el[1 + i], 0); + continue; + } + pos = 2 + tab->M + tab->con[i].index; + if (tab->con[i].negated) + isl_int_neg(tab->dual->el[1 + i], + tab->mat->row[var->index][pos]); + else + isl_int_set(tab->dual->el[1 + i], + tab->mat->row[var->index][pos]); + } + } + if (opt && res == isl_lp_ok) { + if (opt_denom) { + isl_int_set(*opt, tab->mat->row[var->index][1]); + isl_int_set(*opt_denom, tab->mat->row[var->index][0]); + } else + isl_int_cdiv_q(*opt, tab->mat->row[var->index][1], + tab->mat->row[var->index][0]); + } + if (isl_tab_rollback(tab, snap) < 0) + return isl_lp_error; + return res; +} + +/* Is the constraint at position "con" marked as being redundant? + * If it is marked as representing an equality, then it is not + * considered to be redundant. + * Note that isl_tab_mark_redundant marks both the isl_tab_var as + * redundant and moves the corresponding row into the first + * tab->n_redundant positions (or removes the row, assigning it index -1), + * so the final test is actually redundant itself. + */ +int isl_tab_is_redundant(struct isl_tab *tab, int con) +{ + if (!tab) + return -1; + if (con < 0 || con >= tab->n_con) + isl_die(isl_tab_get_ctx(tab), isl_error_invalid, + "position out of bounds", return -1); + if (tab->con[con].is_zero) + return 0; + if (tab->con[con].is_redundant) + return 1; + return tab->con[con].is_row && tab->con[con].index < tab->n_redundant; +} + +/* Take a snapshot of the tableau that can be restored by s call to + * isl_tab_rollback. + */ +struct isl_tab_undo *isl_tab_snap(struct isl_tab *tab) +{ + if (!tab) + return NULL; + tab->need_undo = 1; + return tab->top; +} + +/* Undo the operation performed by isl_tab_relax. + */ +static int unrelax(struct isl_tab *tab, struct isl_tab_var *var) WARN_UNUSED; +static int unrelax(struct isl_tab *tab, struct isl_tab_var *var) +{ + unsigned off = 2 + tab->M; + + if (!var->is_row && !max_is_manifestly_unbounded(tab, var)) + if (to_row(tab, var, 1) < 0) + return -1; + + if (var->is_row) { + isl_int_sub(tab->mat->row[var->index][1], + tab->mat->row[var->index][1], tab->mat->row[var->index][0]); + if (var->is_nonneg) { + int sgn = restore_row(tab, var); + isl_assert(tab->mat->ctx, sgn >= 0, return -1); + } + } else { + int i; + + for (i = 0; i < tab->n_row; ++i) { + if (isl_int_is_zero(tab->mat->row[i][off + var->index])) + continue; + isl_int_add(tab->mat->row[i][1], tab->mat->row[i][1], + tab->mat->row[i][off + var->index]); + } + + } + + return 0; +} + +/* Undo the operation performed by isl_tab_unrestrict. + * + * In particular, mark the variable as being non-negative and make + * sure the sample value respects this constraint. + */ +static int ununrestrict(struct isl_tab *tab, struct isl_tab_var *var) +{ + var->is_nonneg = 1; + + if (var->is_row && restore_row(tab, var) < -1) + return -1; + + return 0; +} + +static int perform_undo_var(struct isl_tab *tab, struct isl_tab_undo *undo) WARN_UNUSED; +static int perform_undo_var(struct isl_tab *tab, struct isl_tab_undo *undo) +{ + struct isl_tab_var *var = var_from_index(tab, undo->u.var_index); + switch (undo->type) { + case isl_tab_undo_nonneg: + var->is_nonneg = 0; + break; + case isl_tab_undo_redundant: + var->is_redundant = 0; + tab->n_redundant--; + restore_row(tab, isl_tab_var_from_row(tab, tab->n_redundant)); + break; + case isl_tab_undo_freeze: + var->frozen = 0; + break; + case isl_tab_undo_zero: + var->is_zero = 0; + if (!var->is_row) + tab->n_dead--; + break; + case isl_tab_undo_allocate: + if (undo->u.var_index >= 0) { + isl_assert(tab->mat->ctx, !var->is_row, return -1); + return drop_col(tab, var->index); + } + if (!var->is_row) { + if (!max_is_manifestly_unbounded(tab, var)) { + if (to_row(tab, var, 1) < 0) + return -1; + } else if (!min_is_manifestly_unbounded(tab, var)) { + if (to_row(tab, var, -1) < 0) + return -1; + } else + if (to_row(tab, var, 0) < 0) + return -1; + } + return drop_row(tab, var->index); + case isl_tab_undo_relax: + return unrelax(tab, var); + case isl_tab_undo_unrestrict: + return ununrestrict(tab, var); + default: + isl_die(tab->mat->ctx, isl_error_internal, + "perform_undo_var called on invalid undo record", + return -1); + } + + return 0; +} + +/* Restore the tableau to the state where the basic variables + * are those in "col_var". + * We first construct a list of variables that are currently in + * the basis, but shouldn't. Then we iterate over all variables + * that should be in the basis and for each one that is currently + * not in the basis, we exchange it with one of the elements of the + * list constructed before. + * We can always find an appropriate variable to pivot with because + * the current basis is mapped to the old basis by a non-singular + * matrix and so we can never end up with a zero row. + */ +static int restore_basis(struct isl_tab *tab, int *col_var) +{ + int i, j; + int n_extra = 0; + int *extra = NULL; /* current columns that contain bad stuff */ + unsigned off = 2 + tab->M; + + extra = isl_alloc_array(tab->mat->ctx, int, tab->n_col); + if (tab->n_col && !extra) + goto error; + for (i = 0; i < tab->n_col; ++i) { + for (j = 0; j < tab->n_col; ++j) + if (tab->col_var[i] == col_var[j]) + break; + if (j < tab->n_col) + continue; + extra[n_extra++] = i; + } + for (i = 0; i < tab->n_col && n_extra > 0; ++i) { + struct isl_tab_var *var; + int row; + + for (j = 0; j < tab->n_col; ++j) + if (col_var[i] == tab->col_var[j]) + break; + if (j < tab->n_col) + continue; + var = var_from_index(tab, col_var[i]); + row = var->index; + for (j = 0; j < n_extra; ++j) + if (!isl_int_is_zero(tab->mat->row[row][off+extra[j]])) + break; + isl_assert(tab->mat->ctx, j < n_extra, goto error); + if (isl_tab_pivot(tab, row, extra[j]) < 0) + goto error; + extra[j] = extra[--n_extra]; + } + + free(extra); + return 0; +error: + free(extra); + return -1; +} + +/* Remove all samples with index n or greater, i.e., those samples + * that were added since we saved this number of samples in + * isl_tab_save_samples. + */ +static void drop_samples_since(struct isl_tab *tab, int n) +{ + int i; + + for (i = tab->n_sample - 1; i >= 0 && tab->n_sample > n; --i) { + if (tab->sample_index[i] < n) + continue; + + if (i != tab->n_sample - 1) { + int t = tab->sample_index[tab->n_sample-1]; + tab->sample_index[tab->n_sample-1] = tab->sample_index[i]; + tab->sample_index[i] = t; + isl_mat_swap_rows(tab->samples, tab->n_sample-1, i); + } + tab->n_sample--; + } +} + +static int perform_undo(struct isl_tab *tab, struct isl_tab_undo *undo) WARN_UNUSED; +static int perform_undo(struct isl_tab *tab, struct isl_tab_undo *undo) +{ + switch (undo->type) { + case isl_tab_undo_rational: + tab->rational = 0; + break; + case isl_tab_undo_empty: + tab->empty = 0; + break; + case isl_tab_undo_nonneg: + case isl_tab_undo_redundant: + case isl_tab_undo_freeze: + case isl_tab_undo_zero: + case isl_tab_undo_allocate: + case isl_tab_undo_relax: + case isl_tab_undo_unrestrict: + return perform_undo_var(tab, undo); + case isl_tab_undo_bmap_eq: + return isl_basic_map_free_equality(tab->bmap, 1); + case isl_tab_undo_bmap_ineq: + return isl_basic_map_free_inequality(tab->bmap, 1); + case isl_tab_undo_bmap_div: + if (isl_basic_map_free_div(tab->bmap, 1) < 0) + return -1; + if (tab->samples) + tab->samples->n_col--; + break; + case isl_tab_undo_saved_basis: + if (restore_basis(tab, undo->u.col_var) < 0) + return -1; + break; + case isl_tab_undo_drop_sample: + tab->n_outside--; + break; + case isl_tab_undo_saved_samples: + drop_samples_since(tab, undo->u.n); + break; + case isl_tab_undo_callback: + return undo->u.callback->run(undo->u.callback); + default: + isl_assert(tab->mat->ctx, 0, return -1); + } + return 0; +} + +/* Return the tableau to the state it was in when the snapshot "snap" + * was taken. + */ +int isl_tab_rollback(struct isl_tab *tab, struct isl_tab_undo *snap) +{ + struct isl_tab_undo *undo, *next; + + if (!tab) + return -1; + + tab->in_undo = 1; + for (undo = tab->top; undo && undo != &tab->bottom; undo = next) { + next = undo->next; + if (undo == snap) + break; + if (perform_undo(tab, undo) < 0) { + tab->top = undo; + free_undo(tab); + tab->in_undo = 0; + return -1; + } + free_undo_record(undo); + } + tab->in_undo = 0; + tab->top = undo; + if (!undo) + return -1; + return 0; +} + +/* The given row "row" represents an inequality violated by all + * points in the tableau. Check for some special cases of such + * separating constraints. + * In particular, if the row has been reduced to the constant -1, + * then we know the inequality is adjacent (but opposite) to + * an equality in the tableau. + * If the row has been reduced to r = c*(-1 -r'), with r' an inequality + * of the tableau and c a positive constant, then the inequality + * is adjacent (but opposite) to the inequality r'. + */ +static enum isl_ineq_type separation_type(struct isl_tab *tab, unsigned row) +{ + int pos; + unsigned off = 2 + tab->M; + + if (tab->rational) + return isl_ineq_separate; + + if (!isl_int_is_one(tab->mat->row[row][0])) + return isl_ineq_separate; + + pos = isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead, + tab->n_col - tab->n_dead); + if (pos == -1) { + if (isl_int_is_negone(tab->mat->row[row][1])) + return isl_ineq_adj_eq; + else + return isl_ineq_separate; + } + + if (!isl_int_eq(tab->mat->row[row][1], + tab->mat->row[row][off + tab->n_dead + pos])) + return isl_ineq_separate; + + pos = isl_seq_first_non_zero( + tab->mat->row[row] + off + tab->n_dead + pos + 1, + tab->n_col - tab->n_dead - pos - 1); + + return pos == -1 ? isl_ineq_adj_ineq : isl_ineq_separate; +} + +/* Check the effect of inequality "ineq" on the tableau "tab". + * The result may be + * isl_ineq_redundant: satisfied by all points in the tableau + * isl_ineq_separate: satisfied by no point in the tableau + * isl_ineq_cut: satisfied by some by not all points + * isl_ineq_adj_eq: adjacent to an equality + * isl_ineq_adj_ineq: adjacent to an inequality. + */ +enum isl_ineq_type isl_tab_ineq_type(struct isl_tab *tab, isl_int *ineq) +{ + enum isl_ineq_type type = isl_ineq_error; + struct isl_tab_undo *snap = NULL; + int con; + int row; + + if (!tab) + return isl_ineq_error; + + if (isl_tab_extend_cons(tab, 1) < 0) + return isl_ineq_error; + + snap = isl_tab_snap(tab); + + con = isl_tab_add_row(tab, ineq); + if (con < 0) + goto error; + + row = tab->con[con].index; + if (isl_tab_row_is_redundant(tab, row)) + type = isl_ineq_redundant; + else if (isl_int_is_neg(tab->mat->row[row][1]) && + (tab->rational || + isl_int_abs_ge(tab->mat->row[row][1], + tab->mat->row[row][0]))) { + int nonneg = at_least_zero(tab, &tab->con[con]); + if (nonneg < 0) + goto error; + if (nonneg) + type = isl_ineq_cut; + else + type = separation_type(tab, row); + } else { + int red = con_is_redundant(tab, &tab->con[con]); + if (red < 0) + goto error; + if (!red) + type = isl_ineq_cut; + else + type = isl_ineq_redundant; + } + + if (isl_tab_rollback(tab, snap)) + return isl_ineq_error; + return type; +error: + return isl_ineq_error; +} + +int isl_tab_track_bmap(struct isl_tab *tab, __isl_take isl_basic_map *bmap) +{ + bmap = isl_basic_map_cow(bmap); + if (!tab || !bmap) + goto error; + + if (tab->empty) { + bmap = isl_basic_map_set_to_empty(bmap); + if (!bmap) + goto error; + tab->bmap = bmap; + return 0; + } + + isl_assert(tab->mat->ctx, tab->n_eq == bmap->n_eq, goto error); + isl_assert(tab->mat->ctx, + tab->n_con == bmap->n_eq + bmap->n_ineq, goto error); + + tab->bmap = bmap; + + return 0; +error: + isl_basic_map_free(bmap); + return -1; +} + +int isl_tab_track_bset(struct isl_tab *tab, __isl_take isl_basic_set *bset) +{ + return isl_tab_track_bmap(tab, (isl_basic_map *)bset); +} + +__isl_keep isl_basic_set *isl_tab_peek_bset(struct isl_tab *tab) +{ + if (!tab) + return NULL; + + return (isl_basic_set *)tab->bmap; +} + +static void isl_tab_print_internal(__isl_keep struct isl_tab *tab, + FILE *out, int indent) +{ + unsigned r, c; + int i; + + if (!tab) { + fprintf(out, "%*snull tab\n", indent, ""); + return; + } + fprintf(out, "%*sn_redundant: %d, n_dead: %d", indent, "", + tab->n_redundant, tab->n_dead); + if (tab->rational) + fprintf(out, ", rational"); + if (tab->empty) + fprintf(out, ", empty"); + fprintf(out, "\n"); + fprintf(out, "%*s[", indent, ""); + for (i = 0; i < tab->n_var; ++i) { + if (i) + fprintf(out, (i == tab->n_param || + i == tab->n_var - tab->n_div) ? "; " + : ", "); + fprintf(out, "%c%d%s", tab->var[i].is_row ? 'r' : 'c', + tab->var[i].index, + tab->var[i].is_zero ? " [=0]" : + tab->var[i].is_redundant ? " [R]" : ""); + } + fprintf(out, "]\n"); + fprintf(out, "%*s[", indent, ""); + for (i = 0; i < tab->n_con; ++i) { + if (i) + fprintf(out, ", "); + fprintf(out, "%c%d%s", tab->con[i].is_row ? 'r' : 'c', + tab->con[i].index, + tab->con[i].is_zero ? " [=0]" : + tab->con[i].is_redundant ? " [R]" : ""); + } + fprintf(out, "]\n"); + fprintf(out, "%*s[", indent, ""); + for (i = 0; i < tab->n_row; ++i) { + const char *sign = ""; + if (i) + fprintf(out, ", "); + if (tab->row_sign) { + if (tab->row_sign[i] == isl_tab_row_unknown) + sign = "?"; + else if (tab->row_sign[i] == isl_tab_row_neg) + sign = "-"; + else if (tab->row_sign[i] == isl_tab_row_pos) + sign = "+"; + else + sign = "+-"; + } + fprintf(out, "r%d: %d%s%s", i, tab->row_var[i], + isl_tab_var_from_row(tab, i)->is_nonneg ? " [>=0]" : "", sign); + } + fprintf(out, "]\n"); + fprintf(out, "%*s[", indent, ""); + for (i = 0; i < tab->n_col; ++i) { + if (i) + fprintf(out, ", "); + fprintf(out, "c%d: %d%s", i, tab->col_var[i], + var_from_col(tab, i)->is_nonneg ? " [>=0]" : ""); + } + fprintf(out, "]\n"); + r = tab->mat->n_row; + tab->mat->n_row = tab->n_row; + c = tab->mat->n_col; + tab->mat->n_col = 2 + tab->M + tab->n_col; + isl_mat_print_internal(tab->mat, out, indent); + tab->mat->n_row = r; + tab->mat->n_col = c; + if (tab->bmap) + isl_basic_map_print_internal(tab->bmap, out, indent); +} + +void isl_tab_dump(__isl_keep struct isl_tab *tab) +{ + isl_tab_print_internal(tab, stderr, 0); +} Index: lib/Analysis/isl/isl_tab_pip.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_tab_pip.c @@ -0,0 +1,5785 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + */ + +#include +#include "isl_map_private.h" +#include +#include "isl_tab.h" +#include "isl_sample.h" +#include +#include +#include +#include +#include + +/* + * The implementation of parametric integer linear programming in this file + * was inspired by the paper "Parametric Integer Programming" and the + * report "Solving systems of affine (in)equalities" by Paul Feautrier + * (and others). + * + * The strategy used for obtaining a feasible solution is different + * from the one used in isl_tab.c. In particular, in isl_tab.c, + * upon finding a constraint that is not yet satisfied, we pivot + * in a row that increases the constant term of the row holding the + * constraint, making sure the sample solution remains feasible + * for all the constraints it already satisfied. + * Here, we always pivot in the row holding the constraint, + * choosing a column that induces the lexicographically smallest + * increment to the sample solution. + * + * By starting out from a sample value that is lexicographically + * smaller than any integer point in the problem space, the first + * feasible integer sample point we find will also be the lexicographically + * smallest. If all variables can be assumed to be non-negative, + * then the initial sample value may be chosen equal to zero. + * However, we will not make this assumption. Instead, we apply + * the "big parameter" trick. Any variable x is then not directly + * used in the tableau, but instead it is represented by another + * variable x' = M + x, where M is an arbitrarily large (positive) + * value. x' is therefore always non-negative, whatever the value of x. + * Taking as initial sample value x' = 0 corresponds to x = -M, + * which is always smaller than any possible value of x. + * + * The big parameter trick is used in the main tableau and + * also in the context tableau if isl_context_lex is used. + * In this case, each tableaus has its own big parameter. + * Before doing any real work, we check if all the parameters + * happen to be non-negative. If so, we drop the column corresponding + * to M from the initial context tableau. + * If isl_context_gbr is used, then the big parameter trick is only + * used in the main tableau. + */ + +struct isl_context; +struct isl_context_op { + /* detect nonnegative parameters in context and mark them in tab */ + struct isl_tab *(*detect_nonnegative_parameters)( + struct isl_context *context, struct isl_tab *tab); + /* return temporary reference to basic set representation of context */ + struct isl_basic_set *(*peek_basic_set)(struct isl_context *context); + /* return temporary reference to tableau representation of context */ + struct isl_tab *(*peek_tab)(struct isl_context *context); + /* add equality; check is 1 if eq may not be valid; + * update is 1 if we may want to call ineq_sign on context later. + */ + void (*add_eq)(struct isl_context *context, isl_int *eq, + int check, int update); + /* add inequality; check is 1 if ineq may not be valid; + * update is 1 if we may want to call ineq_sign on context later. + */ + void (*add_ineq)(struct isl_context *context, isl_int *ineq, + int check, int update); + /* check sign of ineq based on previous information. + * strict is 1 if saturation should be treated as a positive sign. + */ + enum isl_tab_row_sign (*ineq_sign)(struct isl_context *context, + isl_int *ineq, int strict); + /* check if inequality maintains feasibility */ + int (*test_ineq)(struct isl_context *context, isl_int *ineq); + /* return index of a div that corresponds to "div" */ + int (*get_div)(struct isl_context *context, struct isl_tab *tab, + struct isl_vec *div); + /* add div "div" to context and return non-negativity */ + int (*add_div)(struct isl_context *context, struct isl_vec *div); + int (*detect_equalities)(struct isl_context *context, + struct isl_tab *tab); + /* return row index of "best" split */ + int (*best_split)(struct isl_context *context, struct isl_tab *tab); + /* check if context has already been determined to be empty */ + int (*is_empty)(struct isl_context *context); + /* check if context is still usable */ + int (*is_ok)(struct isl_context *context); + /* save a copy/snapshot of context */ + void *(*save)(struct isl_context *context); + /* restore saved context */ + void (*restore)(struct isl_context *context, void *); + /* discard saved context */ + void (*discard)(void *); + /* invalidate context */ + void (*invalidate)(struct isl_context *context); + /* free context */ + void (*free)(struct isl_context *context); +}; + +struct isl_context { + struct isl_context_op *op; +}; + +struct isl_context_lex { + struct isl_context context; + struct isl_tab *tab; +}; + +/* A stack (linked list) of solutions of subtrees of the search space. + * + * "M" describes the solution in terms of the dimensions of "dom". + * The number of columns of "M" is one more than the total number + * of dimensions of "dom". + * + * If "M" is NULL, then there is no solution on "dom". + */ +struct isl_partial_sol { + int level; + struct isl_basic_set *dom; + struct isl_mat *M; + + struct isl_partial_sol *next; +}; + +struct isl_sol; +struct isl_sol_callback { + struct isl_tab_callback callback; + struct isl_sol *sol; +}; + +/* isl_sol is an interface for constructing a solution to + * a parametric integer linear programming problem. + * Every time the algorithm reaches a state where a solution + * can be read off from the tableau (including cases where the tableau + * is empty), the function "add" is called on the isl_sol passed + * to find_solutions_main. + * + * The context tableau is owned by isl_sol and is updated incrementally. + * + * There are currently two implementations of this interface, + * isl_sol_map, which simply collects the solutions in an isl_map + * and (optionally) the parts of the context where there is no solution + * in an isl_set, and + * isl_sol_for, which calls a user-defined function for each part of + * the solution. + */ +struct isl_sol { + int error; + int rational; + int level; + int max; + int n_out; + struct isl_context *context; + struct isl_partial_sol *partial; + void (*add)(struct isl_sol *sol, + struct isl_basic_set *dom, struct isl_mat *M); + void (*add_empty)(struct isl_sol *sol, struct isl_basic_set *bset); + void (*free)(struct isl_sol *sol); + struct isl_sol_callback dec_level; +}; + +static void sol_free(struct isl_sol *sol) +{ + struct isl_partial_sol *partial, *next; + if (!sol) + return; + for (partial = sol->partial; partial; partial = next) { + next = partial->next; + isl_basic_set_free(partial->dom); + isl_mat_free(partial->M); + free(partial); + } + sol->free(sol); +} + +/* Push a partial solution represented by a domain and mapping M + * onto the stack of partial solutions. + */ +static void sol_push_sol(struct isl_sol *sol, + struct isl_basic_set *dom, struct isl_mat *M) +{ + struct isl_partial_sol *partial; + + if (sol->error || !dom) + goto error; + + partial = isl_alloc_type(dom->ctx, struct isl_partial_sol); + if (!partial) + goto error; + + partial->level = sol->level; + partial->dom = dom; + partial->M = M; + partial->next = sol->partial; + + sol->partial = partial; + + return; +error: + isl_basic_set_free(dom); + isl_mat_free(M); + sol->error = 1; +} + +/* Pop one partial solution from the partial solution stack and + * pass it on to sol->add or sol->add_empty. + */ +static void sol_pop_one(struct isl_sol *sol) +{ + struct isl_partial_sol *partial; + + partial = sol->partial; + sol->partial = partial->next; + + if (partial->M) + sol->add(sol, partial->dom, partial->M); + else + sol->add_empty(sol, partial->dom); + free(partial); +} + +/* Return a fresh copy of the domain represented by the context tableau. + */ +static struct isl_basic_set *sol_domain(struct isl_sol *sol) +{ + struct isl_basic_set *bset; + + if (sol->error) + return NULL; + + bset = isl_basic_set_dup(sol->context->op->peek_basic_set(sol->context)); + bset = isl_basic_set_update_from_tab(bset, + sol->context->op->peek_tab(sol->context)); + + return bset; +} + +/* Check whether two partial solutions have the same mapping, where n_div + * is the number of divs that the two partial solutions have in common. + */ +static int same_solution(struct isl_partial_sol *s1, struct isl_partial_sol *s2, + unsigned n_div) +{ + int i; + unsigned dim; + + if (!s1->M != !s2->M) + return 0; + if (!s1->M) + return 1; + + dim = isl_basic_set_total_dim(s1->dom) - s1->dom->n_div; + + for (i = 0; i < s1->M->n_row; ++i) { + if (isl_seq_first_non_zero(s1->M->row[i]+1+dim+n_div, + s1->M->n_col-1-dim-n_div) != -1) + return 0; + if (isl_seq_first_non_zero(s2->M->row[i]+1+dim+n_div, + s2->M->n_col-1-dim-n_div) != -1) + return 0; + if (!isl_seq_eq(s1->M->row[i], s2->M->row[i], 1+dim+n_div)) + return 0; + } + return 1; +} + +/* Pop all solutions from the partial solution stack that were pushed onto + * the stack at levels that are deeper than the current level. + * If the two topmost elements on the stack have the same level + * and represent the same solution, then their domains are combined. + * This combined domain is the same as the current context domain + * as sol_pop is called each time we move back to a higher level. + * If the outer level (0) has been reached, then all partial solutions + * at the current level are also popped off. + */ +static void sol_pop(struct isl_sol *sol) +{ + struct isl_partial_sol *partial; + unsigned n_div; + + if (sol->error) + return; + + partial = sol->partial; + if (!partial) + return; + + if (partial->level == 0 && sol->level == 0) { + for (partial = sol->partial; partial; partial = sol->partial) + sol_pop_one(sol); + return; + } + + if (partial->level <= sol->level) + return; + + if (partial->next && partial->next->level == partial->level) { + n_div = isl_basic_set_dim( + sol->context->op->peek_basic_set(sol->context), + isl_dim_div); + + if (!same_solution(partial, partial->next, n_div)) { + sol_pop_one(sol); + sol_pop_one(sol); + } else { + struct isl_basic_set *bset; + isl_mat *M; + unsigned n; + + n = isl_basic_set_dim(partial->next->dom, isl_dim_div); + n -= n_div; + bset = sol_domain(sol); + isl_basic_set_free(partial->next->dom); + partial->next->dom = bset; + M = partial->next->M; + if (M) { + M = isl_mat_drop_cols(M, M->n_col - n, n); + partial->next->M = M; + if (!M) + goto error; + } + partial->next->level = sol->level; + + if (!bset) + goto error; + + sol->partial = partial->next; + isl_basic_set_free(partial->dom); + isl_mat_free(partial->M); + free(partial); + } + } else + sol_pop_one(sol); + + if (sol->level == 0) { + for (partial = sol->partial; partial; partial = sol->partial) + sol_pop_one(sol); + return; + } + + if (0) +error: sol->error = 1; +} + +static void sol_dec_level(struct isl_sol *sol) +{ + if (sol->error) + return; + + sol->level--; + + sol_pop(sol); +} + +static int sol_dec_level_wrap(struct isl_tab_callback *cb) +{ + struct isl_sol_callback *callback = (struct isl_sol_callback *)cb; + + sol_dec_level(callback->sol); + + return callback->sol->error ? -1 : 0; +} + +/* Move down to next level and push callback onto context tableau + * to decrease the level again when it gets rolled back across + * the current state. That is, dec_level will be called with + * the context tableau in the same state as it is when inc_level + * is called. + */ +static void sol_inc_level(struct isl_sol *sol) +{ + struct isl_tab *tab; + + if (sol->error) + return; + + sol->level++; + tab = sol->context->op->peek_tab(sol->context); + if (isl_tab_push_callback(tab, &sol->dec_level.callback) < 0) + sol->error = 1; +} + +static void scale_rows(struct isl_mat *mat, isl_int m, int n_row) +{ + int i; + + if (isl_int_is_one(m)) + return; + + for (i = 0; i < n_row; ++i) + isl_seq_scale(mat->row[i], mat->row[i], m, mat->n_col); +} + +/* Add the solution identified by the tableau and the context tableau. + * + * The layout of the variables is as follows. + * tab->n_var is equal to the total number of variables in the input + * map (including divs that were copied from the context) + * + the number of extra divs constructed + * Of these, the first tab->n_param and the last tab->n_div variables + * correspond to the variables in the context, i.e., + * tab->n_param + tab->n_div = context_tab->n_var + * tab->n_param is equal to the number of parameters and input + * dimensions in the input map + * tab->n_div is equal to the number of divs in the context + * + * If there is no solution, then call add_empty with a basic set + * that corresponds to the context tableau. (If add_empty is NULL, + * then do nothing). + * + * If there is a solution, then first construct a matrix that maps + * all dimensions of the context to the output variables, i.e., + * the output dimensions in the input map. + * The divs in the input map (if any) that do not correspond to any + * div in the context do not appear in the solution. + * The algorithm will make sure that they have an integer value, + * but these values themselves are of no interest. + * We have to be careful not to drop or rearrange any divs in the + * context because that would change the meaning of the matrix. + * + * To extract the value of the output variables, it should be noted + * that we always use a big parameter M in the main tableau and so + * the variable stored in this tableau is not an output variable x itself, but + * x' = M + x (in case of minimization) + * or + * x' = M - x (in case of maximization) + * If x' appears in a column, then its optimal value is zero, + * which means that the optimal value of x is an unbounded number + * (-M for minimization and M for maximization). + * We currently assume that the output dimensions in the original map + * are bounded, so this cannot occur. + * Similarly, when x' appears in a row, then the coefficient of M in that + * row is necessarily 1. + * If the row in the tableau represents + * d x' = c + d M + e(y) + * then, in case of minimization, the corresponding row in the matrix + * will be + * a c + a e(y) + * with a d = m, the (updated) common denominator of the matrix. + * In case of maximization, the row will be + * -a c - a e(y) + */ +static void sol_add(struct isl_sol *sol, struct isl_tab *tab) +{ + struct isl_basic_set *bset = NULL; + struct isl_mat *mat = NULL; + unsigned off; + int row; + isl_int m; + + if (sol->error || !tab) + goto error; + + if (tab->empty && !sol->add_empty) + return; + if (sol->context->op->is_empty(sol->context)) + return; + + bset = sol_domain(sol); + + if (tab->empty) { + sol_push_sol(sol, bset, NULL); + return; + } + + off = 2 + tab->M; + + mat = isl_mat_alloc(tab->mat->ctx, 1 + sol->n_out, + 1 + tab->n_param + tab->n_div); + if (!mat) + goto error; + + isl_int_init(m); + + isl_seq_clr(mat->row[0] + 1, mat->n_col - 1); + isl_int_set_si(mat->row[0][0], 1); + for (row = 0; row < sol->n_out; ++row) { + int i = tab->n_param + row; + int r, j; + + isl_seq_clr(mat->row[1 + row], mat->n_col); + if (!tab->var[i].is_row) { + if (tab->M) + isl_die(mat->ctx, isl_error_invalid, + "unbounded optimum", goto error2); + continue; + } + + r = tab->var[i].index; + if (tab->M && + isl_int_ne(tab->mat->row[r][2], tab->mat->row[r][0])) + isl_die(mat->ctx, isl_error_invalid, + "unbounded optimum", goto error2); + isl_int_gcd(m, mat->row[0][0], tab->mat->row[r][0]); + isl_int_divexact(m, tab->mat->row[r][0], m); + scale_rows(mat, m, 1 + row); + isl_int_divexact(m, mat->row[0][0], tab->mat->row[r][0]); + isl_int_mul(mat->row[1 + row][0], m, tab->mat->row[r][1]); + for (j = 0; j < tab->n_param; ++j) { + int col; + if (tab->var[j].is_row) + continue; + col = tab->var[j].index; + isl_int_mul(mat->row[1 + row][1 + j], m, + tab->mat->row[r][off + col]); + } + for (j = 0; j < tab->n_div; ++j) { + int col; + if (tab->var[tab->n_var - tab->n_div+j].is_row) + continue; + col = tab->var[tab->n_var - tab->n_div+j].index; + isl_int_mul(mat->row[1 + row][1 + tab->n_param + j], m, + tab->mat->row[r][off + col]); + } + if (sol->max) + isl_seq_neg(mat->row[1 + row], mat->row[1 + row], + mat->n_col); + } + + isl_int_clear(m); + + sol_push_sol(sol, bset, mat); + return; +error2: + isl_int_clear(m); +error: + isl_basic_set_free(bset); + isl_mat_free(mat); + sol->error = 1; +} + +struct isl_sol_map { + struct isl_sol sol; + struct isl_map *map; + struct isl_set *empty; +}; + +static void sol_map_free(struct isl_sol_map *sol_map) +{ + if (!sol_map) + return; + if (sol_map->sol.context) + sol_map->sol.context->op->free(sol_map->sol.context); + isl_map_free(sol_map->map); + isl_set_free(sol_map->empty); + free(sol_map); +} + +static void sol_map_free_wrap(struct isl_sol *sol) +{ + sol_map_free((struct isl_sol_map *)sol); +} + +/* This function is called for parts of the context where there is + * no solution, with "bset" corresponding to the context tableau. + * Simply add the basic set to the set "empty". + */ +static void sol_map_add_empty(struct isl_sol_map *sol, + struct isl_basic_set *bset) +{ + if (!bset || !sol->empty) + goto error; + + sol->empty = isl_set_grow(sol->empty, 1); + bset = isl_basic_set_simplify(bset); + bset = isl_basic_set_finalize(bset); + sol->empty = isl_set_add_basic_set(sol->empty, isl_basic_set_copy(bset)); + if (!sol->empty) + goto error; + isl_basic_set_free(bset); + return; +error: + isl_basic_set_free(bset); + sol->sol.error = 1; +} + +static void sol_map_add_empty_wrap(struct isl_sol *sol, + struct isl_basic_set *bset) +{ + sol_map_add_empty((struct isl_sol_map *)sol, bset); +} + +/* Given a basic map "dom" that represents the context and an affine + * matrix "M" that maps the dimensions of the context to the + * output variables, construct a basic map with the same parameters + * and divs as the context, the dimensions of the context as input + * dimensions and a number of output dimensions that is equal to + * the number of output dimensions in the input map. + * + * The constraints and divs of the context are simply copied + * from "dom". For each row + * x = c + e(y) + * an equality + * c + e(y) - d x = 0 + * is added, with d the common denominator of M. + */ +static void sol_map_add(struct isl_sol_map *sol, + struct isl_basic_set *dom, struct isl_mat *M) +{ + int i; + struct isl_basic_map *bmap = NULL; + unsigned n_eq; + unsigned n_ineq; + unsigned nparam; + unsigned total; + unsigned n_div; + unsigned n_out; + + if (sol->sol.error || !dom || !M) + goto error; + + n_out = sol->sol.n_out; + n_eq = dom->n_eq + n_out; + n_ineq = dom->n_ineq; + n_div = dom->n_div; + nparam = isl_basic_set_total_dim(dom) - n_div; + total = isl_map_dim(sol->map, isl_dim_all); + bmap = isl_basic_map_alloc_space(isl_map_get_space(sol->map), + n_div, n_eq, 2 * n_div + n_ineq); + if (!bmap) + goto error; + if (sol->sol.rational) + ISL_F_SET(bmap, ISL_BASIC_MAP_RATIONAL); + for (i = 0; i < dom->n_div; ++i) { + int k = isl_basic_map_alloc_div(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->div[k], dom->div[i], 1 + 1 + nparam); + isl_seq_clr(bmap->div[k] + 1 + 1 + nparam, total - nparam); + isl_seq_cpy(bmap->div[k] + 1 + 1 + total, + dom->div[i] + 1 + 1 + nparam, i); + } + for (i = 0; i < dom->n_eq; ++i) { + int k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->eq[k], dom->eq[i], 1 + nparam); + isl_seq_clr(bmap->eq[k] + 1 + nparam, total - nparam); + isl_seq_cpy(bmap->eq[k] + 1 + total, + dom->eq[i] + 1 + nparam, n_div); + } + for (i = 0; i < dom->n_ineq; ++i) { + int k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->ineq[k], dom->ineq[i], 1 + nparam); + isl_seq_clr(bmap->ineq[k] + 1 + nparam, total - nparam); + isl_seq_cpy(bmap->ineq[k] + 1 + total, + dom->ineq[i] + 1 + nparam, n_div); + } + for (i = 0; i < M->n_row - 1; ++i) { + int k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_cpy(bmap->eq[k], M->row[1 + i], 1 + nparam); + isl_seq_clr(bmap->eq[k] + 1 + nparam, n_out); + isl_int_neg(bmap->eq[k][1 + nparam + i], M->row[0][0]); + isl_seq_cpy(bmap->eq[k] + 1 + nparam + n_out, + M->row[1 + i] + 1 + nparam, n_div); + } + bmap = isl_basic_map_simplify(bmap); + bmap = isl_basic_map_finalize(bmap); + sol->map = isl_map_grow(sol->map, 1); + sol->map = isl_map_add_basic_map(sol->map, bmap); + isl_basic_set_free(dom); + isl_mat_free(M); + if (!sol->map) + sol->sol.error = 1; + return; +error: + isl_basic_set_free(dom); + isl_mat_free(M); + isl_basic_map_free(bmap); + sol->sol.error = 1; +} + +static void sol_map_add_wrap(struct isl_sol *sol, + struct isl_basic_set *dom, struct isl_mat *M) +{ + sol_map_add((struct isl_sol_map *)sol, dom, M); +} + + +/* Store the "parametric constant" of row "row" of tableau "tab" in "line", + * i.e., the constant term and the coefficients of all variables that + * appear in the context tableau. + * Note that the coefficient of the big parameter M is NOT copied. + * The context tableau may not have a big parameter and even when it + * does, it is a different big parameter. + */ +static void get_row_parameter_line(struct isl_tab *tab, int row, isl_int *line) +{ + int i; + unsigned off = 2 + tab->M; + + isl_int_set(line[0], tab->mat->row[row][1]); + for (i = 0; i < tab->n_param; ++i) { + if (tab->var[i].is_row) + isl_int_set_si(line[1 + i], 0); + else { + int col = tab->var[i].index; + isl_int_set(line[1 + i], tab->mat->row[row][off + col]); + } + } + for (i = 0; i < tab->n_div; ++i) { + if (tab->var[tab->n_var - tab->n_div + i].is_row) + isl_int_set_si(line[1 + tab->n_param + i], 0); + else { + int col = tab->var[tab->n_var - tab->n_div + i].index; + isl_int_set(line[1 + tab->n_param + i], + tab->mat->row[row][off + col]); + } + } +} + +/* Check if rows "row1" and "row2" have identical "parametric constants", + * as explained above. + * In this case, we also insist that the coefficients of the big parameter + * be the same as the values of the constants will only be the same + * if these coefficients are also the same. + */ +static int identical_parameter_line(struct isl_tab *tab, int row1, int row2) +{ + int i; + unsigned off = 2 + tab->M; + + if (isl_int_ne(tab->mat->row[row1][1], tab->mat->row[row2][1])) + return 0; + + if (tab->M && isl_int_ne(tab->mat->row[row1][2], + tab->mat->row[row2][2])) + return 0; + + for (i = 0; i < tab->n_param + tab->n_div; ++i) { + int pos = i < tab->n_param ? i : + tab->n_var - tab->n_div + i - tab->n_param; + int col; + + if (tab->var[pos].is_row) + continue; + col = tab->var[pos].index; + if (isl_int_ne(tab->mat->row[row1][off + col], + tab->mat->row[row2][off + col])) + return 0; + } + return 1; +} + +/* Return an inequality that expresses that the "parametric constant" + * should be non-negative. + * This function is only called when the coefficient of the big parameter + * is equal to zero. + */ +static struct isl_vec *get_row_parameter_ineq(struct isl_tab *tab, int row) +{ + struct isl_vec *ineq; + + ineq = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_param + tab->n_div); + if (!ineq) + return NULL; + + get_row_parameter_line(tab, row, ineq->el); + if (ineq) + ineq = isl_vec_normalize(ineq); + + return ineq; +} + +/* Normalize a div expression of the form + * + * [(g*f(x) + c)/(g * m)] + * + * with c the constant term and f(x) the remaining coefficients, to + * + * [(f(x) + [c/g])/m] + */ +static void normalize_div(__isl_keep isl_vec *div) +{ + isl_ctx *ctx = isl_vec_get_ctx(div); + int len = div->size - 2; + + isl_seq_gcd(div->el + 2, len, &ctx->normalize_gcd); + isl_int_gcd(ctx->normalize_gcd, ctx->normalize_gcd, div->el[0]); + + if (isl_int_is_one(ctx->normalize_gcd)) + return; + + isl_int_divexact(div->el[0], div->el[0], ctx->normalize_gcd); + isl_int_fdiv_q(div->el[1], div->el[1], ctx->normalize_gcd); + isl_seq_scale_down(div->el + 2, div->el + 2, ctx->normalize_gcd, len); +} + +/* Return a integer division for use in a parametric cut based on the given row. + * In particular, let the parametric constant of the row be + * + * \sum_i a_i y_i + * + * where y_0 = 1, but none of the y_i corresponds to the big parameter M. + * The div returned is equal to + * + * floor(\sum_i {-a_i} y_i) = floor((\sum_i (-a_i mod d) y_i)/d) + */ +static struct isl_vec *get_row_parameter_div(struct isl_tab *tab, int row) +{ + struct isl_vec *div; + + div = isl_vec_alloc(tab->mat->ctx, 1 + 1 + tab->n_param + tab->n_div); + if (!div) + return NULL; + + isl_int_set(div->el[0], tab->mat->row[row][0]); + get_row_parameter_line(tab, row, div->el + 1); + isl_seq_neg(div->el + 1, div->el + 1, div->size - 1); + normalize_div(div); + isl_seq_fdiv_r(div->el + 1, div->el + 1, div->el[0], div->size - 1); + + return div; +} + +/* Return a integer division for use in transferring an integrality constraint + * to the context. + * In particular, let the parametric constant of the row be + * + * \sum_i a_i y_i + * + * where y_0 = 1, but none of the y_i corresponds to the big parameter M. + * The the returned div is equal to + * + * floor(\sum_i {a_i} y_i) = floor((\sum_i (a_i mod d) y_i)/d) + */ +static struct isl_vec *get_row_split_div(struct isl_tab *tab, int row) +{ + struct isl_vec *div; + + div = isl_vec_alloc(tab->mat->ctx, 1 + 1 + tab->n_param + tab->n_div); + if (!div) + return NULL; + + isl_int_set(div->el[0], tab->mat->row[row][0]); + get_row_parameter_line(tab, row, div->el + 1); + normalize_div(div); + isl_seq_fdiv_r(div->el + 1, div->el + 1, div->el[0], div->size - 1); + + return div; +} + +/* Construct and return an inequality that expresses an upper bound + * on the given div. + * In particular, if the div is given by + * + * d = floor(e/m) + * + * then the inequality expresses + * + * m d <= e + */ +static struct isl_vec *ineq_for_div(struct isl_basic_set *bset, unsigned div) +{ + unsigned total; + unsigned div_pos; + struct isl_vec *ineq; + + if (!bset) + return NULL; + + total = isl_basic_set_total_dim(bset); + div_pos = 1 + total - bset->n_div + div; + + ineq = isl_vec_alloc(bset->ctx, 1 + total); + if (!ineq) + return NULL; + + isl_seq_cpy(ineq->el, bset->div[div] + 1, 1 + total); + isl_int_neg(ineq->el[div_pos], bset->div[div][0]); + return ineq; +} + +/* Given a row in the tableau and a div that was created + * using get_row_split_div and that has been constrained to equality, i.e., + * + * d = floor(\sum_i {a_i} y_i) = \sum_i {a_i} y_i + * + * replace the expression "\sum_i {a_i} y_i" in the row by d, + * i.e., we subtract "\sum_i {a_i} y_i" and add 1 d. + * The coefficients of the non-parameters in the tableau have been + * verified to be integral. We can therefore simply replace coefficient b + * by floor(b). For the coefficients of the parameters we have + * floor(a_i) = a_i - {a_i}, while for the other coefficients, we have + * floor(b) = b. + */ +static struct isl_tab *set_row_cst_to_div(struct isl_tab *tab, int row, int div) +{ + isl_seq_fdiv_q(tab->mat->row[row] + 1, tab->mat->row[row] + 1, + tab->mat->row[row][0], 1 + tab->M + tab->n_col); + + isl_int_set_si(tab->mat->row[row][0], 1); + + if (tab->var[tab->n_var - tab->n_div + div].is_row) { + int drow = tab->var[tab->n_var - tab->n_div + div].index; + + isl_assert(tab->mat->ctx, + isl_int_is_one(tab->mat->row[drow][0]), goto error); + isl_seq_combine(tab->mat->row[row] + 1, + tab->mat->ctx->one, tab->mat->row[row] + 1, + tab->mat->ctx->one, tab->mat->row[drow] + 1, + 1 + tab->M + tab->n_col); + } else { + int dcol = tab->var[tab->n_var - tab->n_div + div].index; + + isl_int_add_ui(tab->mat->row[row][2 + tab->M + dcol], + tab->mat->row[row][2 + tab->M + dcol], 1); + } + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Check if the (parametric) constant of the given row is obviously + * negative, meaning that we don't need to consult the context tableau. + * If there is a big parameter and its coefficient is non-zero, + * then this coefficient determines the outcome. + * Otherwise, we check whether the constant is negative and + * all non-zero coefficients of parameters are negative and + * belong to non-negative parameters. + */ +static int is_obviously_neg(struct isl_tab *tab, int row) +{ + int i; + int col; + unsigned off = 2 + tab->M; + + if (tab->M) { + if (isl_int_is_pos(tab->mat->row[row][2])) + return 0; + if (isl_int_is_neg(tab->mat->row[row][2])) + return 1; + } + + if (isl_int_is_nonneg(tab->mat->row[row][1])) + return 0; + for (i = 0; i < tab->n_param; ++i) { + /* Eliminated parameter */ + if (tab->var[i].is_row) + continue; + col = tab->var[i].index; + if (isl_int_is_zero(tab->mat->row[row][off + col])) + continue; + if (!tab->var[i].is_nonneg) + return 0; + if (isl_int_is_pos(tab->mat->row[row][off + col])) + return 0; + } + for (i = 0; i < tab->n_div; ++i) { + if (tab->var[tab->n_var - tab->n_div + i].is_row) + continue; + col = tab->var[tab->n_var - tab->n_div + i].index; + if (isl_int_is_zero(tab->mat->row[row][off + col])) + continue; + if (!tab->var[tab->n_var - tab->n_div + i].is_nonneg) + return 0; + if (isl_int_is_pos(tab->mat->row[row][off + col])) + return 0; + } + return 1; +} + +/* Check if the (parametric) constant of the given row is obviously + * non-negative, meaning that we don't need to consult the context tableau. + * If there is a big parameter and its coefficient is non-zero, + * then this coefficient determines the outcome. + * Otherwise, we check whether the constant is non-negative and + * all non-zero coefficients of parameters are positive and + * belong to non-negative parameters. + */ +static int is_obviously_nonneg(struct isl_tab *tab, int row) +{ + int i; + int col; + unsigned off = 2 + tab->M; + + if (tab->M) { + if (isl_int_is_pos(tab->mat->row[row][2])) + return 1; + if (isl_int_is_neg(tab->mat->row[row][2])) + return 0; + } + + if (isl_int_is_neg(tab->mat->row[row][1])) + return 0; + for (i = 0; i < tab->n_param; ++i) { + /* Eliminated parameter */ + if (tab->var[i].is_row) + continue; + col = tab->var[i].index; + if (isl_int_is_zero(tab->mat->row[row][off + col])) + continue; + if (!tab->var[i].is_nonneg) + return 0; + if (isl_int_is_neg(tab->mat->row[row][off + col])) + return 0; + } + for (i = 0; i < tab->n_div; ++i) { + if (tab->var[tab->n_var - tab->n_div + i].is_row) + continue; + col = tab->var[tab->n_var - tab->n_div + i].index; + if (isl_int_is_zero(tab->mat->row[row][off + col])) + continue; + if (!tab->var[tab->n_var - tab->n_div + i].is_nonneg) + return 0; + if (isl_int_is_neg(tab->mat->row[row][off + col])) + return 0; + } + return 1; +} + +/* Given a row r and two columns, return the column that would + * lead to the lexicographically smallest increment in the sample + * solution when leaving the basis in favor of the row. + * Pivoting with column c will increment the sample value by a non-negative + * constant times a_{V,c}/a_{r,c}, with a_{V,c} the elements of column c + * corresponding to the non-parametric variables. + * If variable v appears in a column c_v, the a_{v,c} = 1 iff c = c_v, + * with all other entries in this virtual row equal to zero. + * If variable v appears in a row, then a_{v,c} is the element in column c + * of that row. + * + * Let v be the first variable with a_{v,c1}/a_{r,c1} != a_{v,c2}/a_{r,c2}. + * Then if a_{v,c1}/a_{r,c1} < a_{v,c2}/a_{r,c2}, i.e., + * a_{v,c2} a_{r,c1} - a_{v,c1} a_{r,c2} > 0, c1 results in the minimal + * increment. Otherwise, it's c2. + */ +static int lexmin_col_pair(struct isl_tab *tab, + int row, int col1, int col2, isl_int tmp) +{ + int i; + isl_int *tr; + + tr = tab->mat->row[row] + 2 + tab->M; + + for (i = tab->n_param; i < tab->n_var - tab->n_div; ++i) { + int s1, s2; + isl_int *r; + + if (!tab->var[i].is_row) { + if (tab->var[i].index == col1) + return col2; + if (tab->var[i].index == col2) + return col1; + continue; + } + + if (tab->var[i].index == row) + continue; + + r = tab->mat->row[tab->var[i].index] + 2 + tab->M; + s1 = isl_int_sgn(r[col1]); + s2 = isl_int_sgn(r[col2]); + if (s1 == 0 && s2 == 0) + continue; + if (s1 < s2) + return col1; + if (s2 < s1) + return col2; + + isl_int_mul(tmp, r[col2], tr[col1]); + isl_int_submul(tmp, r[col1], tr[col2]); + if (isl_int_is_pos(tmp)) + return col1; + if (isl_int_is_neg(tmp)) + return col2; + } + return -1; +} + +/* Given a row in the tableau, find and return the column that would + * result in the lexicographically smallest, but positive, increment + * in the sample point. + * If there is no such column, then return tab->n_col. + * If anything goes wrong, return -1. + */ +static int lexmin_pivot_col(struct isl_tab *tab, int row) +{ + int j; + int col = tab->n_col; + isl_int *tr; + isl_int tmp; + + tr = tab->mat->row[row] + 2 + tab->M; + + isl_int_init(tmp); + + for (j = tab->n_dead; j < tab->n_col; ++j) { + if (tab->col_var[j] >= 0 && + (tab->col_var[j] < tab->n_param || + tab->col_var[j] >= tab->n_var - tab->n_div)) + continue; + + if (!isl_int_is_pos(tr[j])) + continue; + + if (col == tab->n_col) + col = j; + else + col = lexmin_col_pair(tab, row, col, j, tmp); + isl_assert(tab->mat->ctx, col >= 0, goto error); + } + + isl_int_clear(tmp); + return col; +error: + isl_int_clear(tmp); + return -1; +} + +/* Return the first known violated constraint, i.e., a non-negative + * constraint that currently has an either obviously negative value + * or a previously determined to be negative value. + * + * If any constraint has a negative coefficient for the big parameter, + * if any, then we return one of these first. + */ +static int first_neg(struct isl_tab *tab) +{ + int row; + + if (tab->M) + for (row = tab->n_redundant; row < tab->n_row; ++row) { + if (!isl_tab_var_from_row(tab, row)->is_nonneg) + continue; + if (!isl_int_is_neg(tab->mat->row[row][2])) + continue; + if (tab->row_sign) + tab->row_sign[row] = isl_tab_row_neg; + return row; + } + for (row = tab->n_redundant; row < tab->n_row; ++row) { + if (!isl_tab_var_from_row(tab, row)->is_nonneg) + continue; + if (tab->row_sign) { + if (tab->row_sign[row] == 0 && + is_obviously_neg(tab, row)) + tab->row_sign[row] = isl_tab_row_neg; + if (tab->row_sign[row] != isl_tab_row_neg) + continue; + } else if (!is_obviously_neg(tab, row)) + continue; + return row; + } + return -1; +} + +/* Check whether the invariant that all columns are lexico-positive + * is satisfied. This function is not called from the current code + * but is useful during debugging. + */ +static void check_lexpos(struct isl_tab *tab) __attribute__ ((unused)); +static void check_lexpos(struct isl_tab *tab) +{ + unsigned off = 2 + tab->M; + int col; + int var; + int row; + + for (col = tab->n_dead; col < tab->n_col; ++col) { + if (tab->col_var[col] >= 0 && + (tab->col_var[col] < tab->n_param || + tab->col_var[col] >= tab->n_var - tab->n_div)) + continue; + for (var = tab->n_param; var < tab->n_var - tab->n_div; ++var) { + if (!tab->var[var].is_row) { + if (tab->var[var].index == col) + break; + else + continue; + } + row = tab->var[var].index; + if (isl_int_is_zero(tab->mat->row[row][off + col])) + continue; + if (isl_int_is_pos(tab->mat->row[row][off + col])) + break; + fprintf(stderr, "lexneg column %d (row %d)\n", + col, row); + } + if (var >= tab->n_var - tab->n_div) + fprintf(stderr, "zero column %d\n", col); + } +} + +/* Report to the caller that the given constraint is part of an encountered + * conflict. + */ +static int report_conflicting_constraint(struct isl_tab *tab, int con) +{ + return tab->conflict(con, tab->conflict_user); +} + +/* Given a conflicting row in the tableau, report all constraints + * involved in the row to the caller. That is, the row itself + * (if it represents a constraint) and all constraint columns with + * non-zero (and therefore negative) coefficients. + */ +static int report_conflict(struct isl_tab *tab, int row) +{ + int j; + isl_int *tr; + + if (!tab->conflict) + return 0; + + if (tab->row_var[row] < 0 && + report_conflicting_constraint(tab, ~tab->row_var[row]) < 0) + return -1; + + tr = tab->mat->row[row] + 2 + tab->M; + + for (j = tab->n_dead; j < tab->n_col; ++j) { + if (tab->col_var[j] >= 0 && + (tab->col_var[j] < tab->n_param || + tab->col_var[j] >= tab->n_var - tab->n_div)) + continue; + + if (!isl_int_is_neg(tr[j])) + continue; + + if (tab->col_var[j] < 0 && + report_conflicting_constraint(tab, ~tab->col_var[j]) < 0) + return -1; + } + + return 0; +} + +/* Resolve all known or obviously violated constraints through pivoting. + * In particular, as long as we can find any violated constraint, we + * look for a pivoting column that would result in the lexicographically + * smallest increment in the sample point. If there is no such column + * then the tableau is infeasible. + */ +static int restore_lexmin(struct isl_tab *tab) WARN_UNUSED; +static int restore_lexmin(struct isl_tab *tab) +{ + int row, col; + + if (!tab) + return -1; + if (tab->empty) + return 0; + while ((row = first_neg(tab)) != -1) { + col = lexmin_pivot_col(tab, row); + if (col >= tab->n_col) { + if (report_conflict(tab, row) < 0) + return -1; + if (isl_tab_mark_empty(tab) < 0) + return -1; + return 0; + } + if (col < 0) + return -1; + if (isl_tab_pivot(tab, row, col) < 0) + return -1; + } + return 0; +} + +/* Given a row that represents an equality, look for an appropriate + * pivoting column. + * In particular, if there are any non-zero coefficients among + * the non-parameter variables, then we take the last of these + * variables. Eliminating this variable in terms of the other + * variables and/or parameters does not influence the property + * that all column in the initial tableau are lexicographically + * positive. The row corresponding to the eliminated variable + * will only have non-zero entries below the diagonal of the + * initial tableau. That is, we transform + * + * I I + * 1 into a + * I I + * + * If there is no such non-parameter variable, then we are dealing with + * pure parameter equality and we pick any parameter with coefficient 1 or -1 + * for elimination. This will ensure that the eliminated parameter + * always has an integer value whenever all the other parameters are integral. + * If there is no such parameter then we return -1. + */ +static int last_var_col_or_int_par_col(struct isl_tab *tab, int row) +{ + unsigned off = 2 + tab->M; + int i; + + for (i = tab->n_var - tab->n_div - 1; i >= 0 && i >= tab->n_param; --i) { + int col; + if (tab->var[i].is_row) + continue; + col = tab->var[i].index; + if (col <= tab->n_dead) + continue; + if (!isl_int_is_zero(tab->mat->row[row][off + col])) + return col; + } + for (i = tab->n_dead; i < tab->n_col; ++i) { + if (isl_int_is_one(tab->mat->row[row][off + i])) + return i; + if (isl_int_is_negone(tab->mat->row[row][off + i])) + return i; + } + return -1; +} + +/* Add an equality that is known to be valid to the tableau. + * We first check if we can eliminate a variable or a parameter. + * If not, we add the equality as two inequalities. + * In this case, the equality was a pure parameter equality and there + * is no need to resolve any constraint violations. + * + * This function assumes that at least two more rows and at least + * two more elements in the constraint array are available in the tableau. + */ +static struct isl_tab *add_lexmin_valid_eq(struct isl_tab *tab, isl_int *eq) +{ + int i; + int r; + + if (!tab) + return NULL; + r = isl_tab_add_row(tab, eq); + if (r < 0) + goto error; + + r = tab->con[r].index; + i = last_var_col_or_int_par_col(tab, r); + if (i < 0) { + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + goto error; + isl_seq_neg(eq, eq, 1 + tab->n_var); + r = isl_tab_add_row(tab, eq); + if (r < 0) + goto error; + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + goto error; + } else { + if (isl_tab_pivot(tab, r, i) < 0) + goto error; + if (isl_tab_kill_col(tab, i) < 0) + goto error; + tab->n_eq++; + } + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Check if the given row is a pure constant. + */ +static int is_constant(struct isl_tab *tab, int row) +{ + unsigned off = 2 + tab->M; + + return isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead, + tab->n_col - tab->n_dead) == -1; +} + +/* Add an equality that may or may not be valid to the tableau. + * If the resulting row is a pure constant, then it must be zero. + * Otherwise, the resulting tableau is empty. + * + * If the row is not a pure constant, then we add two inequalities, + * each time checking that they can be satisfied. + * In the end we try to use one of the two constraints to eliminate + * a column. + * + * This function assumes that at least two more rows and at least + * two more elements in the constraint array are available in the tableau. + */ +static int add_lexmin_eq(struct isl_tab *tab, isl_int *eq) WARN_UNUSED; +static int add_lexmin_eq(struct isl_tab *tab, isl_int *eq) +{ + int r1, r2; + int row; + struct isl_tab_undo *snap; + + if (!tab) + return -1; + snap = isl_tab_snap(tab); + r1 = isl_tab_add_row(tab, eq); + if (r1 < 0) + return -1; + tab->con[r1].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r1]) < 0) + return -1; + + row = tab->con[r1].index; + if (is_constant(tab, row)) { + if (!isl_int_is_zero(tab->mat->row[row][1]) || + (tab->M && !isl_int_is_zero(tab->mat->row[row][2]))) { + if (isl_tab_mark_empty(tab) < 0) + return -1; + return 0; + } + if (isl_tab_rollback(tab, snap) < 0) + return -1; + return 0; + } + + if (restore_lexmin(tab) < 0) + return -1; + if (tab->empty) + return 0; + + isl_seq_neg(eq, eq, 1 + tab->n_var); + + r2 = isl_tab_add_row(tab, eq); + if (r2 < 0) + return -1; + tab->con[r2].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r2]) < 0) + return -1; + + if (restore_lexmin(tab) < 0) + return -1; + if (tab->empty) + return 0; + + if (!tab->con[r1].is_row) { + if (isl_tab_kill_col(tab, tab->con[r1].index) < 0) + return -1; + } else if (!tab->con[r2].is_row) { + if (isl_tab_kill_col(tab, tab->con[r2].index) < 0) + return -1; + } + + if (tab->bmap) { + tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq); + if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0) + return -1; + isl_seq_neg(eq, eq, 1 + tab->n_var); + tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq); + isl_seq_neg(eq, eq, 1 + tab->n_var); + if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0) + return -1; + if (!tab->bmap) + return -1; + } + + return 0; +} + +/* Add an inequality to the tableau, resolving violations using + * restore_lexmin. + * + * This function assumes that at least one more row and at least + * one more element in the constraint array are available in the tableau. + */ +static struct isl_tab *add_lexmin_ineq(struct isl_tab *tab, isl_int *ineq) +{ + int r; + + if (!tab) + return NULL; + if (tab->bmap) { + tab->bmap = isl_basic_map_add_ineq(tab->bmap, ineq); + if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0) + goto error; + if (!tab->bmap) + goto error; + } + r = isl_tab_add_row(tab, ineq); + if (r < 0) + goto error; + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + goto error; + if (isl_tab_row_is_redundant(tab, tab->con[r].index)) { + if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0) + goto error; + return tab; + } + + if (restore_lexmin(tab) < 0) + goto error; + if (!tab->empty && tab->con[r].is_row && + isl_tab_row_is_redundant(tab, tab->con[r].index)) + if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0) + goto error; + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Check if the coefficients of the parameters are all integral. + */ +static int integer_parameter(struct isl_tab *tab, int row) +{ + int i; + int col; + unsigned off = 2 + tab->M; + + for (i = 0; i < tab->n_param; ++i) { + /* Eliminated parameter */ + if (tab->var[i].is_row) + continue; + col = tab->var[i].index; + if (!isl_int_is_divisible_by(tab->mat->row[row][off + col], + tab->mat->row[row][0])) + return 0; + } + for (i = 0; i < tab->n_div; ++i) { + if (tab->var[tab->n_var - tab->n_div + i].is_row) + continue; + col = tab->var[tab->n_var - tab->n_div + i].index; + if (!isl_int_is_divisible_by(tab->mat->row[row][off + col], + tab->mat->row[row][0])) + return 0; + } + return 1; +} + +/* Check if the coefficients of the non-parameter variables are all integral. + */ +static int integer_variable(struct isl_tab *tab, int row) +{ + int i; + unsigned off = 2 + tab->M; + + for (i = tab->n_dead; i < tab->n_col; ++i) { + if (tab->col_var[i] >= 0 && + (tab->col_var[i] < tab->n_param || + tab->col_var[i] >= tab->n_var - tab->n_div)) + continue; + if (!isl_int_is_divisible_by(tab->mat->row[row][off + i], + tab->mat->row[row][0])) + return 0; + } + return 1; +} + +/* Check if the constant term is integral. + */ +static int integer_constant(struct isl_tab *tab, int row) +{ + return isl_int_is_divisible_by(tab->mat->row[row][1], + tab->mat->row[row][0]); +} + +#define I_CST 1 << 0 +#define I_PAR 1 << 1 +#define I_VAR 1 << 2 + +/* Check for next (non-parameter) variable after "var" (first if var == -1) + * that is non-integer and therefore requires a cut and return + * the index of the variable. + * For parametric tableaus, there are three parts in a row, + * the constant, the coefficients of the parameters and the rest. + * For each part, we check whether the coefficients in that part + * are all integral and if so, set the corresponding flag in *f. + * If the constant and the parameter part are integral, then the + * current sample value is integral and no cut is required + * (irrespective of whether the variable part is integral). + */ +static int next_non_integer_var(struct isl_tab *tab, int var, int *f) +{ + var = var < 0 ? tab->n_param : var + 1; + + for (; var < tab->n_var - tab->n_div; ++var) { + int flags = 0; + int row; + if (!tab->var[var].is_row) + continue; + row = tab->var[var].index; + if (integer_constant(tab, row)) + ISL_FL_SET(flags, I_CST); + if (integer_parameter(tab, row)) + ISL_FL_SET(flags, I_PAR); + if (ISL_FL_ISSET(flags, I_CST) && ISL_FL_ISSET(flags, I_PAR)) + continue; + if (integer_variable(tab, row)) + ISL_FL_SET(flags, I_VAR); + *f = flags; + return var; + } + return -1; +} + +/* Check for first (non-parameter) variable that is non-integer and + * therefore requires a cut and return the corresponding row. + * For parametric tableaus, there are three parts in a row, + * the constant, the coefficients of the parameters and the rest. + * For each part, we check whether the coefficients in that part + * are all integral and if so, set the corresponding flag in *f. + * If the constant and the parameter part are integral, then the + * current sample value is integral and no cut is required + * (irrespective of whether the variable part is integral). + */ +static int first_non_integer_row(struct isl_tab *tab, int *f) +{ + int var = next_non_integer_var(tab, -1, f); + + return var < 0 ? -1 : tab->var[var].index; +} + +/* Add a (non-parametric) cut to cut away the non-integral sample + * value of the given row. + * + * If the row is given by + * + * m r = f + \sum_i a_i y_i + * + * then the cut is + * + * c = - {-f/m} + \sum_i {a_i/m} y_i >= 0 + * + * The big parameter, if any, is ignored, since it is assumed to be big + * enough to be divisible by any integer. + * If the tableau is actually a parametric tableau, then this function + * is only called when all coefficients of the parameters are integral. + * The cut therefore has zero coefficients for the parameters. + * + * The current value is known to be negative, so row_sign, if it + * exists, is set accordingly. + * + * Return the row of the cut or -1. + */ +static int add_cut(struct isl_tab *tab, int row) +{ + int i; + int r; + isl_int *r_row; + unsigned off = 2 + tab->M; + + if (isl_tab_extend_cons(tab, 1) < 0) + return -1; + r = isl_tab_allocate_con(tab); + if (r < 0) + return -1; + + r_row = tab->mat->row[tab->con[r].index]; + isl_int_set(r_row[0], tab->mat->row[row][0]); + isl_int_neg(r_row[1], tab->mat->row[row][1]); + isl_int_fdiv_r(r_row[1], r_row[1], tab->mat->row[row][0]); + isl_int_neg(r_row[1], r_row[1]); + if (tab->M) + isl_int_set_si(r_row[2], 0); + for (i = 0; i < tab->n_col; ++i) + isl_int_fdiv_r(r_row[off + i], + tab->mat->row[row][off + i], tab->mat->row[row][0]); + + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + return -1; + if (tab->row_sign) + tab->row_sign[tab->con[r].index] = isl_tab_row_neg; + + return tab->con[r].index; +} + +#define CUT_ALL 1 +#define CUT_ONE 0 + +/* Given a non-parametric tableau, add cuts until an integer + * sample point is obtained or until the tableau is determined + * to be integer infeasible. + * As long as there is any non-integer value in the sample point, + * we add appropriate cuts, if possible, for each of these + * non-integer values and then resolve the violated + * cut constraints using restore_lexmin. + * If one of the corresponding rows is equal to an integral + * combination of variables/constraints plus a non-integral constant, + * then there is no way to obtain an integer point and we return + * a tableau that is marked empty. + * The parameter cutting_strategy controls the strategy used when adding cuts + * to remove non-integer points. CUT_ALL adds all possible cuts + * before continuing the search. CUT_ONE adds only one cut at a time. + */ +static struct isl_tab *cut_to_integer_lexmin(struct isl_tab *tab, + int cutting_strategy) +{ + int var; + int row; + int flags; + + if (!tab) + return NULL; + if (tab->empty) + return tab; + + while ((var = next_non_integer_var(tab, -1, &flags)) != -1) { + do { + if (ISL_FL_ISSET(flags, I_VAR)) { + if (isl_tab_mark_empty(tab) < 0) + goto error; + return tab; + } + row = tab->var[var].index; + row = add_cut(tab, row); + if (row < 0) + goto error; + if (cutting_strategy == CUT_ONE) + break; + } while ((var = next_non_integer_var(tab, var, &flags)) != -1); + if (restore_lexmin(tab) < 0) + goto error; + if (tab->empty) + break; + } + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Check whether all the currently active samples also satisfy the inequality + * "ineq" (treated as an equality if eq is set). + * Remove those samples that do not. + */ +static struct isl_tab *check_samples(struct isl_tab *tab, isl_int *ineq, int eq) +{ + int i; + isl_int v; + + if (!tab) + return NULL; + + isl_assert(tab->mat->ctx, tab->bmap, goto error); + isl_assert(tab->mat->ctx, tab->samples, goto error); + isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, goto error); + + isl_int_init(v); + for (i = tab->n_outside; i < tab->n_sample; ++i) { + int sgn; + isl_seq_inner_product(ineq, tab->samples->row[i], + 1 + tab->n_var, &v); + sgn = isl_int_sgn(v); + if (eq ? (sgn == 0) : (sgn >= 0)) + continue; + tab = isl_tab_drop_sample(tab, i); + if (!tab) + break; + } + isl_int_clear(v); + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Check whether the sample value of the tableau is finite, + * i.e., either the tableau does not use a big parameter, or + * all values of the variables are equal to the big parameter plus + * some constant. This constant is the actual sample value. + */ +static int sample_is_finite(struct isl_tab *tab) +{ + int i; + + if (!tab->M) + return 1; + + for (i = 0; i < tab->n_var; ++i) { + int row; + if (!tab->var[i].is_row) + return 0; + row = tab->var[i].index; + if (isl_int_ne(tab->mat->row[row][0], tab->mat->row[row][2])) + return 0; + } + return 1; +} + +/* Check if the context tableau of sol has any integer points. + * Leave tab in empty state if no integer point can be found. + * If an integer point can be found and if moreover it is finite, + * then it is added to the list of sample values. + * + * This function is only called when none of the currently active sample + * values satisfies the most recently added constraint. + */ +static struct isl_tab *check_integer_feasible(struct isl_tab *tab) +{ + struct isl_tab_undo *snap; + + if (!tab) + return NULL; + + snap = isl_tab_snap(tab); + if (isl_tab_push_basis(tab) < 0) + goto error; + + tab = cut_to_integer_lexmin(tab, CUT_ALL); + if (!tab) + goto error; + + if (!tab->empty && sample_is_finite(tab)) { + struct isl_vec *sample; + + sample = isl_tab_get_sample_value(tab); + + if (isl_tab_add_sample(tab, sample) < 0) + goto error; + } + + if (!tab->empty && isl_tab_rollback(tab, snap) < 0) + goto error; + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Check if any of the currently active sample values satisfies + * the inequality "ineq" (an equality if eq is set). + */ +static int tab_has_valid_sample(struct isl_tab *tab, isl_int *ineq, int eq) +{ + int i; + isl_int v; + + if (!tab) + return -1; + + isl_assert(tab->mat->ctx, tab->bmap, return -1); + isl_assert(tab->mat->ctx, tab->samples, return -1); + isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, return -1); + + isl_int_init(v); + for (i = tab->n_outside; i < tab->n_sample; ++i) { + int sgn; + isl_seq_inner_product(ineq, tab->samples->row[i], + 1 + tab->n_var, &v); + sgn = isl_int_sgn(v); + if (eq ? (sgn == 0) : (sgn >= 0)) + break; + } + isl_int_clear(v); + + return i < tab->n_sample; +} + +/* Add a div specified by "div" to the tableau "tab" and return + * 1 if the div is obviously non-negative. + */ +static int context_tab_add_div(struct isl_tab *tab, struct isl_vec *div, + int (*add_ineq)(void *user, isl_int *), void *user) +{ + int i; + int r; + struct isl_mat *samples; + int nonneg; + + r = isl_tab_add_div(tab, div, add_ineq, user); + if (r < 0) + return -1; + nonneg = tab->var[r].is_nonneg; + tab->var[r].frozen = 1; + + samples = isl_mat_extend(tab->samples, + tab->n_sample, 1 + tab->n_var); + tab->samples = samples; + if (!samples) + return -1; + for (i = tab->n_outside; i < samples->n_row; ++i) { + isl_seq_inner_product(div->el + 1, samples->row[i], + div->size - 1, &samples->row[i][samples->n_col - 1]); + isl_int_fdiv_q(samples->row[i][samples->n_col - 1], + samples->row[i][samples->n_col - 1], div->el[0]); + } + + return nonneg; +} + +/* Add a div specified by "div" to both the main tableau and + * the context tableau. In case of the main tableau, we only + * need to add an extra div. In the context tableau, we also + * need to express the meaning of the div. + * Return the index of the div or -1 if anything went wrong. + */ +static int add_div(struct isl_tab *tab, struct isl_context *context, + struct isl_vec *div) +{ + int r; + int nonneg; + + if ((nonneg = context->op->add_div(context, div)) < 0) + goto error; + + if (!context->op->is_ok(context)) + goto error; + + if (isl_tab_extend_vars(tab, 1) < 0) + goto error; + r = isl_tab_allocate_var(tab); + if (r < 0) + goto error; + if (nonneg) + tab->var[r].is_nonneg = 1; + tab->var[r].frozen = 1; + tab->n_div++; + + return tab->n_div - 1; +error: + context->op->invalidate(context); + return -1; +} + +static int find_div(struct isl_tab *tab, isl_int *div, isl_int denom) +{ + int i; + unsigned total = isl_basic_map_total_dim(tab->bmap); + + for (i = 0; i < tab->bmap->n_div; ++i) { + if (isl_int_ne(tab->bmap->div[i][0], denom)) + continue; + if (!isl_seq_eq(tab->bmap->div[i] + 1, div, 1 + total)) + continue; + return i; + } + return -1; +} + +/* Return the index of a div that corresponds to "div". + * We first check if we already have such a div and if not, we create one. + */ +static int get_div(struct isl_tab *tab, struct isl_context *context, + struct isl_vec *div) +{ + int d; + struct isl_tab *context_tab = context->op->peek_tab(context); + + if (!context_tab) + return -1; + + d = find_div(context_tab, div->el + 1, div->el[0]); + if (d != -1) + return d; + + return add_div(tab, context, div); +} + +/* Add a parametric cut to cut away the non-integral sample value + * of the give row. + * Let a_i be the coefficients of the constant term and the parameters + * and let b_i be the coefficients of the variables or constraints + * in basis of the tableau. + * Let q be the div q = floor(\sum_i {-a_i} y_i). + * + * The cut is expressed as + * + * c = \sum_i -{-a_i} y_i + \sum_i {b_i} x_i + q >= 0 + * + * If q did not already exist in the context tableau, then it is added first. + * If q is in a column of the main tableau then the "+ q" can be accomplished + * by setting the corresponding entry to the denominator of the constraint. + * If q happens to be in a row of the main tableau, then the corresponding + * row needs to be added instead (taking care of the denominators). + * Note that this is very unlikely, but perhaps not entirely impossible. + * + * The current value of the cut is known to be negative (or at least + * non-positive), so row_sign is set accordingly. + * + * Return the row of the cut or -1. + */ +static int add_parametric_cut(struct isl_tab *tab, int row, + struct isl_context *context) +{ + struct isl_vec *div; + int d; + int i; + int r; + isl_int *r_row; + int col; + int n; + unsigned off = 2 + tab->M; + + if (!context) + return -1; + + div = get_row_parameter_div(tab, row); + if (!div) + return -1; + + n = tab->n_div; + d = context->op->get_div(context, tab, div); + isl_vec_free(div); + if (d < 0) + return -1; + + if (isl_tab_extend_cons(tab, 1) < 0) + return -1; + r = isl_tab_allocate_con(tab); + if (r < 0) + return -1; + + r_row = tab->mat->row[tab->con[r].index]; + isl_int_set(r_row[0], tab->mat->row[row][0]); + isl_int_neg(r_row[1], tab->mat->row[row][1]); + isl_int_fdiv_r(r_row[1], r_row[1], tab->mat->row[row][0]); + isl_int_neg(r_row[1], r_row[1]); + if (tab->M) + isl_int_set_si(r_row[2], 0); + for (i = 0; i < tab->n_param; ++i) { + if (tab->var[i].is_row) + continue; + col = tab->var[i].index; + isl_int_neg(r_row[off + col], tab->mat->row[row][off + col]); + isl_int_fdiv_r(r_row[off + col], r_row[off + col], + tab->mat->row[row][0]); + isl_int_neg(r_row[off + col], r_row[off + col]); + } + for (i = 0; i < tab->n_div; ++i) { + if (tab->var[tab->n_var - tab->n_div + i].is_row) + continue; + col = tab->var[tab->n_var - tab->n_div + i].index; + isl_int_neg(r_row[off + col], tab->mat->row[row][off + col]); + isl_int_fdiv_r(r_row[off + col], r_row[off + col], + tab->mat->row[row][0]); + isl_int_neg(r_row[off + col], r_row[off + col]); + } + for (i = 0; i < tab->n_col; ++i) { + if (tab->col_var[i] >= 0 && + (tab->col_var[i] < tab->n_param || + tab->col_var[i] >= tab->n_var - tab->n_div)) + continue; + isl_int_fdiv_r(r_row[off + i], + tab->mat->row[row][off + i], tab->mat->row[row][0]); + } + if (tab->var[tab->n_var - tab->n_div + d].is_row) { + isl_int gcd; + int d_row = tab->var[tab->n_var - tab->n_div + d].index; + isl_int_init(gcd); + isl_int_gcd(gcd, tab->mat->row[d_row][0], r_row[0]); + isl_int_divexact(r_row[0], r_row[0], gcd); + isl_int_divexact(gcd, tab->mat->row[d_row][0], gcd); + isl_seq_combine(r_row + 1, gcd, r_row + 1, + r_row[0], tab->mat->row[d_row] + 1, + off - 1 + tab->n_col); + isl_int_mul(r_row[0], r_row[0], tab->mat->row[d_row][0]); + isl_int_clear(gcd); + } else { + col = tab->var[tab->n_var - tab->n_div + d].index; + isl_int_set(r_row[off + col], tab->mat->row[row][0]); + } + + tab->con[r].is_nonneg = 1; + if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0) + return -1; + if (tab->row_sign) + tab->row_sign[tab->con[r].index] = isl_tab_row_neg; + + row = tab->con[r].index; + + if (d >= n && context->op->detect_equalities(context, tab) < 0) + return -1; + + return row; +} + +/* Construct a tableau for bmap that can be used for computing + * the lexicographic minimum (or maximum) of bmap. + * If not NULL, then dom is the domain where the minimum + * should be computed. In this case, we set up a parametric + * tableau with row signs (initialized to "unknown"). + * If M is set, then the tableau will use a big parameter. + * If max is set, then a maximum should be computed instead of a minimum. + * This means that for each variable x, the tableau will contain the variable + * x' = M - x, rather than x' = M + x. This in turn means that the coefficient + * of the variables in all constraints are negated prior to adding them + * to the tableau. + */ +static struct isl_tab *tab_for_lexmin(struct isl_basic_map *bmap, + struct isl_basic_set *dom, unsigned M, int max) +{ + int i; + struct isl_tab *tab; + unsigned n_var; + unsigned o_var; + + tab = isl_tab_alloc(bmap->ctx, 2 * bmap->n_eq + bmap->n_ineq + 1, + isl_basic_map_total_dim(bmap), M); + if (!tab) + return NULL; + + tab->rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + if (dom) { + tab->n_param = isl_basic_set_total_dim(dom) - dom->n_div; + tab->n_div = dom->n_div; + tab->row_sign = isl_calloc_array(bmap->ctx, + enum isl_tab_row_sign, tab->mat->n_row); + if (tab->mat->n_row && !tab->row_sign) + goto error; + } + if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) { + if (isl_tab_mark_empty(tab) < 0) + goto error; + return tab; + } + + for (i = tab->n_param; i < tab->n_var - tab->n_div; ++i) { + tab->var[i].is_nonneg = 1; + tab->var[i].frozen = 1; + } + o_var = 1 + tab->n_param; + n_var = tab->n_var - tab->n_param - tab->n_div; + for (i = 0; i < bmap->n_eq; ++i) { + if (max) + isl_seq_neg(bmap->eq[i] + o_var, + bmap->eq[i] + o_var, n_var); + tab = add_lexmin_valid_eq(tab, bmap->eq[i]); + if (max) + isl_seq_neg(bmap->eq[i] + o_var, + bmap->eq[i] + o_var, n_var); + if (!tab || tab->empty) + return tab; + } + if (bmap->n_eq && restore_lexmin(tab) < 0) + goto error; + for (i = 0; i < bmap->n_ineq; ++i) { + if (max) + isl_seq_neg(bmap->ineq[i] + o_var, + bmap->ineq[i] + o_var, n_var); + tab = add_lexmin_ineq(tab, bmap->ineq[i]); + if (max) + isl_seq_neg(bmap->ineq[i] + o_var, + bmap->ineq[i] + o_var, n_var); + if (!tab || tab->empty) + return tab; + } + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Given a main tableau where more than one row requires a split, + * determine and return the "best" row to split on. + * + * Given two rows in the main tableau, if the inequality corresponding + * to the first row is redundant with respect to that of the second row + * in the current tableau, then it is better to split on the second row, + * since in the positive part, both rows will be positive. + * (In the negative part a pivot will have to be performed and just about + * anything can happen to the sign of the other row.) + * + * As a simple heuristic, we therefore select the row that makes the most + * of the other rows redundant. + * + * Perhaps it would also be useful to look at the number of constraints + * that conflict with any given constraint. + * + * best is the best row so far (-1 when we have not found any row yet). + * best_r is the number of other rows made redundant by row best. + * When best is still -1, bset_r is meaningless, but it is initialized + * to some arbitrary value (0) anyway. Without this redundant initialization + * valgrind may warn about uninitialized memory accesses when isl + * is compiled with some versions of gcc. + */ +static int best_split(struct isl_tab *tab, struct isl_tab *context_tab) +{ + struct isl_tab_undo *snap; + int split; + int row; + int best = -1; + int best_r = 0; + + if (isl_tab_extend_cons(context_tab, 2) < 0) + return -1; + + snap = isl_tab_snap(context_tab); + + for (split = tab->n_redundant; split < tab->n_row; ++split) { + struct isl_tab_undo *snap2; + struct isl_vec *ineq = NULL; + int r = 0; + int ok; + + if (!isl_tab_var_from_row(tab, split)->is_nonneg) + continue; + if (tab->row_sign[split] != isl_tab_row_any) + continue; + + ineq = get_row_parameter_ineq(tab, split); + if (!ineq) + return -1; + ok = isl_tab_add_ineq(context_tab, ineq->el) >= 0; + isl_vec_free(ineq); + if (!ok) + return -1; + + snap2 = isl_tab_snap(context_tab); + + for (row = tab->n_redundant; row < tab->n_row; ++row) { + struct isl_tab_var *var; + + if (row == split) + continue; + if (!isl_tab_var_from_row(tab, row)->is_nonneg) + continue; + if (tab->row_sign[row] != isl_tab_row_any) + continue; + + ineq = get_row_parameter_ineq(tab, row); + if (!ineq) + return -1; + ok = isl_tab_add_ineq(context_tab, ineq->el) >= 0; + isl_vec_free(ineq); + if (!ok) + return -1; + var = &context_tab->con[context_tab->n_con - 1]; + if (!context_tab->empty && + !isl_tab_min_at_most_neg_one(context_tab, var)) + r++; + if (isl_tab_rollback(context_tab, snap2) < 0) + return -1; + } + if (best == -1 || r > best_r) { + best = split; + best_r = r; + } + if (isl_tab_rollback(context_tab, snap) < 0) + return -1; + } + + return best; +} + +static struct isl_basic_set *context_lex_peek_basic_set( + struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + if (!clex->tab) + return NULL; + return isl_tab_peek_bset(clex->tab); +} + +static struct isl_tab *context_lex_peek_tab(struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + return clex->tab; +} + +static void context_lex_add_eq(struct isl_context *context, isl_int *eq, + int check, int update) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + if (isl_tab_extend_cons(clex->tab, 2) < 0) + goto error; + if (add_lexmin_eq(clex->tab, eq) < 0) + goto error; + if (check) { + int v = tab_has_valid_sample(clex->tab, eq, 1); + if (v < 0) + goto error; + if (!v) + clex->tab = check_integer_feasible(clex->tab); + } + if (update) + clex->tab = check_samples(clex->tab, eq, 1); + return; +error: + isl_tab_free(clex->tab); + clex->tab = NULL; +} + +static void context_lex_add_ineq(struct isl_context *context, isl_int *ineq, + int check, int update) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + if (isl_tab_extend_cons(clex->tab, 1) < 0) + goto error; + clex->tab = add_lexmin_ineq(clex->tab, ineq); + if (check) { + int v = tab_has_valid_sample(clex->tab, ineq, 0); + if (v < 0) + goto error; + if (!v) + clex->tab = check_integer_feasible(clex->tab); + } + if (update) + clex->tab = check_samples(clex->tab, ineq, 0); + return; +error: + isl_tab_free(clex->tab); + clex->tab = NULL; +} + +static int context_lex_add_ineq_wrap(void *user, isl_int *ineq) +{ + struct isl_context *context = (struct isl_context *)user; + context_lex_add_ineq(context, ineq, 0, 0); + return context->op->is_ok(context) ? 0 : -1; +} + +/* Check which signs can be obtained by "ineq" on all the currently + * active sample values. See row_sign for more information. + */ +static enum isl_tab_row_sign tab_ineq_sign(struct isl_tab *tab, isl_int *ineq, + int strict) +{ + int i; + int sgn; + isl_int tmp; + enum isl_tab_row_sign res = isl_tab_row_unknown; + + isl_assert(tab->mat->ctx, tab->samples, return isl_tab_row_unknown); + isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, + return isl_tab_row_unknown); + + isl_int_init(tmp); + for (i = tab->n_outside; i < tab->n_sample; ++i) { + isl_seq_inner_product(tab->samples->row[i], ineq, + 1 + tab->n_var, &tmp); + sgn = isl_int_sgn(tmp); + if (sgn > 0 || (sgn == 0 && strict)) { + if (res == isl_tab_row_unknown) + res = isl_tab_row_pos; + if (res == isl_tab_row_neg) + res = isl_tab_row_any; + } + if (sgn < 0) { + if (res == isl_tab_row_unknown) + res = isl_tab_row_neg; + if (res == isl_tab_row_pos) + res = isl_tab_row_any; + } + if (res == isl_tab_row_any) + break; + } + isl_int_clear(tmp); + + return res; +} + +static enum isl_tab_row_sign context_lex_ineq_sign(struct isl_context *context, + isl_int *ineq, int strict) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + return tab_ineq_sign(clex->tab, ineq, strict); +} + +/* Check whether "ineq" can be added to the tableau without rendering + * it infeasible. + */ +static int context_lex_test_ineq(struct isl_context *context, isl_int *ineq) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + struct isl_tab_undo *snap; + int feasible; + + if (!clex->tab) + return -1; + + if (isl_tab_extend_cons(clex->tab, 1) < 0) + return -1; + + snap = isl_tab_snap(clex->tab); + if (isl_tab_push_basis(clex->tab) < 0) + return -1; + clex->tab = add_lexmin_ineq(clex->tab, ineq); + clex->tab = check_integer_feasible(clex->tab); + if (!clex->tab) + return -1; + feasible = !clex->tab->empty; + if (isl_tab_rollback(clex->tab, snap) < 0) + return -1; + + return feasible; +} + +static int context_lex_get_div(struct isl_context *context, struct isl_tab *tab, + struct isl_vec *div) +{ + return get_div(tab, context, div); +} + +/* Add a div specified by "div" to the context tableau and return + * 1 if the div is obviously non-negative. + * context_tab_add_div will always return 1, because all variables + * in a isl_context_lex tableau are non-negative. + * However, if we are using a big parameter in the context, then this only + * reflects the non-negativity of the variable used to _encode_ the + * div, i.e., div' = M + div, so we can't draw any conclusions. + */ +static int context_lex_add_div(struct isl_context *context, struct isl_vec *div) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + int nonneg; + nonneg = context_tab_add_div(clex->tab, div, + context_lex_add_ineq_wrap, context); + if (nonneg < 0) + return -1; + if (clex->tab->M) + return 0; + return nonneg; +} + +static int context_lex_detect_equalities(struct isl_context *context, + struct isl_tab *tab) +{ + return 0; +} + +static int context_lex_best_split(struct isl_context *context, + struct isl_tab *tab) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + struct isl_tab_undo *snap; + int r; + + snap = isl_tab_snap(clex->tab); + if (isl_tab_push_basis(clex->tab) < 0) + return -1; + r = best_split(tab, clex->tab); + + if (r >= 0 && isl_tab_rollback(clex->tab, snap) < 0) + return -1; + + return r; +} + +static int context_lex_is_empty(struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + if (!clex->tab) + return -1; + return clex->tab->empty; +} + +static void *context_lex_save(struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + struct isl_tab_undo *snap; + + snap = isl_tab_snap(clex->tab); + if (isl_tab_push_basis(clex->tab) < 0) + return NULL; + if (isl_tab_save_samples(clex->tab) < 0) + return NULL; + + return snap; +} + +static void context_lex_restore(struct isl_context *context, void *save) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + if (isl_tab_rollback(clex->tab, (struct isl_tab_undo *)save) < 0) { + isl_tab_free(clex->tab); + clex->tab = NULL; + } +} + +static void context_lex_discard(void *save) +{ +} + +static int context_lex_is_ok(struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + return !!clex->tab; +} + +/* For each variable in the context tableau, check if the variable can + * only attain non-negative values. If so, mark the parameter as non-negative + * in the main tableau. This allows for a more direct identification of some + * cases of violated constraints. + */ +static struct isl_tab *tab_detect_nonnegative_parameters(struct isl_tab *tab, + struct isl_tab *context_tab) +{ + int i; + struct isl_tab_undo *snap; + struct isl_vec *ineq = NULL; + struct isl_tab_var *var; + int n; + + if (context_tab->n_var == 0) + return tab; + + ineq = isl_vec_alloc(tab->mat->ctx, 1 + context_tab->n_var); + if (!ineq) + goto error; + + if (isl_tab_extend_cons(context_tab, 1) < 0) + goto error; + + snap = isl_tab_snap(context_tab); + + n = 0; + isl_seq_clr(ineq->el, ineq->size); + for (i = 0; i < context_tab->n_var; ++i) { + isl_int_set_si(ineq->el[1 + i], 1); + if (isl_tab_add_ineq(context_tab, ineq->el) < 0) + goto error; + var = &context_tab->con[context_tab->n_con - 1]; + if (!context_tab->empty && + !isl_tab_min_at_most_neg_one(context_tab, var)) { + int j = i; + if (i >= tab->n_param) + j = i - tab->n_param + tab->n_var - tab->n_div; + tab->var[j].is_nonneg = 1; + n++; + } + isl_int_set_si(ineq->el[1 + i], 0); + if (isl_tab_rollback(context_tab, snap) < 0) + goto error; + } + + if (context_tab->M && n == context_tab->n_var) { + context_tab->mat = isl_mat_drop_cols(context_tab->mat, 2, 1); + context_tab->M = 0; + } + + isl_vec_free(ineq); + return tab; +error: + isl_vec_free(ineq); + isl_tab_free(tab); + return NULL; +} + +static struct isl_tab *context_lex_detect_nonnegative_parameters( + struct isl_context *context, struct isl_tab *tab) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + struct isl_tab_undo *snap; + + if (!tab) + return NULL; + + snap = isl_tab_snap(clex->tab); + if (isl_tab_push_basis(clex->tab) < 0) + goto error; + + tab = tab_detect_nonnegative_parameters(tab, clex->tab); + + if (isl_tab_rollback(clex->tab, snap) < 0) + goto error; + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +static void context_lex_invalidate(struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + isl_tab_free(clex->tab); + clex->tab = NULL; +} + +static void context_lex_free(struct isl_context *context) +{ + struct isl_context_lex *clex = (struct isl_context_lex *)context; + isl_tab_free(clex->tab); + free(clex); +} + +struct isl_context_op isl_context_lex_op = { + context_lex_detect_nonnegative_parameters, + context_lex_peek_basic_set, + context_lex_peek_tab, + context_lex_add_eq, + context_lex_add_ineq, + context_lex_ineq_sign, + context_lex_test_ineq, + context_lex_get_div, + context_lex_add_div, + context_lex_detect_equalities, + context_lex_best_split, + context_lex_is_empty, + context_lex_is_ok, + context_lex_save, + context_lex_restore, + context_lex_discard, + context_lex_invalidate, + context_lex_free, +}; + +static struct isl_tab *context_tab_for_lexmin(struct isl_basic_set *bset) +{ + struct isl_tab *tab; + + if (!bset) + return NULL; + tab = tab_for_lexmin((struct isl_basic_map *)bset, NULL, 1, 0); + if (!tab) + goto error; + if (isl_tab_track_bset(tab, bset) < 0) + goto error; + tab = isl_tab_init_samples(tab); + return tab; +error: + isl_basic_set_free(bset); + return NULL; +} + +static struct isl_context *isl_context_lex_alloc(struct isl_basic_set *dom) +{ + struct isl_context_lex *clex; + + if (!dom) + return NULL; + + clex = isl_alloc_type(dom->ctx, struct isl_context_lex); + if (!clex) + return NULL; + + clex->context.op = &isl_context_lex_op; + + clex->tab = context_tab_for_lexmin(isl_basic_set_copy(dom)); + if (restore_lexmin(clex->tab) < 0) + goto error; + clex->tab = check_integer_feasible(clex->tab); + if (!clex->tab) + goto error; + + return &clex->context; +error: + clex->context.op->free(&clex->context); + return NULL; +} + +/* Representation of the context when using generalized basis reduction. + * + * "shifted" contains the offsets of the unit hypercubes that lie inside the + * context. Any rational point in "shifted" can therefore be rounded + * up to an integer point in the context. + * If the context is constrained by any equality, then "shifted" is not used + * as it would be empty. + */ +struct isl_context_gbr { + struct isl_context context; + struct isl_tab *tab; + struct isl_tab *shifted; + struct isl_tab *cone; +}; + +static struct isl_tab *context_gbr_detect_nonnegative_parameters( + struct isl_context *context, struct isl_tab *tab) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + if (!tab) + return NULL; + return tab_detect_nonnegative_parameters(tab, cgbr->tab); +} + +static struct isl_basic_set *context_gbr_peek_basic_set( + struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + if (!cgbr->tab) + return NULL; + return isl_tab_peek_bset(cgbr->tab); +} + +static struct isl_tab *context_gbr_peek_tab(struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + return cgbr->tab; +} + +/* Initialize the "shifted" tableau of the context, which + * contains the constraints of the original tableau shifted + * by the sum of all negative coefficients. This ensures + * that any rational point in the shifted tableau can + * be rounded up to yield an integer point in the original tableau. + */ +static void gbr_init_shifted(struct isl_context_gbr *cgbr) +{ + int i, j; + struct isl_vec *cst; + struct isl_basic_set *bset = isl_tab_peek_bset(cgbr->tab); + unsigned dim = isl_basic_set_total_dim(bset); + + cst = isl_vec_alloc(cgbr->tab->mat->ctx, bset->n_ineq); + if (!cst) + return; + + for (i = 0; i < bset->n_ineq; ++i) { + isl_int_set(cst->el[i], bset->ineq[i][0]); + for (j = 0; j < dim; ++j) { + if (!isl_int_is_neg(bset->ineq[i][1 + j])) + continue; + isl_int_add(bset->ineq[i][0], bset->ineq[i][0], + bset->ineq[i][1 + j]); + } + } + + cgbr->shifted = isl_tab_from_basic_set(bset, 0); + + for (i = 0; i < bset->n_ineq; ++i) + isl_int_set(bset->ineq[i][0], cst->el[i]); + + isl_vec_free(cst); +} + +/* Check if the shifted tableau is non-empty, and if so + * use the sample point to construct an integer point + * of the context tableau. + */ +static struct isl_vec *gbr_get_shifted_sample(struct isl_context_gbr *cgbr) +{ + struct isl_vec *sample; + + if (!cgbr->shifted) + gbr_init_shifted(cgbr); + if (!cgbr->shifted) + return NULL; + if (cgbr->shifted->empty) + return isl_vec_alloc(cgbr->tab->mat->ctx, 0); + + sample = isl_tab_get_sample_value(cgbr->shifted); + sample = isl_vec_ceil(sample); + + return sample; +} + +static struct isl_basic_set *drop_constant_terms(struct isl_basic_set *bset) +{ + int i; + + if (!bset) + return NULL; + + for (i = 0; i < bset->n_eq; ++i) + isl_int_set_si(bset->eq[i][0], 0); + + for (i = 0; i < bset->n_ineq; ++i) + isl_int_set_si(bset->ineq[i][0], 0); + + return bset; +} + +static int use_shifted(struct isl_context_gbr *cgbr) +{ + if (!cgbr->tab) + return 0; + return cgbr->tab->bmap->n_eq == 0 && cgbr->tab->bmap->n_div == 0; +} + +static struct isl_vec *gbr_get_sample(struct isl_context_gbr *cgbr) +{ + struct isl_basic_set *bset; + struct isl_basic_set *cone; + + if (isl_tab_sample_is_integer(cgbr->tab)) + return isl_tab_get_sample_value(cgbr->tab); + + if (use_shifted(cgbr)) { + struct isl_vec *sample; + + sample = gbr_get_shifted_sample(cgbr); + if (!sample || sample->size > 0) + return sample; + + isl_vec_free(sample); + } + + if (!cgbr->cone) { + bset = isl_tab_peek_bset(cgbr->tab); + cgbr->cone = isl_tab_from_recession_cone(bset, 0); + if (!cgbr->cone) + return NULL; + if (isl_tab_track_bset(cgbr->cone, + isl_basic_set_copy(bset)) < 0) + return NULL; + } + if (isl_tab_detect_implicit_equalities(cgbr->cone) < 0) + return NULL; + + if (cgbr->cone->n_dead == cgbr->cone->n_col) { + struct isl_vec *sample; + struct isl_tab_undo *snap; + + if (cgbr->tab->basis) { + if (cgbr->tab->basis->n_col != 1 + cgbr->tab->n_var) { + isl_mat_free(cgbr->tab->basis); + cgbr->tab->basis = NULL; + } + cgbr->tab->n_zero = 0; + cgbr->tab->n_unbounded = 0; + } + + snap = isl_tab_snap(cgbr->tab); + + sample = isl_tab_sample(cgbr->tab); + + if (!sample || isl_tab_rollback(cgbr->tab, snap) < 0) { + isl_vec_free(sample); + return NULL; + } + + return sample; + } + + cone = isl_basic_set_dup(isl_tab_peek_bset(cgbr->cone)); + cone = drop_constant_terms(cone); + cone = isl_basic_set_update_from_tab(cone, cgbr->cone); + cone = isl_basic_set_underlying_set(cone); + cone = isl_basic_set_gauss(cone, NULL); + + bset = isl_basic_set_dup(isl_tab_peek_bset(cgbr->tab)); + bset = isl_basic_set_update_from_tab(bset, cgbr->tab); + bset = isl_basic_set_underlying_set(bset); + bset = isl_basic_set_gauss(bset, NULL); + + return isl_basic_set_sample_with_cone(bset, cone); +} + +static void check_gbr_integer_feasible(struct isl_context_gbr *cgbr) +{ + struct isl_vec *sample; + + if (!cgbr->tab) + return; + + if (cgbr->tab->empty) + return; + + sample = gbr_get_sample(cgbr); + if (!sample) + goto error; + + if (sample->size == 0) { + isl_vec_free(sample); + if (isl_tab_mark_empty(cgbr->tab) < 0) + goto error; + return; + } + + if (isl_tab_add_sample(cgbr->tab, sample) < 0) + goto error; + + return; +error: + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; +} + +static struct isl_tab *add_gbr_eq(struct isl_tab *tab, isl_int *eq) +{ + if (!tab) + return NULL; + + if (isl_tab_extend_cons(tab, 2) < 0) + goto error; + + if (isl_tab_add_eq(tab, eq) < 0) + goto error; + + return tab; +error: + isl_tab_free(tab); + return NULL; +} + +/* Add the equality described by "eq" to the context. + * If "check" is set, then we check if the context is empty after + * adding the equality. + * If "update" is set, then we check if the samples are still valid. + * + * We do not explicitly add shifted copies of the equality to + * cgbr->shifted since they would conflict with each other. + * Instead, we directly mark cgbr->shifted empty. + */ +static void context_gbr_add_eq(struct isl_context *context, isl_int *eq, + int check, int update) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + + cgbr->tab = add_gbr_eq(cgbr->tab, eq); + + if (cgbr->shifted && !cgbr->shifted->empty && use_shifted(cgbr)) { + if (isl_tab_mark_empty(cgbr->shifted) < 0) + goto error; + } + + if (cgbr->cone && cgbr->cone->n_col != cgbr->cone->n_dead) { + if (isl_tab_extend_cons(cgbr->cone, 2) < 0) + goto error; + if (isl_tab_add_eq(cgbr->cone, eq) < 0) + goto error; + } + + if (check) { + int v = tab_has_valid_sample(cgbr->tab, eq, 1); + if (v < 0) + goto error; + if (!v) + check_gbr_integer_feasible(cgbr); + } + if (update) + cgbr->tab = check_samples(cgbr->tab, eq, 1); + return; +error: + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; +} + +static void add_gbr_ineq(struct isl_context_gbr *cgbr, isl_int *ineq) +{ + if (!cgbr->tab) + return; + + if (isl_tab_extend_cons(cgbr->tab, 1) < 0) + goto error; + + if (isl_tab_add_ineq(cgbr->tab, ineq) < 0) + goto error; + + if (cgbr->shifted && !cgbr->shifted->empty && use_shifted(cgbr)) { + int i; + unsigned dim; + dim = isl_basic_map_total_dim(cgbr->tab->bmap); + + if (isl_tab_extend_cons(cgbr->shifted, 1) < 0) + goto error; + + for (i = 0; i < dim; ++i) { + if (!isl_int_is_neg(ineq[1 + i])) + continue; + isl_int_add(ineq[0], ineq[0], ineq[1 + i]); + } + + if (isl_tab_add_ineq(cgbr->shifted, ineq) < 0) + goto error; + + for (i = 0; i < dim; ++i) { + if (!isl_int_is_neg(ineq[1 + i])) + continue; + isl_int_sub(ineq[0], ineq[0], ineq[1 + i]); + } + } + + if (cgbr->cone && cgbr->cone->n_col != cgbr->cone->n_dead) { + if (isl_tab_extend_cons(cgbr->cone, 1) < 0) + goto error; + if (isl_tab_add_ineq(cgbr->cone, ineq) < 0) + goto error; + } + + return; +error: + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; +} + +static void context_gbr_add_ineq(struct isl_context *context, isl_int *ineq, + int check, int update) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + + add_gbr_ineq(cgbr, ineq); + if (!cgbr->tab) + return; + + if (check) { + int v = tab_has_valid_sample(cgbr->tab, ineq, 0); + if (v < 0) + goto error; + if (!v) + check_gbr_integer_feasible(cgbr); + } + if (update) + cgbr->tab = check_samples(cgbr->tab, ineq, 0); + return; +error: + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; +} + +static int context_gbr_add_ineq_wrap(void *user, isl_int *ineq) +{ + struct isl_context *context = (struct isl_context *)user; + context_gbr_add_ineq(context, ineq, 0, 0); + return context->op->is_ok(context) ? 0 : -1; +} + +static enum isl_tab_row_sign context_gbr_ineq_sign(struct isl_context *context, + isl_int *ineq, int strict) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + return tab_ineq_sign(cgbr->tab, ineq, strict); +} + +/* Check whether "ineq" can be added to the tableau without rendering + * it infeasible. + */ +static int context_gbr_test_ineq(struct isl_context *context, isl_int *ineq) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + struct isl_tab_undo *snap; + struct isl_tab_undo *shifted_snap = NULL; + struct isl_tab_undo *cone_snap = NULL; + int feasible; + + if (!cgbr->tab) + return -1; + + if (isl_tab_extend_cons(cgbr->tab, 1) < 0) + return -1; + + snap = isl_tab_snap(cgbr->tab); + if (cgbr->shifted) + shifted_snap = isl_tab_snap(cgbr->shifted); + if (cgbr->cone) + cone_snap = isl_tab_snap(cgbr->cone); + add_gbr_ineq(cgbr, ineq); + check_gbr_integer_feasible(cgbr); + if (!cgbr->tab) + return -1; + feasible = !cgbr->tab->empty; + if (isl_tab_rollback(cgbr->tab, snap) < 0) + return -1; + if (shifted_snap) { + if (isl_tab_rollback(cgbr->shifted, shifted_snap)) + return -1; + } else if (cgbr->shifted) { + isl_tab_free(cgbr->shifted); + cgbr->shifted = NULL; + } + if (cone_snap) { + if (isl_tab_rollback(cgbr->cone, cone_snap)) + return -1; + } else if (cgbr->cone) { + isl_tab_free(cgbr->cone); + cgbr->cone = NULL; + } + + return feasible; +} + +/* Return the column of the last of the variables associated to + * a column that has a non-zero coefficient. + * This function is called in a context where only coefficients + * of parameters or divs can be non-zero. + */ +static int last_non_zero_var_col(struct isl_tab *tab, isl_int *p) +{ + int i; + int col; + + if (tab->n_var == 0) + return -1; + + for (i = tab->n_var - 1; i >= 0; --i) { + if (i >= tab->n_param && i < tab->n_var - tab->n_div) + continue; + if (tab->var[i].is_row) + continue; + col = tab->var[i].index; + if (!isl_int_is_zero(p[col])) + return col; + } + + return -1; +} + +/* Look through all the recently added equalities in the context + * to see if we can propagate any of them to the main tableau. + * + * The newly added equalities in the context are encoded as pairs + * of inequalities starting at inequality "first". + * + * We tentatively add each of these equalities to the main tableau + * and if this happens to result in a row with a final coefficient + * that is one or negative one, we use it to kill a column + * in the main tableau. Otherwise, we discard the tentatively + * added row. + * + * Return 0 on success and -1 on failure. + */ +static int propagate_equalities(struct isl_context_gbr *cgbr, + struct isl_tab *tab, unsigned first) +{ + int i; + struct isl_vec *eq = NULL; + + eq = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var); + if (!eq) + goto error; + + if (isl_tab_extend_cons(tab, (cgbr->tab->bmap->n_ineq - first)/2) < 0) + goto error; + + isl_seq_clr(eq->el + 1 + tab->n_param, + tab->n_var - tab->n_param - tab->n_div); + for (i = first; i < cgbr->tab->bmap->n_ineq; i += 2) { + int j; + int r; + struct isl_tab_undo *snap; + snap = isl_tab_snap(tab); + + isl_seq_cpy(eq->el, cgbr->tab->bmap->ineq[i], 1 + tab->n_param); + isl_seq_cpy(eq->el + 1 + tab->n_var - tab->n_div, + cgbr->tab->bmap->ineq[i] + 1 + tab->n_param, + tab->n_div); + + r = isl_tab_add_row(tab, eq->el); + if (r < 0) + goto error; + r = tab->con[r].index; + j = last_non_zero_var_col(tab, tab->mat->row[r] + 2 + tab->M); + if (j < 0 || j < tab->n_dead || + !isl_int_is_one(tab->mat->row[r][0]) || + (!isl_int_is_one(tab->mat->row[r][2 + tab->M + j]) && + !isl_int_is_negone(tab->mat->row[r][2 + tab->M + j]))) { + if (isl_tab_rollback(tab, snap) < 0) + goto error; + continue; + } + if (isl_tab_pivot(tab, r, j) < 0) + goto error; + if (isl_tab_kill_col(tab, j) < 0) + goto error; + + if (restore_lexmin(tab) < 0) + goto error; + } + + isl_vec_free(eq); + + return 0; +error: + isl_vec_free(eq); + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; + return -1; +} + +static int context_gbr_detect_equalities(struct isl_context *context, + struct isl_tab *tab) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + unsigned n_ineq; + + if (!cgbr->cone) { + struct isl_basic_set *bset = isl_tab_peek_bset(cgbr->tab); + cgbr->cone = isl_tab_from_recession_cone(bset, 0); + if (!cgbr->cone) + goto error; + if (isl_tab_track_bset(cgbr->cone, + isl_basic_set_copy(bset)) < 0) + goto error; + } + if (isl_tab_detect_implicit_equalities(cgbr->cone) < 0) + goto error; + + n_ineq = cgbr->tab->bmap->n_ineq; + cgbr->tab = isl_tab_detect_equalities(cgbr->tab, cgbr->cone); + if (!cgbr->tab) + return -1; + if (cgbr->tab->bmap->n_ineq > n_ineq && + propagate_equalities(cgbr, tab, n_ineq) < 0) + return -1; + + return 0; +error: + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; + return -1; +} + +static int context_gbr_get_div(struct isl_context *context, struct isl_tab *tab, + struct isl_vec *div) +{ + return get_div(tab, context, div); +} + +static int context_gbr_add_div(struct isl_context *context, struct isl_vec *div) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + if (cgbr->cone) { + int k; + + if (isl_tab_extend_cons(cgbr->cone, 3) < 0) + return -1; + if (isl_tab_extend_vars(cgbr->cone, 1) < 0) + return -1; + if (isl_tab_allocate_var(cgbr->cone) <0) + return -1; + + cgbr->cone->bmap = isl_basic_map_extend_space(cgbr->cone->bmap, + isl_basic_map_get_space(cgbr->cone->bmap), 1, 0, 2); + k = isl_basic_map_alloc_div(cgbr->cone->bmap); + if (k < 0) + return -1; + isl_seq_cpy(cgbr->cone->bmap->div[k], div->el, div->size); + if (isl_tab_push(cgbr->cone, isl_tab_undo_bmap_div) < 0) + return -1; + } + return context_tab_add_div(cgbr->tab, div, + context_gbr_add_ineq_wrap, context); +} + +static int context_gbr_best_split(struct isl_context *context, + struct isl_tab *tab) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + struct isl_tab_undo *snap; + int r; + + snap = isl_tab_snap(cgbr->tab); + r = best_split(tab, cgbr->tab); + + if (r >= 0 && isl_tab_rollback(cgbr->tab, snap) < 0) + return -1; + + return r; +} + +static int context_gbr_is_empty(struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + if (!cgbr->tab) + return -1; + return cgbr->tab->empty; +} + +struct isl_gbr_tab_undo { + struct isl_tab_undo *tab_snap; + struct isl_tab_undo *shifted_snap; + struct isl_tab_undo *cone_snap; +}; + +static void *context_gbr_save(struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + struct isl_gbr_tab_undo *snap; + + if (!cgbr->tab) + return NULL; + + snap = isl_alloc_type(cgbr->tab->mat->ctx, struct isl_gbr_tab_undo); + if (!snap) + return NULL; + + snap->tab_snap = isl_tab_snap(cgbr->tab); + if (isl_tab_save_samples(cgbr->tab) < 0) + goto error; + + if (cgbr->shifted) + snap->shifted_snap = isl_tab_snap(cgbr->shifted); + else + snap->shifted_snap = NULL; + + if (cgbr->cone) + snap->cone_snap = isl_tab_snap(cgbr->cone); + else + snap->cone_snap = NULL; + + return snap; +error: + free(snap); + return NULL; +} + +static void context_gbr_restore(struct isl_context *context, void *save) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + struct isl_gbr_tab_undo *snap = (struct isl_gbr_tab_undo *)save; + if (!snap) + goto error; + if (isl_tab_rollback(cgbr->tab, snap->tab_snap) < 0) + goto error; + + if (snap->shifted_snap) { + if (isl_tab_rollback(cgbr->shifted, snap->shifted_snap) < 0) + goto error; + } else if (cgbr->shifted) { + isl_tab_free(cgbr->shifted); + cgbr->shifted = NULL; + } + + if (snap->cone_snap) { + if (isl_tab_rollback(cgbr->cone, snap->cone_snap) < 0) + goto error; + } else if (cgbr->cone) { + isl_tab_free(cgbr->cone); + cgbr->cone = NULL; + } + + free(snap); + + return; +error: + free(snap); + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; +} + +static void context_gbr_discard(void *save) +{ + struct isl_gbr_tab_undo *snap = (struct isl_gbr_tab_undo *)save; + free(snap); +} + +static int context_gbr_is_ok(struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + return !!cgbr->tab; +} + +static void context_gbr_invalidate(struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + isl_tab_free(cgbr->tab); + cgbr->tab = NULL; +} + +static void context_gbr_free(struct isl_context *context) +{ + struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context; + isl_tab_free(cgbr->tab); + isl_tab_free(cgbr->shifted); + isl_tab_free(cgbr->cone); + free(cgbr); +} + +struct isl_context_op isl_context_gbr_op = { + context_gbr_detect_nonnegative_parameters, + context_gbr_peek_basic_set, + context_gbr_peek_tab, + context_gbr_add_eq, + context_gbr_add_ineq, + context_gbr_ineq_sign, + context_gbr_test_ineq, + context_gbr_get_div, + context_gbr_add_div, + context_gbr_detect_equalities, + context_gbr_best_split, + context_gbr_is_empty, + context_gbr_is_ok, + context_gbr_save, + context_gbr_restore, + context_gbr_discard, + context_gbr_invalidate, + context_gbr_free, +}; + +static struct isl_context *isl_context_gbr_alloc(struct isl_basic_set *dom) +{ + struct isl_context_gbr *cgbr; + + if (!dom) + return NULL; + + cgbr = isl_calloc_type(dom->ctx, struct isl_context_gbr); + if (!cgbr) + return NULL; + + cgbr->context.op = &isl_context_gbr_op; + + cgbr->shifted = NULL; + cgbr->cone = NULL; + cgbr->tab = isl_tab_from_basic_set(dom, 1); + cgbr->tab = isl_tab_init_samples(cgbr->tab); + if (!cgbr->tab) + goto error; + check_gbr_integer_feasible(cgbr); + + return &cgbr->context; +error: + cgbr->context.op->free(&cgbr->context); + return NULL; +} + +static struct isl_context *isl_context_alloc(struct isl_basic_set *dom) +{ + if (!dom) + return NULL; + + if (dom->ctx->opt->context == ISL_CONTEXT_LEXMIN) + return isl_context_lex_alloc(dom); + else + return isl_context_gbr_alloc(dom); +} + +/* Construct an isl_sol_map structure for accumulating the solution. + * If track_empty is set, then we also keep track of the parts + * of the context where there is no solution. + * If max is set, then we are solving a maximization, rather than + * a minimization problem, which means that the variables in the + * tableau have value "M - x" rather than "M + x". + */ +static struct isl_sol *sol_map_init(struct isl_basic_map *bmap, + struct isl_basic_set *dom, int track_empty, int max) +{ + struct isl_sol_map *sol_map = NULL; + + if (!bmap) + goto error; + + sol_map = isl_calloc_type(bmap->ctx, struct isl_sol_map); + if (!sol_map) + goto error; + + sol_map->sol.rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + sol_map->sol.dec_level.callback.run = &sol_dec_level_wrap; + sol_map->sol.dec_level.sol = &sol_map->sol; + sol_map->sol.max = max; + sol_map->sol.n_out = isl_basic_map_dim(bmap, isl_dim_out); + sol_map->sol.add = &sol_map_add_wrap; + sol_map->sol.add_empty = track_empty ? &sol_map_add_empty_wrap : NULL; + sol_map->sol.free = &sol_map_free_wrap; + sol_map->map = isl_map_alloc_space(isl_basic_map_get_space(bmap), 1, + ISL_MAP_DISJOINT); + if (!sol_map->map) + goto error; + + sol_map->sol.context = isl_context_alloc(dom); + if (!sol_map->sol.context) + goto error; + + if (track_empty) { + sol_map->empty = isl_set_alloc_space(isl_basic_set_get_space(dom), + 1, ISL_SET_DISJOINT); + if (!sol_map->empty) + goto error; + } + + isl_basic_set_free(dom); + return &sol_map->sol; +error: + isl_basic_set_free(dom); + sol_map_free(sol_map); + return NULL; +} + +/* Check whether all coefficients of (non-parameter) variables + * are non-positive, meaning that no pivots can be performed on the row. + */ +static int is_critical(struct isl_tab *tab, int row) +{ + int j; + unsigned off = 2 + tab->M; + + for (j = tab->n_dead; j < tab->n_col; ++j) { + if (tab->col_var[j] >= 0 && + (tab->col_var[j] < tab->n_param || + tab->col_var[j] >= tab->n_var - tab->n_div)) + continue; + + if (isl_int_is_pos(tab->mat->row[row][off + j])) + return 0; + } + + return 1; +} + +/* Check whether the inequality represented by vec is strict over the integers, + * i.e., there are no integer values satisfying the constraint with + * equality. This happens if the gcd of the coefficients is not a divisor + * of the constant term. If so, scale the constraint down by the gcd + * of the coefficients. + */ +static int is_strict(struct isl_vec *vec) +{ + isl_int gcd; + int strict = 0; + + isl_int_init(gcd); + isl_seq_gcd(vec->el + 1, vec->size - 1, &gcd); + if (!isl_int_is_one(gcd)) { + strict = !isl_int_is_divisible_by(vec->el[0], gcd); + isl_int_fdiv_q(vec->el[0], vec->el[0], gcd); + isl_seq_scale_down(vec->el + 1, vec->el + 1, gcd, vec->size-1); + } + isl_int_clear(gcd); + + return strict; +} + +/* Determine the sign of the given row of the main tableau. + * The result is one of + * isl_tab_row_pos: always non-negative; no pivot needed + * isl_tab_row_neg: always non-positive; pivot + * isl_tab_row_any: can be both positive and negative; split + * + * We first handle some simple cases + * - the row sign may be known already + * - the row may be obviously non-negative + * - the parametric constant may be equal to that of another row + * for which we know the sign. This sign will be either "pos" or + * "any". If it had been "neg" then we would have pivoted before. + * + * If none of these cases hold, we check the value of the row for each + * of the currently active samples. Based on the signs of these values + * we make an initial determination of the sign of the row. + * + * all zero -> unk(nown) + * all non-negative -> pos + * all non-positive -> neg + * both negative and positive -> all + * + * If we end up with "all", we are done. + * Otherwise, we perform a check for positive and/or negative + * values as follows. + * + * samples neg unk pos + * <0 ? Y N Y N + * pos any pos + * >0 ? Y N Y N + * any neg any neg + * + * There is no special sign for "zero", because we can usually treat zero + * as either non-negative or non-positive, whatever works out best. + * However, if the row is "critical", meaning that pivoting is impossible + * then we don't want to limp zero with the non-positive case, because + * then we we would lose the solution for those values of the parameters + * where the value of the row is zero. Instead, we treat 0 as non-negative + * ensuring a split if the row can attain both zero and negative values. + * The same happens when the original constraint was one that could not + * be satisfied with equality by any integer values of the parameters. + * In this case, we normalize the constraint, but then a value of zero + * for the normalized constraint is actually a positive value for the + * original constraint, so again we need to treat zero as non-negative. + * In both these cases, we have the following decision tree instead: + * + * all non-negative -> pos + * all negative -> neg + * both negative and non-negative -> all + * + * samples neg pos + * <0 ? Y N + * any pos + * >=0 ? Y N + * any neg + */ +static enum isl_tab_row_sign row_sign(struct isl_tab *tab, + struct isl_sol *sol, int row) +{ + struct isl_vec *ineq = NULL; + enum isl_tab_row_sign res = isl_tab_row_unknown; + int critical; + int strict; + int row2; + + if (tab->row_sign[row] != isl_tab_row_unknown) + return tab->row_sign[row]; + if (is_obviously_nonneg(tab, row)) + return isl_tab_row_pos; + for (row2 = tab->n_redundant; row2 < tab->n_row; ++row2) { + if (tab->row_sign[row2] == isl_tab_row_unknown) + continue; + if (identical_parameter_line(tab, row, row2)) + return tab->row_sign[row2]; + } + + critical = is_critical(tab, row); + + ineq = get_row_parameter_ineq(tab, row); + if (!ineq) + goto error; + + strict = is_strict(ineq); + + res = sol->context->op->ineq_sign(sol->context, ineq->el, + critical || strict); + + if (res == isl_tab_row_unknown || res == isl_tab_row_pos) { + /* test for negative values */ + int feasible; + isl_seq_neg(ineq->el, ineq->el, ineq->size); + isl_int_sub_ui(ineq->el[0], ineq->el[0], 1); + + feasible = sol->context->op->test_ineq(sol->context, ineq->el); + if (feasible < 0) + goto error; + if (!feasible) + res = isl_tab_row_pos; + else + res = (res == isl_tab_row_unknown) ? isl_tab_row_neg + : isl_tab_row_any; + if (res == isl_tab_row_neg) { + isl_seq_neg(ineq->el, ineq->el, ineq->size); + isl_int_sub_ui(ineq->el[0], ineq->el[0], 1); + } + } + + if (res == isl_tab_row_neg) { + /* test for positive values */ + int feasible; + if (!critical && !strict) + isl_int_sub_ui(ineq->el[0], ineq->el[0], 1); + + feasible = sol->context->op->test_ineq(sol->context, ineq->el); + if (feasible < 0) + goto error; + if (feasible) + res = isl_tab_row_any; + } + + isl_vec_free(ineq); + return res; +error: + isl_vec_free(ineq); + return isl_tab_row_unknown; +} + +static void find_solutions(struct isl_sol *sol, struct isl_tab *tab); + +/* Find solutions for values of the parameters that satisfy the given + * inequality. + * + * We currently take a snapshot of the context tableau that is reset + * when we return from this function, while we make a copy of the main + * tableau, leaving the original main tableau untouched. + * These are fairly arbitrary choices. Making a copy also of the context + * tableau would obviate the need to undo any changes made to it later, + * while taking a snapshot of the main tableau could reduce memory usage. + * If we were to switch to taking a snapshot of the main tableau, + * we would have to keep in mind that we need to save the row signs + * and that we need to do this before saving the current basis + * such that the basis has been restore before we restore the row signs. + */ +static void find_in_pos(struct isl_sol *sol, struct isl_tab *tab, isl_int *ineq) +{ + void *saved; + + if (!sol->context) + goto error; + saved = sol->context->op->save(sol->context); + + tab = isl_tab_dup(tab); + if (!tab) + goto error; + + sol->context->op->add_ineq(sol->context, ineq, 0, 1); + + find_solutions(sol, tab); + + if (!sol->error) + sol->context->op->restore(sol->context, saved); + else + sol->context->op->discard(saved); + return; +error: + sol->error = 1; +} + +/* Record the absence of solutions for those values of the parameters + * that do not satisfy the given inequality with equality. + */ +static void no_sol_in_strict(struct isl_sol *sol, + struct isl_tab *tab, struct isl_vec *ineq) +{ + int empty; + void *saved; + + if (!sol->context || sol->error) + goto error; + saved = sol->context->op->save(sol->context); + + isl_int_sub_ui(ineq->el[0], ineq->el[0], 1); + + sol->context->op->add_ineq(sol->context, ineq->el, 1, 0); + if (!sol->context) + goto error; + + empty = tab->empty; + tab->empty = 1; + sol_add(sol, tab); + tab->empty = empty; + + isl_int_add_ui(ineq->el[0], ineq->el[0], 1); + + sol->context->op->restore(sol->context, saved); + return; +error: + sol->error = 1; +} + +/* Reset all row variables that are marked to have a sign that may + * be both positive and negative to have an unknown sign. + */ +static void reset_any_to_unknown(struct isl_tab *tab) +{ + int row; + + for (row = tab->n_redundant; row < tab->n_row; ++row) { + if (!isl_tab_var_from_row(tab, row)->is_nonneg) + continue; + if (tab->row_sign[row] == isl_tab_row_any) + tab->row_sign[row] = isl_tab_row_unknown; + } +} + +/* Compute the lexicographic minimum of the set represented by the main + * tableau "tab" within the context "sol->context_tab". + * On entry the sample value of the main tableau is lexicographically + * less than or equal to this lexicographic minimum. + * Pivots are performed until a feasible point is found, which is then + * necessarily equal to the minimum, or until the tableau is found to + * be infeasible. Some pivots may need to be performed for only some + * feasible values of the context tableau. If so, the context tableau + * is split into a part where the pivot is needed and a part where it is not. + * + * Whenever we enter the main loop, the main tableau is such that no + * "obvious" pivots need to be performed on it, where "obvious" means + * that the given row can be seen to be negative without looking at + * the context tableau. In particular, for non-parametric problems, + * no pivots need to be performed on the main tableau. + * The caller of find_solutions is responsible for making this property + * hold prior to the first iteration of the loop, while restore_lexmin + * is called before every other iteration. + * + * Inside the main loop, we first examine the signs of the rows of + * the main tableau within the context of the context tableau. + * If we find a row that is always non-positive for all values of + * the parameters satisfying the context tableau and negative for at + * least one value of the parameters, we perform the appropriate pivot + * and start over. An exception is the case where no pivot can be + * performed on the row. In this case, we require that the sign of + * the row is negative for all values of the parameters (rather than just + * non-positive). This special case is handled inside row_sign, which + * will say that the row can have any sign if it determines that it can + * attain both negative and zero values. + * + * If we can't find a row that always requires a pivot, but we can find + * one or more rows that require a pivot for some values of the parameters + * (i.e., the row can attain both positive and negative signs), then we split + * the context tableau into two parts, one where we force the sign to be + * non-negative and one where we force is to be negative. + * The non-negative part is handled by a recursive call (through find_in_pos). + * Upon returning from this call, we continue with the negative part and + * perform the required pivot. + * + * If no such rows can be found, all rows are non-negative and we have + * found a (rational) feasible point. If we only wanted a rational point + * then we are done. + * Otherwise, we check if all values of the sample point of the tableau + * are integral for the variables. If so, we have found the minimal + * integral point and we are done. + * If the sample point is not integral, then we need to make a distinction + * based on whether the constant term is non-integral or the coefficients + * of the parameters. Furthermore, in order to decide how to handle + * the non-integrality, we also need to know whether the coefficients + * of the other columns in the tableau are integral. This leads + * to the following table. The first two rows do not correspond + * to a non-integral sample point and are only mentioned for completeness. + * + * constant parameters other + * + * int int int | + * int int rat | -> no problem + * + * rat int int -> fail + * + * rat int rat -> cut + * + * int rat rat | + * rat rat rat | -> parametric cut + * + * int rat int | + * rat rat int | -> split context + * + * If the parametric constant is completely integral, then there is nothing + * to be done. If the constant term is non-integral, but all the other + * coefficient are integral, then there is nothing that can be done + * and the tableau has no integral solution. + * If, on the other hand, one or more of the other columns have rational + * coefficients, but the parameter coefficients are all integral, then + * we can perform a regular (non-parametric) cut. + * Finally, if there is any parameter coefficient that is non-integral, + * then we need to involve the context tableau. There are two cases here. + * If at least one other column has a rational coefficient, then we + * can perform a parametric cut in the main tableau by adding a new + * integer division in the context tableau. + * If all other columns have integral coefficients, then we need to + * enforce that the rational combination of parameters (c + \sum a_i y_i)/m + * is always integral. We do this by introducing an integer division + * q = floor((c + \sum a_i y_i)/m) and stipulating that its argument should + * always be integral in the context tableau, i.e., m q = c + \sum a_i y_i. + * Since q is expressed in the tableau as + * c + \sum a_i y_i - m q >= 0 + * -c - \sum a_i y_i + m q + m - 1 >= 0 + * it is sufficient to add the inequality + * -c - \sum a_i y_i + m q >= 0 + * In the part of the context where this inequality does not hold, the + * main tableau is marked as being empty. + */ +static void find_solutions(struct isl_sol *sol, struct isl_tab *tab) +{ + struct isl_context *context; + int r; + + if (!tab || sol->error) + goto error; + + context = sol->context; + + if (tab->empty) + goto done; + if (context->op->is_empty(context)) + goto done; + + for (r = 0; r >= 0 && tab && !tab->empty; r = restore_lexmin(tab)) { + int flags; + int row; + enum isl_tab_row_sign sgn; + int split = -1; + int n_split = 0; + + for (row = tab->n_redundant; row < tab->n_row; ++row) { + if (!isl_tab_var_from_row(tab, row)->is_nonneg) + continue; + sgn = row_sign(tab, sol, row); + if (!sgn) + goto error; + tab->row_sign[row] = sgn; + if (sgn == isl_tab_row_any) + n_split++; + if (sgn == isl_tab_row_any && split == -1) + split = row; + if (sgn == isl_tab_row_neg) + break; + } + if (row < tab->n_row) + continue; + if (split != -1) { + struct isl_vec *ineq; + if (n_split != 1) + split = context->op->best_split(context, tab); + if (split < 0) + goto error; + ineq = get_row_parameter_ineq(tab, split); + if (!ineq) + goto error; + is_strict(ineq); + reset_any_to_unknown(tab); + tab->row_sign[split] = isl_tab_row_pos; + sol_inc_level(sol); + find_in_pos(sol, tab, ineq->el); + tab->row_sign[split] = isl_tab_row_neg; + isl_seq_neg(ineq->el, ineq->el, ineq->size); + isl_int_sub_ui(ineq->el[0], ineq->el[0], 1); + if (!sol->error) + context->op->add_ineq(context, ineq->el, 0, 1); + isl_vec_free(ineq); + if (sol->error) + goto error; + continue; + } + if (tab->rational) + break; + row = first_non_integer_row(tab, &flags); + if (row < 0) + break; + if (ISL_FL_ISSET(flags, I_PAR)) { + if (ISL_FL_ISSET(flags, I_VAR)) { + if (isl_tab_mark_empty(tab) < 0) + goto error; + break; + } + row = add_cut(tab, row); + } else if (ISL_FL_ISSET(flags, I_VAR)) { + struct isl_vec *div; + struct isl_vec *ineq; + int d; + div = get_row_split_div(tab, row); + if (!div) + goto error; + d = context->op->get_div(context, tab, div); + isl_vec_free(div); + if (d < 0) + goto error; + ineq = ineq_for_div(context->op->peek_basic_set(context), d); + if (!ineq) + goto error; + sol_inc_level(sol); + no_sol_in_strict(sol, tab, ineq); + isl_seq_neg(ineq->el, ineq->el, ineq->size); + context->op->add_ineq(context, ineq->el, 1, 1); + isl_vec_free(ineq); + if (sol->error || !context->op->is_ok(context)) + goto error; + tab = set_row_cst_to_div(tab, row, d); + if (context->op->is_empty(context)) + break; + } else + row = add_parametric_cut(tab, row, context); + if (row < 0) + goto error; + } + if (r < 0) + goto error; +done: + sol_add(sol, tab); + isl_tab_free(tab); + return; +error: + isl_tab_free(tab); + sol->error = 1; +} + +/* Does "sol" contain a pair of partial solutions that could potentially + * be merged? + * + * We currently only check that "sol" is not in an error state + * and that there are at least two partial solutions of which the final two + * are defined at the same level. + */ +static int sol_has_mergeable_solutions(struct isl_sol *sol) +{ + if (sol->error) + return 0; + if (!sol->partial) + return 0; + if (!sol->partial->next) + return 0; + return sol->partial->level == sol->partial->next->level; +} + +/* Compute the lexicographic minimum of the set represented by the main + * tableau "tab" within the context "sol->context_tab". + * + * As a preprocessing step, we first transfer all the purely parametric + * equalities from the main tableau to the context tableau, i.e., + * parameters that have been pivoted to a row. + * These equalities are ignored by the main algorithm, because the + * corresponding rows may not be marked as being non-negative. + * In parts of the context where the added equality does not hold, + * the main tableau is marked as being empty. + * + * Before we embark on the actual computation, we save a copy + * of the context. When we return, we check if there are any + * partial solutions that can potentially be merged. If so, + * we perform a rollback to the initial state of the context. + * The merging of partial solutions happens inside calls to + * sol_dec_level that are pushed onto the undo stack of the context. + * If there are no partial solutions that can potentially be merged + * then the rollback is skipped as it would just be wasted effort. + */ +static void find_solutions_main(struct isl_sol *sol, struct isl_tab *tab) +{ + int row; + void *saved; + + if (!tab) + goto error; + + sol->level = 0; + + for (row = tab->n_redundant; row < tab->n_row; ++row) { + int p; + struct isl_vec *eq; + + if (tab->row_var[row] < 0) + continue; + if (tab->row_var[row] >= tab->n_param && + tab->row_var[row] < tab->n_var - tab->n_div) + continue; + if (tab->row_var[row] < tab->n_param) + p = tab->row_var[row]; + else + p = tab->row_var[row] + + tab->n_param - (tab->n_var - tab->n_div); + + eq = isl_vec_alloc(tab->mat->ctx, 1+tab->n_param+tab->n_div); + if (!eq) + goto error; + get_row_parameter_line(tab, row, eq->el); + isl_int_neg(eq->el[1 + p], tab->mat->row[row][0]); + eq = isl_vec_normalize(eq); + + sol_inc_level(sol); + no_sol_in_strict(sol, tab, eq); + + isl_seq_neg(eq->el, eq->el, eq->size); + sol_inc_level(sol); + no_sol_in_strict(sol, tab, eq); + isl_seq_neg(eq->el, eq->el, eq->size); + + sol->context->op->add_eq(sol->context, eq->el, 1, 1); + + isl_vec_free(eq); + + if (isl_tab_mark_redundant(tab, row) < 0) + goto error; + + if (sol->context->op->is_empty(sol->context)) + break; + + row = tab->n_redundant - 1; + } + + saved = sol->context->op->save(sol->context); + + find_solutions(sol, tab); + + if (sol_has_mergeable_solutions(sol)) + sol->context->op->restore(sol->context, saved); + else + sol->context->op->discard(saved); + + sol->level = 0; + sol_pop(sol); + + return; +error: + isl_tab_free(tab); + sol->error = 1; +} + +/* Check if integer division "div" of "dom" also occurs in "bmap". + * If so, return its position within the divs. + * If not, return -1. + */ +static int find_context_div(struct isl_basic_map *bmap, + struct isl_basic_set *dom, unsigned div) +{ + int i; + unsigned b_dim = isl_space_dim(bmap->dim, isl_dim_all); + unsigned d_dim = isl_space_dim(dom->dim, isl_dim_all); + + if (isl_int_is_zero(dom->div[div][0])) + return -1; + if (isl_seq_first_non_zero(dom->div[div] + 2 + d_dim, dom->n_div) != -1) + return -1; + + for (i = 0; i < bmap->n_div; ++i) { + if (isl_int_is_zero(bmap->div[i][0])) + continue; + if (isl_seq_first_non_zero(bmap->div[i] + 2 + d_dim, + (b_dim - d_dim) + bmap->n_div) != -1) + continue; + if (isl_seq_eq(bmap->div[i], dom->div[div], 2 + d_dim)) + return i; + } + return -1; +} + +/* The correspondence between the variables in the main tableau, + * the context tableau, and the input map and domain is as follows. + * The first n_param and the last n_div variables of the main tableau + * form the variables of the context tableau. + * In the basic map, these n_param variables correspond to the + * parameters and the input dimensions. In the domain, they correspond + * to the parameters and the set dimensions. + * The n_div variables correspond to the integer divisions in the domain. + * To ensure that everything lines up, we may need to copy some of the + * integer divisions of the domain to the map. These have to be placed + * in the same order as those in the context and they have to be placed + * after any other integer divisions that the map may have. + * This function performs the required reordering. + */ +static struct isl_basic_map *align_context_divs(struct isl_basic_map *bmap, + struct isl_basic_set *dom) +{ + int i; + int common = 0; + int other; + + for (i = 0; i < dom->n_div; ++i) + if (find_context_div(bmap, dom, i) != -1) + common++; + other = bmap->n_div - common; + if (dom->n_div - common > 0) { + bmap = isl_basic_map_extend_space(bmap, isl_space_copy(bmap->dim), + dom->n_div - common, 0, 0); + if (!bmap) + return NULL; + } + for (i = 0; i < dom->n_div; ++i) { + int pos = find_context_div(bmap, dom, i); + if (pos < 0) { + pos = isl_basic_map_alloc_div(bmap); + if (pos < 0) + goto error; + isl_int_set_si(bmap->div[pos][0], 0); + } + if (pos != other + i) + isl_basic_map_swap_div(bmap, pos, other + i); + } + return bmap; +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Base case of isl_tab_basic_map_partial_lexopt, after removing + * some obvious symmetries. + * + * We make sure the divs in the domain are properly ordered, + * because they will be added one by one in the given order + * during the construction of the solution map. + */ +static struct isl_sol *basic_map_partial_lexopt_base( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max, + struct isl_sol *(*init)(__isl_keep isl_basic_map *bmap, + __isl_take isl_basic_set *dom, int track_empty, int max)) +{ + struct isl_tab *tab; + struct isl_sol *sol = NULL; + struct isl_context *context; + + if (dom->n_div) { + dom = isl_basic_set_order_divs(dom); + bmap = align_context_divs(bmap, dom); + } + sol = init(bmap, dom, !!empty, max); + if (!sol) + goto error; + + context = sol->context; + if (isl_basic_set_plain_is_empty(context->op->peek_basic_set(context))) + /* nothing */; + else if (isl_basic_map_plain_is_empty(bmap)) { + if (sol->add_empty) + sol->add_empty(sol, + isl_basic_set_copy(context->op->peek_basic_set(context))); + } else { + tab = tab_for_lexmin(bmap, + context->op->peek_basic_set(context), 1, max); + tab = context->op->detect_nonnegative_parameters(context, tab); + find_solutions_main(sol, tab); + } + if (sol->error) + goto error; + + isl_basic_map_free(bmap); + return sol; +error: + sol_free(sol); + isl_basic_map_free(bmap); + return NULL; +} + +/* Base case of isl_tab_basic_map_partial_lexopt, after removing + * some obvious symmetries. + * + * We call basic_map_partial_lexopt_base and extract the results. + */ +static __isl_give isl_map *basic_map_partial_lexopt_base_map( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max) +{ + isl_map *result = NULL; + struct isl_sol *sol; + struct isl_sol_map *sol_map; + + sol = basic_map_partial_lexopt_base(bmap, dom, empty, max, + &sol_map_init); + if (!sol) + return NULL; + sol_map = (struct isl_sol_map *) sol; + + result = isl_map_copy(sol_map->map); + if (empty) + *empty = isl_set_copy(sol_map->empty); + sol_free(&sol_map->sol); + return result; +} + +/* Structure used during detection of parallel constraints. + * n_in: number of "input" variables: isl_dim_param + isl_dim_in + * n_out: number of "output" variables: isl_dim_out + isl_dim_div + * val: the coefficients of the output variables + */ +struct isl_constraint_equal_info { + isl_basic_map *bmap; + unsigned n_in; + unsigned n_out; + isl_int *val; +}; + +/* Check whether the coefficients of the output variables + * of the constraint in "entry" are equal to info->val. + */ +static int constraint_equal(const void *entry, const void *val) +{ + isl_int **row = (isl_int **)entry; + const struct isl_constraint_equal_info *info = val; + + return isl_seq_eq((*row) + 1 + info->n_in, info->val, info->n_out); +} + +/* Check whether "bmap" has a pair of constraints that have + * the same coefficients for the output variables. + * Note that the coefficients of the existentially quantified + * variables need to be zero since the existentially quantified + * of the result are usually not the same as those of the input. + * the isl_dim_out and isl_dim_div dimensions. + * If so, return 1 and return the row indices of the two constraints + * in *first and *second. + */ +static int parallel_constraints(__isl_keep isl_basic_map *bmap, + int *first, int *second) +{ + int i; + isl_ctx *ctx; + struct isl_hash_table *table = NULL; + struct isl_hash_table_entry *entry; + struct isl_constraint_equal_info info; + unsigned n_out; + unsigned n_div; + + ctx = isl_basic_map_get_ctx(bmap); + table = isl_hash_table_alloc(ctx, bmap->n_ineq); + if (!table) + goto error; + + info.n_in = isl_basic_map_dim(bmap, isl_dim_param) + + isl_basic_map_dim(bmap, isl_dim_in); + info.bmap = bmap; + n_out = isl_basic_map_dim(bmap, isl_dim_out); + n_div = isl_basic_map_dim(bmap, isl_dim_div); + info.n_out = n_out + n_div; + for (i = 0; i < bmap->n_ineq; ++i) { + uint32_t hash; + + info.val = bmap->ineq[i] + 1 + info.n_in; + if (isl_seq_first_non_zero(info.val, n_out) < 0) + continue; + if (isl_seq_first_non_zero(info.val + n_out, n_div) >= 0) + continue; + hash = isl_seq_get_hash(info.val, info.n_out); + entry = isl_hash_table_find(ctx, table, hash, + constraint_equal, &info, 1); + if (!entry) + goto error; + if (entry->data) + break; + entry->data = &bmap->ineq[i]; + } + + if (i < bmap->n_ineq) { + *first = ((isl_int **)entry->data) - bmap->ineq; + *second = i; + } + + isl_hash_table_free(ctx, table); + + return i < bmap->n_ineq; +error: + isl_hash_table_free(ctx, table); + return -1; +} + +/* Given a set of upper bounds in "var", add constraints to "bset" + * that make the i-th bound smallest. + * + * In particular, if there are n bounds b_i, then add the constraints + * + * b_i <= b_j for j > i + * b_i < b_j for j < i + */ +static __isl_give isl_basic_set *select_minimum(__isl_take isl_basic_set *bset, + __isl_keep isl_mat *var, int i) +{ + isl_ctx *ctx; + int j, k; + + ctx = isl_mat_get_ctx(var); + + for (j = 0; j < var->n_row; ++j) { + if (j == i) + continue; + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_combine(bset->ineq[k], ctx->one, var->row[j], + ctx->negone, var->row[i], var->n_col); + isl_int_set_si(bset->ineq[k][var->n_col], 0); + if (j < i) + isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1); + } + + bset = isl_basic_set_finalize(bset); + + return bset; +error: + isl_basic_set_free(bset); + return NULL; +} + +/* Given a set of upper bounds on the last "input" variable m, + * construct a set that assigns the minimal upper bound to m, i.e., + * construct a set that divides the space into cells where one + * of the upper bounds is smaller than all the others and assign + * this upper bound to m. + * + * In particular, if there are n bounds b_i, then the result + * consists of n basic sets, each one of the form + * + * m = b_i + * b_i <= b_j for j > i + * b_i < b_j for j < i + */ +static __isl_give isl_set *set_minimum(__isl_take isl_space *dim, + __isl_take isl_mat *var) +{ + int i, k; + isl_basic_set *bset = NULL; + isl_set *set = NULL; + + if (!dim || !var) + goto error; + + set = isl_set_alloc_space(isl_space_copy(dim), + var->n_row, ISL_SET_DISJOINT); + + for (i = 0; i < var->n_row; ++i) { + bset = isl_basic_set_alloc_space(isl_space_copy(dim), 0, + 1, var->n_row - 1); + k = isl_basic_set_alloc_equality(bset); + if (k < 0) + goto error; + isl_seq_cpy(bset->eq[k], var->row[i], var->n_col); + isl_int_set_si(bset->eq[k][var->n_col], -1); + bset = select_minimum(bset, var, i); + set = isl_set_add_basic_set(set, bset); + } + + isl_space_free(dim); + isl_mat_free(var); + return set; +error: + isl_basic_set_free(bset); + isl_set_free(set); + isl_space_free(dim); + isl_mat_free(var); + return NULL; +} + +/* Given that the last input variable of "bmap" represents the minimum + * of the bounds in "cst", check whether we need to split the domain + * based on which bound attains the minimum. + * + * A split is needed when the minimum appears in an integer division + * or in an equality. Otherwise, it is only needed if it appears in + * an upper bound that is different from the upper bounds on which it + * is defined. + */ +static int need_split_basic_map(__isl_keep isl_basic_map *bmap, + __isl_keep isl_mat *cst) +{ + int i, j; + unsigned total; + unsigned pos; + + pos = cst->n_col - 1; + total = isl_basic_map_dim(bmap, isl_dim_all); + + for (i = 0; i < bmap->n_div; ++i) + if (!isl_int_is_zero(bmap->div[i][2 + pos])) + return 1; + + for (i = 0; i < bmap->n_eq; ++i) + if (!isl_int_is_zero(bmap->eq[i][1 + pos])) + return 1; + + for (i = 0; i < bmap->n_ineq; ++i) { + if (isl_int_is_nonneg(bmap->ineq[i][1 + pos])) + continue; + if (!isl_int_is_negone(bmap->ineq[i][1 + pos])) + return 1; + if (isl_seq_first_non_zero(bmap->ineq[i] + 1 + pos + 1, + total - pos - 1) >= 0) + return 1; + + for (j = 0; j < cst->n_row; ++j) + if (isl_seq_eq(bmap->ineq[i], cst->row[j], cst->n_col)) + break; + if (j >= cst->n_row) + return 1; + } + + return 0; +} + +/* Given that the last set variable of "bset" represents the minimum + * of the bounds in "cst", check whether we need to split the domain + * based on which bound attains the minimum. + * + * We simply call need_split_basic_map here. This is safe because + * the position of the minimum is computed from "cst" and not + * from "bmap". + */ +static int need_split_basic_set(__isl_keep isl_basic_set *bset, + __isl_keep isl_mat *cst) +{ + return need_split_basic_map((isl_basic_map *)bset, cst); +} + +/* Given that the last set variable of "set" represents the minimum + * of the bounds in "cst", check whether we need to split the domain + * based on which bound attains the minimum. + */ +static int need_split_set(__isl_keep isl_set *set, __isl_keep isl_mat *cst) +{ + int i; + + for (i = 0; i < set->n; ++i) + if (need_split_basic_set(set->p[i], cst)) + return 1; + + return 0; +} + +/* Given a set of which the last set variable is the minimum + * of the bounds in "cst", split each basic set in the set + * in pieces where one of the bounds is (strictly) smaller than the others. + * This subdivision is given in "min_expr". + * The variable is subsequently projected out. + * + * We only do the split when it is needed. + * For example if the last input variable m = min(a,b) and the only + * constraints in the given basic set are lower bounds on m, + * i.e., l <= m = min(a,b), then we can simply project out m + * to obtain l <= a and l <= b, without having to split on whether + * m is equal to a or b. + */ +static __isl_give isl_set *split(__isl_take isl_set *empty, + __isl_take isl_set *min_expr, __isl_take isl_mat *cst) +{ + int n_in; + int i; + isl_space *dim; + isl_set *res; + + if (!empty || !min_expr || !cst) + goto error; + + n_in = isl_set_dim(empty, isl_dim_set); + dim = isl_set_get_space(empty); + dim = isl_space_drop_dims(dim, isl_dim_set, n_in - 1, 1); + res = isl_set_empty(dim); + + for (i = 0; i < empty->n; ++i) { + isl_set *set; + + set = isl_set_from_basic_set(isl_basic_set_copy(empty->p[i])); + if (need_split_basic_set(empty->p[i], cst)) + set = isl_set_intersect(set, isl_set_copy(min_expr)); + set = isl_set_remove_dims(set, isl_dim_set, n_in - 1, 1); + + res = isl_set_union_disjoint(res, set); + } + + isl_set_free(empty); + isl_set_free(min_expr); + isl_mat_free(cst); + return res; +error: + isl_set_free(empty); + isl_set_free(min_expr); + isl_mat_free(cst); + return NULL; +} + +/* Given a map of which the last input variable is the minimum + * of the bounds in "cst", split each basic set in the set + * in pieces where one of the bounds is (strictly) smaller than the others. + * This subdivision is given in "min_expr". + * The variable is subsequently projected out. + * + * The implementation is essentially the same as that of "split". + */ +static __isl_give isl_map *split_domain(__isl_take isl_map *opt, + __isl_take isl_set *min_expr, __isl_take isl_mat *cst) +{ + int n_in; + int i; + isl_space *dim; + isl_map *res; + + if (!opt || !min_expr || !cst) + goto error; + + n_in = isl_map_dim(opt, isl_dim_in); + dim = isl_map_get_space(opt); + dim = isl_space_drop_dims(dim, isl_dim_in, n_in - 1, 1); + res = isl_map_empty(dim); + + for (i = 0; i < opt->n; ++i) { + isl_map *map; + + map = isl_map_from_basic_map(isl_basic_map_copy(opt->p[i])); + if (need_split_basic_map(opt->p[i], cst)) + map = isl_map_intersect_domain(map, + isl_set_copy(min_expr)); + map = isl_map_remove_dims(map, isl_dim_in, n_in - 1, 1); + + res = isl_map_union_disjoint(res, map); + } + + isl_map_free(opt); + isl_set_free(min_expr); + isl_mat_free(cst); + return res; +error: + isl_map_free(opt); + isl_set_free(min_expr); + isl_mat_free(cst); + return NULL; +} + +static __isl_give isl_map *basic_map_partial_lexopt( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max); + +union isl_lex_res { + void *p; + isl_map *map; + isl_pw_multi_aff *pma; +}; + +/* This function is called from basic_map_partial_lexopt_symm. + * The last variable of "bmap" and "dom" corresponds to the minimum + * of the bounds in "cst". "map_space" is the space of the original + * input relation (of basic_map_partial_lexopt_symm) and "set_space" + * is the space of the original domain. + * + * We recursively call basic_map_partial_lexopt and then plug in + * the definition of the minimum in the result. + */ +static __isl_give union isl_lex_res basic_map_partial_lexopt_symm_map_core( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max, __isl_take isl_mat *cst, + __isl_take isl_space *map_space, __isl_take isl_space *set_space) +{ + isl_map *opt; + isl_set *min_expr; + union isl_lex_res res; + + min_expr = set_minimum(isl_basic_set_get_space(dom), isl_mat_copy(cst)); + + opt = basic_map_partial_lexopt(bmap, dom, empty, max); + + if (empty) { + *empty = split(*empty, + isl_set_copy(min_expr), isl_mat_copy(cst)); + *empty = isl_set_reset_space(*empty, set_space); + } + + opt = split_domain(opt, min_expr, cst); + opt = isl_map_reset_space(opt, map_space); + + res.map = opt; + return res; +} + +/* Given a basic map with at least two parallel constraints (as found + * by the function parallel_constraints), first look for more constraints + * parallel to the two constraint and replace the found list of parallel + * constraints by a single constraint with as "input" part the minimum + * of the input parts of the list of constraints. Then, recursively call + * basic_map_partial_lexopt (possibly finding more parallel constraints) + * and plug in the definition of the minimum in the result. + * + * More specifically, given a set of constraints + * + * a x + b_i(p) >= 0 + * + * Replace this set by a single constraint + * + * a x + u >= 0 + * + * with u a new parameter with constraints + * + * u <= b_i(p) + * + * Any solution to the new system is also a solution for the original system + * since + * + * a x >= -u >= -b_i(p) + * + * Moreover, m = min_i(b_i(p)) satisfies the constraints on u and can + * therefore be plugged into the solution. + */ +static union isl_lex_res basic_map_partial_lexopt_symm( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max, int first, int second, + __isl_give union isl_lex_res (*core)(__isl_take isl_basic_map *bmap, + __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, + int max, __isl_take isl_mat *cst, + __isl_take isl_space *map_space, + __isl_take isl_space *set_space)) +{ + int i, n, k; + int *list = NULL; + unsigned n_in, n_out, n_div; + isl_ctx *ctx; + isl_vec *var = NULL; + isl_mat *cst = NULL; + isl_space *map_space, *set_space; + union isl_lex_res res; + + map_space = isl_basic_map_get_space(bmap); + set_space = empty ? isl_basic_set_get_space(dom) : NULL; + + n_in = isl_basic_map_dim(bmap, isl_dim_param) + + isl_basic_map_dim(bmap, isl_dim_in); + n_out = isl_basic_map_dim(bmap, isl_dim_all) - n_in; + + ctx = isl_basic_map_get_ctx(bmap); + list = isl_alloc_array(ctx, int, bmap->n_ineq); + var = isl_vec_alloc(ctx, n_out); + if ((bmap->n_ineq && !list) || (n_out && !var)) + goto error; + + list[0] = first; + list[1] = second; + isl_seq_cpy(var->el, bmap->ineq[first] + 1 + n_in, n_out); + for (i = second + 1, n = 2; i < bmap->n_ineq; ++i) { + if (isl_seq_eq(var->el, bmap->ineq[i] + 1 + n_in, n_out)) + list[n++] = i; + } + + cst = isl_mat_alloc(ctx, n, 1 + n_in); + if (!cst) + goto error; + + for (i = 0; i < n; ++i) + isl_seq_cpy(cst->row[i], bmap->ineq[list[i]], 1 + n_in); + + bmap = isl_basic_map_cow(bmap); + if (!bmap) + goto error; + for (i = n - 1; i >= 0; --i) + if (isl_basic_map_drop_inequality(bmap, list[i]) < 0) + goto error; + + bmap = isl_basic_map_add_dims(bmap, isl_dim_in, 1); + bmap = isl_basic_map_extend_constraints(bmap, 0, 1); + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->ineq[k], 1 + n_in); + isl_int_set_si(bmap->ineq[k][1 + n_in], 1); + isl_seq_cpy(bmap->ineq[k] + 1 + n_in + 1, var->el, n_out); + bmap = isl_basic_map_finalize(bmap); + + n_div = isl_basic_set_dim(dom, isl_dim_div); + dom = isl_basic_set_add_dims(dom, isl_dim_set, 1); + dom = isl_basic_set_extend_constraints(dom, 0, n); + for (i = 0; i < n; ++i) { + k = isl_basic_set_alloc_inequality(dom); + if (k < 0) + goto error; + isl_seq_cpy(dom->ineq[k], cst->row[i], 1 + n_in); + isl_int_set_si(dom->ineq[k][1 + n_in], -1); + isl_seq_clr(dom->ineq[k] + 1 + n_in + 1, n_div); + } + + isl_vec_free(var); + free(list); + + return core(bmap, dom, empty, max, cst, map_space, set_space); +error: + isl_space_free(map_space); + isl_space_free(set_space); + isl_mat_free(cst); + isl_vec_free(var); + free(list); + isl_basic_set_free(dom); + isl_basic_map_free(bmap); + res.p = NULL; + return res; +} + +static __isl_give isl_map *basic_map_partial_lexopt_symm_map( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max, int first, int second) +{ + return basic_map_partial_lexopt_symm(bmap, dom, empty, max, + first, second, &basic_map_partial_lexopt_symm_map_core).map; +} + +/* Recursive part of isl_tab_basic_map_partial_lexopt, after detecting + * equalities and removing redundant constraints. + * + * We first check if there are any parallel constraints (left). + * If not, we are in the base case. + * If there are parallel constraints, we replace them by a single + * constraint in basic_map_partial_lexopt_symm and then call + * this function recursively to look for more parallel constraints. + */ +static __isl_give isl_map *basic_map_partial_lexopt( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max) +{ + int par = 0; + int first, second; + + if (!bmap) + goto error; + + if (bmap->ctx->opt->pip_symmetry) + par = parallel_constraints(bmap, &first, &second); + if (par < 0) + goto error; + if (!par) + return basic_map_partial_lexopt_base_map(bmap, dom, empty, max); + + return basic_map_partial_lexopt_symm_map(bmap, dom, empty, max, + first, second); +error: + isl_basic_set_free(dom); + isl_basic_map_free(bmap); + return NULL; +} + +/* Compute the lexicographic minimum (or maximum if "max" is set) + * of "bmap" over the domain "dom" and return the result as a map. + * If "empty" is not NULL, then *empty is assigned a set that + * contains those parts of the domain where there is no solution. + * If "bmap" is marked as rational (ISL_BASIC_MAP_RATIONAL), + * then we compute the rational optimum. Otherwise, we compute + * the integral optimum. + * + * We perform some preprocessing. As the PILP solver does not + * handle implicit equalities very well, we first make sure all + * the equalities are explicitly available. + * + * We also add context constraints to the basic map and remove + * redundant constraints. This is only needed because of the + * way we handle simple symmetries. In particular, we currently look + * for symmetries on the constraints, before we set up the main tableau. + * It is then no good to look for symmetries on possibly redundant constraints. + */ +struct isl_map *isl_tab_basic_map_partial_lexopt( + struct isl_basic_map *bmap, struct isl_basic_set *dom, + struct isl_set **empty, int max) +{ + if (empty) + *empty = NULL; + if (!bmap || !dom) + goto error; + + isl_assert(bmap->ctx, + isl_basic_map_compatible_domain(bmap, dom), goto error); + + if (isl_basic_set_dim(dom, isl_dim_all) == 0) + return basic_map_partial_lexopt(bmap, dom, empty, max); + + bmap = isl_basic_map_intersect_domain(bmap, isl_basic_set_copy(dom)); + bmap = isl_basic_map_detect_equalities(bmap); + bmap = isl_basic_map_remove_redundancies(bmap); + + return basic_map_partial_lexopt(bmap, dom, empty, max); +error: + isl_basic_set_free(dom); + isl_basic_map_free(bmap); + return NULL; +} + +struct isl_sol_for { + struct isl_sol sol; + int (*fn)(__isl_take isl_basic_set *dom, + __isl_take isl_aff_list *list, void *user); + void *user; +}; + +static void sol_for_free(struct isl_sol_for *sol_for) +{ + if (!sol_for) + return; + if (sol_for->sol.context) + sol_for->sol.context->op->free(sol_for->sol.context); + free(sol_for); +} + +static void sol_for_free_wrap(struct isl_sol *sol) +{ + sol_for_free((struct isl_sol_for *)sol); +} + +/* Add the solution identified by the tableau and the context tableau. + * + * See documentation of sol_add for more details. + * + * Instead of constructing a basic map, this function calls a user + * defined function with the current context as a basic set and + * a list of affine expressions representing the relation between + * the input and output. The space over which the affine expressions + * are defined is the same as that of the domain. The number of + * affine expressions in the list is equal to the number of output variables. + */ +static void sol_for_add(struct isl_sol_for *sol, + struct isl_basic_set *dom, struct isl_mat *M) +{ + int i; + isl_ctx *ctx; + isl_local_space *ls; + isl_aff *aff; + isl_aff_list *list; + + if (sol->sol.error || !dom || !M) + goto error; + + ctx = isl_basic_set_get_ctx(dom); + ls = isl_basic_set_get_local_space(dom); + list = isl_aff_list_alloc(ctx, M->n_row - 1); + for (i = 1; i < M->n_row; ++i) { + aff = isl_aff_alloc(isl_local_space_copy(ls)); + if (aff) { + isl_int_set(aff->v->el[0], M->row[0][0]); + isl_seq_cpy(aff->v->el + 1, M->row[i], M->n_col); + } + aff = isl_aff_normalize(aff); + list = isl_aff_list_add(list, aff); + } + isl_local_space_free(ls); + + dom = isl_basic_set_finalize(dom); + + if (sol->fn(isl_basic_set_copy(dom), list, sol->user) < 0) + goto error; + + isl_basic_set_free(dom); + isl_mat_free(M); + return; +error: + isl_basic_set_free(dom); + isl_mat_free(M); + sol->sol.error = 1; +} + +static void sol_for_add_wrap(struct isl_sol *sol, + struct isl_basic_set *dom, struct isl_mat *M) +{ + sol_for_add((struct isl_sol_for *)sol, dom, M); +} + +static struct isl_sol_for *sol_for_init(struct isl_basic_map *bmap, int max, + int (*fn)(__isl_take isl_basic_set *dom, __isl_take isl_aff_list *list, + void *user), + void *user) +{ + struct isl_sol_for *sol_for = NULL; + isl_space *dom_dim; + struct isl_basic_set *dom = NULL; + + sol_for = isl_calloc_type(bmap->ctx, struct isl_sol_for); + if (!sol_for) + goto error; + + dom_dim = isl_space_domain(isl_space_copy(bmap->dim)); + dom = isl_basic_set_universe(dom_dim); + + sol_for->sol.rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + sol_for->sol.dec_level.callback.run = &sol_dec_level_wrap; + sol_for->sol.dec_level.sol = &sol_for->sol; + sol_for->fn = fn; + sol_for->user = user; + sol_for->sol.max = max; + sol_for->sol.n_out = isl_basic_map_dim(bmap, isl_dim_out); + sol_for->sol.add = &sol_for_add_wrap; + sol_for->sol.add_empty = NULL; + sol_for->sol.free = &sol_for_free_wrap; + + sol_for->sol.context = isl_context_alloc(dom); + if (!sol_for->sol.context) + goto error; + + isl_basic_set_free(dom); + return sol_for; +error: + isl_basic_set_free(dom); + sol_for_free(sol_for); + return NULL; +} + +static void sol_for_find_solutions(struct isl_sol_for *sol_for, + struct isl_tab *tab) +{ + find_solutions_main(&sol_for->sol, tab); +} + +int isl_basic_map_foreach_lexopt(__isl_keep isl_basic_map *bmap, int max, + int (*fn)(__isl_take isl_basic_set *dom, __isl_take isl_aff_list *list, + void *user), + void *user) +{ + struct isl_sol_for *sol_for = NULL; + + bmap = isl_basic_map_copy(bmap); + bmap = isl_basic_map_detect_equalities(bmap); + if (!bmap) + return -1; + + sol_for = sol_for_init(bmap, max, fn, user); + if (!sol_for) + goto error; + + if (isl_basic_map_plain_is_empty(bmap)) + /* nothing */; + else { + struct isl_tab *tab; + struct isl_context *context = sol_for->sol.context; + tab = tab_for_lexmin(bmap, + context->op->peek_basic_set(context), 1, max); + tab = context->op->detect_nonnegative_parameters(context, tab); + sol_for_find_solutions(sol_for, tab); + if (sol_for->sol.error) + goto error; + } + + sol_free(&sol_for->sol); + isl_basic_map_free(bmap); + return 0; +error: + sol_free(&sol_for->sol); + isl_basic_map_free(bmap); + return -1; +} + +int isl_basic_set_foreach_lexopt(__isl_keep isl_basic_set *bset, int max, + int (*fn)(__isl_take isl_basic_set *dom, __isl_take isl_aff_list *list, + void *user), + void *user) +{ + return isl_basic_map_foreach_lexopt(bset, max, fn, user); +} + +/* Check if the given sequence of len variables starting at pos + * represents a trivial (i.e., zero) solution. + * The variables are assumed to be non-negative and to come in pairs, + * with each pair representing a variable of unrestricted sign. + * The solution is trivial if each such pair in the sequence consists + * of two identical values, meaning that the variable being represented + * has value zero. + */ +static int region_is_trivial(struct isl_tab *tab, int pos, int len) +{ + int i; + + if (len == 0) + return 0; + + for (i = 0; i < len; i += 2) { + int neg_row; + int pos_row; + + neg_row = tab->var[pos + i].is_row ? + tab->var[pos + i].index : -1; + pos_row = tab->var[pos + i + 1].is_row ? + tab->var[pos + i + 1].index : -1; + + if ((neg_row < 0 || + isl_int_is_zero(tab->mat->row[neg_row][1])) && + (pos_row < 0 || + isl_int_is_zero(tab->mat->row[pos_row][1]))) + continue; + + if (neg_row < 0 || pos_row < 0) + return 0; + if (isl_int_ne(tab->mat->row[neg_row][1], + tab->mat->row[pos_row][1])) + return 0; + } + + return 1; +} + +/* Return the index of the first trivial region or -1 if all regions + * are non-trivial. + */ +static int first_trivial_region(struct isl_tab *tab, + int n_region, struct isl_region *region) +{ + int i; + + for (i = 0; i < n_region; ++i) { + if (region_is_trivial(tab, region[i].pos, region[i].len)) + return i; + } + + return -1; +} + +/* Check if the solution is optimal, i.e., whether the first + * n_op entries are zero. + */ +static int is_optimal(__isl_keep isl_vec *sol, int n_op) +{ + int i; + + for (i = 0; i < n_op; ++i) + if (!isl_int_is_zero(sol->el[1 + i])) + return 0; + return 1; +} + +/* Add constraints to "tab" that ensure that any solution is significantly + * better than that represented by "sol". That is, find the first + * relevant (within first n_op) non-zero coefficient and force it (along + * with all previous coefficients) to be zero. + * If the solution is already optimal (all relevant coefficients are zero), + * then just mark the table as empty. + * + * This function assumes that at least 2 * n_op more rows and at least + * 2 * n_op more elements in the constraint array are available in the tableau. + */ +static int force_better_solution(struct isl_tab *tab, + __isl_keep isl_vec *sol, int n_op) +{ + int i; + isl_ctx *ctx; + isl_vec *v = NULL; + + if (!sol) + return -1; + + for (i = 0; i < n_op; ++i) + if (!isl_int_is_zero(sol->el[1 + i])) + break; + + if (i == n_op) { + if (isl_tab_mark_empty(tab) < 0) + return -1; + return 0; + } + + ctx = isl_vec_get_ctx(sol); + v = isl_vec_alloc(ctx, 1 + tab->n_var); + if (!v) + return -1; + + for (; i >= 0; --i) { + v = isl_vec_clr(v); + isl_int_set_si(v->el[1 + i], -1); + if (add_lexmin_eq(tab, v->el) < 0) + goto error; + } + + isl_vec_free(v); + return 0; +error: + isl_vec_free(v); + return -1; +} + +struct isl_trivial { + int update; + int region; + int side; + struct isl_tab_undo *snap; +}; + +/* Return the lexicographically smallest non-trivial solution of the + * given ILP problem. + * + * All variables are assumed to be non-negative. + * + * n_op is the number of initial coordinates to optimize. + * That is, once a solution has been found, we will only continue looking + * for solution that result in significantly better values for those + * initial coordinates. That is, we only continue looking for solutions + * that increase the number of initial zeros in this sequence. + * + * A solution is non-trivial, if it is non-trivial on each of the + * specified regions. Each region represents a sequence of pairs + * of variables. A solution is non-trivial on such a region if + * at least one of these pairs consists of different values, i.e., + * such that the non-negative variable represented by the pair is non-zero. + * + * Whenever a conflict is encountered, all constraints involved are + * reported to the caller through a call to "conflict". + * + * We perform a simple branch-and-bound backtracking search. + * Each level in the search represents initially trivial region that is forced + * to be non-trivial. + * At each level we consider n cases, where n is the length of the region. + * In terms of the n/2 variables of unrestricted signs being encoded by + * the region, we consider the cases + * x_0 >= 1 + * x_0 <= -1 + * x_0 = 0 and x_1 >= 1 + * x_0 = 0 and x_1 <= -1 + * x_0 = 0 and x_1 = 0 and x_2 >= 1 + * x_0 = 0 and x_1 = 0 and x_2 <= -1 + * ... + * The cases are considered in this order, assuming that each pair + * x_i_a x_i_b represents the value x_i_b - x_i_a. + * That is, x_0 >= 1 is enforced by adding the constraint + * x_0_b - x_0_a >= 1 + */ +__isl_give isl_vec *isl_tab_basic_set_non_trivial_lexmin( + __isl_take isl_basic_set *bset, int n_op, int n_region, + struct isl_region *region, + int (*conflict)(int con, void *user), void *user) +{ + int i, j; + int r; + isl_ctx *ctx; + isl_vec *v = NULL; + isl_vec *sol = NULL; + struct isl_tab *tab; + struct isl_trivial *triv = NULL; + int level, init; + + if (!bset) + return NULL; + + ctx = isl_basic_set_get_ctx(bset); + sol = isl_vec_alloc(ctx, 0); + + tab = tab_for_lexmin(bset, NULL, 0, 0); + if (!tab) + goto error; + tab->conflict = conflict; + tab->conflict_user = user; + + v = isl_vec_alloc(ctx, 1 + tab->n_var); + triv = isl_calloc_array(ctx, struct isl_trivial, n_region); + if (!v || (n_region && !triv)) + goto error; + + level = 0; + init = 1; + + while (level >= 0) { + int side, base; + + if (init) { + tab = cut_to_integer_lexmin(tab, CUT_ONE); + if (!tab) + goto error; + if (tab->empty) + goto backtrack; + r = first_trivial_region(tab, n_region, region); + if (r < 0) { + for (i = 0; i < level; ++i) + triv[i].update = 1; + isl_vec_free(sol); + sol = isl_tab_get_sample_value(tab); + if (!sol) + goto error; + if (is_optimal(sol, n_op)) + break; + goto backtrack; + } + if (level >= n_region) + isl_die(ctx, isl_error_internal, + "nesting level too deep", goto error); + if (isl_tab_extend_cons(tab, + 2 * region[r].len + 2 * n_op) < 0) + goto error; + triv[level].region = r; + triv[level].side = 0; + } + + r = triv[level].region; + side = triv[level].side; + base = 2 * (side/2); + + if (side >= region[r].len) { +backtrack: + level--; + init = 0; + if (level >= 0) + if (isl_tab_rollback(tab, triv[level].snap) < 0) + goto error; + continue; + } + + if (triv[level].update) { + if (force_better_solution(tab, sol, n_op) < 0) + goto error; + triv[level].update = 0; + } + + if (side == base && base >= 2) { + for (j = base - 2; j < base; ++j) { + v = isl_vec_clr(v); + isl_int_set_si(v->el[1 + region[r].pos + j], 1); + if (add_lexmin_eq(tab, v->el) < 0) + goto error; + } + } + + triv[level].snap = isl_tab_snap(tab); + if (isl_tab_push_basis(tab) < 0) + goto error; + + v = isl_vec_clr(v); + isl_int_set_si(v->el[0], -1); + isl_int_set_si(v->el[1 + region[r].pos + side], -1); + isl_int_set_si(v->el[1 + region[r].pos + (side ^ 1)], 1); + tab = add_lexmin_ineq(tab, v->el); + + triv[level].side++; + level++; + init = 1; + } + + free(triv); + isl_vec_free(v); + isl_tab_free(tab); + isl_basic_set_free(bset); + + return sol; +error: + free(triv); + isl_vec_free(v); + isl_tab_free(tab); + isl_basic_set_free(bset); + isl_vec_free(sol); + return NULL; +} + +/* Wrapper for a tableau that is used for computing + * the lexicographically smallest rational point of a non-negative set. + * This point is represented by the sample value of "tab", + * unless "tab" is empty. + */ +struct isl_tab_lexmin { + isl_ctx *ctx; + struct isl_tab *tab; +}; + +/* Free "tl" and return NULL. + */ +__isl_null isl_tab_lexmin *isl_tab_lexmin_free(__isl_take isl_tab_lexmin *tl) +{ + if (!tl) + return NULL; + isl_ctx_deref(tl->ctx); + isl_tab_free(tl->tab); + free(tl); + + return NULL; +} + +/* Construct an isl_tab_lexmin for computing + * the lexicographically smallest rational point in "bset", + * assuming that all variables are non-negative. + */ +__isl_give isl_tab_lexmin *isl_tab_lexmin_from_basic_set( + __isl_take isl_basic_set *bset) +{ + isl_ctx *ctx; + isl_tab_lexmin *tl; + + if (!bset) + return NULL; + + ctx = isl_basic_set_get_ctx(bset); + tl = isl_calloc_type(ctx, struct isl_tab_lexmin); + if (!tl) + goto error; + tl->ctx = ctx; + isl_ctx_ref(ctx); + tl->tab = tab_for_lexmin(bset, NULL, 0, 0); + isl_basic_set_free(bset); + if (!tl->tab) + return isl_tab_lexmin_free(tl); + return tl; +error: + isl_basic_set_free(bset); + isl_tab_lexmin_free(tl); + return NULL; +} + +/* Return the dimension of the set represented by "tl". + */ +int isl_tab_lexmin_dim(__isl_keep isl_tab_lexmin *tl) +{ + return tl ? tl->tab->n_var : -1; +} + +/* Add the equality with coefficients "eq" to "tl", updating the optimal + * solution if needed. + * The equality is added as two opposite inequality constraints. + */ +__isl_give isl_tab_lexmin *isl_tab_lexmin_add_eq(__isl_take isl_tab_lexmin *tl, + isl_int *eq) +{ + unsigned n_var; + + if (!tl || !eq) + return isl_tab_lexmin_free(tl); + + if (isl_tab_extend_cons(tl->tab, 2) < 0) + return isl_tab_lexmin_free(tl); + n_var = tl->tab->n_var; + isl_seq_neg(eq, eq, 1 + n_var); + tl->tab = add_lexmin_ineq(tl->tab, eq); + isl_seq_neg(eq, eq, 1 + n_var); + tl->tab = add_lexmin_ineq(tl->tab, eq); + + if (!tl->tab) + return isl_tab_lexmin_free(tl); + + return tl; +} + +/* Return the lexicographically smallest rational point in the basic set + * from which "tl" was constructed. + * If the original input was empty, then return a zero-length vector. + */ +__isl_give isl_vec *isl_tab_lexmin_get_solution(__isl_keep isl_tab_lexmin *tl) +{ + if (!tl) + return NULL; + if (tl->tab->empty) + return isl_vec_alloc(tl->ctx, 0); + else + return isl_tab_get_sample_value(tl->tab); +} + +/* Return the lexicographically smallest rational point in "bset", + * assuming that all variables are non-negative. + * If "bset" is empty, then return a zero-length vector. + */ +__isl_give isl_vec *isl_tab_basic_set_non_neg_lexmin( + __isl_take isl_basic_set *bset) +{ + isl_tab_lexmin *tl; + isl_vec *sol; + + tl = isl_tab_lexmin_from_basic_set(bset); + sol = isl_tab_lexmin_get_solution(tl); + isl_tab_lexmin_free(tl); + return sol; +} + +struct isl_sol_pma { + struct isl_sol sol; + isl_pw_multi_aff *pma; + isl_set *empty; +}; + +static void sol_pma_free(struct isl_sol_pma *sol_pma) +{ + if (!sol_pma) + return; + if (sol_pma->sol.context) + sol_pma->sol.context->op->free(sol_pma->sol.context); + isl_pw_multi_aff_free(sol_pma->pma); + isl_set_free(sol_pma->empty); + free(sol_pma); +} + +/* This function is called for parts of the context where there is + * no solution, with "bset" corresponding to the context tableau. + * Simply add the basic set to the set "empty". + */ +static void sol_pma_add_empty(struct isl_sol_pma *sol, + __isl_take isl_basic_set *bset) +{ + if (!bset || !sol->empty) + goto error; + + sol->empty = isl_set_grow(sol->empty, 1); + bset = isl_basic_set_simplify(bset); + bset = isl_basic_set_finalize(bset); + sol->empty = isl_set_add_basic_set(sol->empty, bset); + if (!sol->empty) + sol->sol.error = 1; + return; +error: + isl_basic_set_free(bset); + sol->sol.error = 1; +} + +/* Given a basic map "dom" that represents the context and an affine + * matrix "M" that maps the dimensions of the context to the + * output variables, construct an isl_pw_multi_aff with a single + * cell corresponding to "dom" and affine expressions copied from "M". + */ +static void sol_pma_add(struct isl_sol_pma *sol, + __isl_take isl_basic_set *dom, __isl_take isl_mat *M) +{ + int i; + isl_local_space *ls; + isl_aff *aff; + isl_multi_aff *maff; + isl_pw_multi_aff *pma; + + maff = isl_multi_aff_alloc(isl_pw_multi_aff_get_space(sol->pma)); + ls = isl_basic_set_get_local_space(dom); + for (i = 1; i < M->n_row; ++i) { + aff = isl_aff_alloc(isl_local_space_copy(ls)); + if (aff) { + isl_int_set(aff->v->el[0], M->row[0][0]); + isl_seq_cpy(aff->v->el + 1, M->row[i], M->n_col); + } + aff = isl_aff_normalize(aff); + maff = isl_multi_aff_set_aff(maff, i - 1, aff); + } + isl_local_space_free(ls); + isl_mat_free(M); + dom = isl_basic_set_simplify(dom); + dom = isl_basic_set_finalize(dom); + pma = isl_pw_multi_aff_alloc(isl_set_from_basic_set(dom), maff); + sol->pma = isl_pw_multi_aff_add_disjoint(sol->pma, pma); + if (!sol->pma) + sol->sol.error = 1; +} + +static void sol_pma_free_wrap(struct isl_sol *sol) +{ + sol_pma_free((struct isl_sol_pma *)sol); +} + +static void sol_pma_add_empty_wrap(struct isl_sol *sol, + __isl_take isl_basic_set *bset) +{ + sol_pma_add_empty((struct isl_sol_pma *)sol, bset); +} + +static void sol_pma_add_wrap(struct isl_sol *sol, + __isl_take isl_basic_set *dom, __isl_take isl_mat *M) +{ + sol_pma_add((struct isl_sol_pma *)sol, dom, M); +} + +/* Construct an isl_sol_pma structure for accumulating the solution. + * If track_empty is set, then we also keep track of the parts + * of the context where there is no solution. + * If max is set, then we are solving a maximization, rather than + * a minimization problem, which means that the variables in the + * tableau have value "M - x" rather than "M + x". + */ +static struct isl_sol *sol_pma_init(__isl_keep isl_basic_map *bmap, + __isl_take isl_basic_set *dom, int track_empty, int max) +{ + struct isl_sol_pma *sol_pma = NULL; + + if (!bmap) + goto error; + + sol_pma = isl_calloc_type(bmap->ctx, struct isl_sol_pma); + if (!sol_pma) + goto error; + + sol_pma->sol.rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL); + sol_pma->sol.dec_level.callback.run = &sol_dec_level_wrap; + sol_pma->sol.dec_level.sol = &sol_pma->sol; + sol_pma->sol.max = max; + sol_pma->sol.n_out = isl_basic_map_dim(bmap, isl_dim_out); + sol_pma->sol.add = &sol_pma_add_wrap; + sol_pma->sol.add_empty = track_empty ? &sol_pma_add_empty_wrap : NULL; + sol_pma->sol.free = &sol_pma_free_wrap; + sol_pma->pma = isl_pw_multi_aff_empty(isl_basic_map_get_space(bmap)); + if (!sol_pma->pma) + goto error; + + sol_pma->sol.context = isl_context_alloc(dom); + if (!sol_pma->sol.context) + goto error; + + if (track_empty) { + sol_pma->empty = isl_set_alloc_space(isl_basic_set_get_space(dom), + 1, ISL_SET_DISJOINT); + if (!sol_pma->empty) + goto error; + } + + isl_basic_set_free(dom); + return &sol_pma->sol; +error: + isl_basic_set_free(dom); + sol_pma_free(sol_pma); + return NULL; +} + +/* Base case of isl_tab_basic_map_partial_lexopt, after removing + * some obvious symmetries. + * + * We call basic_map_partial_lexopt_base and extract the results. + */ +static __isl_give isl_pw_multi_aff *basic_map_partial_lexopt_base_pma( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max) +{ + isl_pw_multi_aff *result = NULL; + struct isl_sol *sol; + struct isl_sol_pma *sol_pma; + + sol = basic_map_partial_lexopt_base(bmap, dom, empty, max, + &sol_pma_init); + if (!sol) + return NULL; + sol_pma = (struct isl_sol_pma *) sol; + + result = isl_pw_multi_aff_copy(sol_pma->pma); + if (empty) + *empty = isl_set_copy(sol_pma->empty); + sol_free(&sol_pma->sol); + return result; +} + +/* Given that the last input variable of "maff" represents the minimum + * of some bounds, check whether we need to plug in the expression + * of the minimum. + * + * In particular, check if the last input variable appears in any + * of the expressions in "maff". + */ +static int need_substitution(__isl_keep isl_multi_aff *maff) +{ + int i; + unsigned pos; + + pos = isl_multi_aff_dim(maff, isl_dim_in) - 1; + + for (i = 0; i < maff->n; ++i) + if (isl_aff_involves_dims(maff->p[i], isl_dim_in, pos, 1)) + return 1; + + return 0; +} + +/* Given a set of upper bounds on the last "input" variable m, + * construct a piecewise affine expression that selects + * the minimal upper bound to m, i.e., + * divide the space into cells where one + * of the upper bounds is smaller than all the others and select + * this upper bound on that cell. + * + * In particular, if there are n bounds b_i, then the result + * consists of n cell, each one of the form + * + * b_i <= b_j for j > i + * b_i < b_j for j < i + * + * The affine expression on this cell is + * + * b_i + */ +static __isl_give isl_pw_aff *set_minimum_pa(__isl_take isl_space *space, + __isl_take isl_mat *var) +{ + int i; + isl_aff *aff = NULL; + isl_basic_set *bset = NULL; + isl_pw_aff *paff = NULL; + isl_space *pw_space; + isl_local_space *ls = NULL; + + if (!space || !var) + goto error; + + ls = isl_local_space_from_space(isl_space_copy(space)); + pw_space = isl_space_copy(space); + pw_space = isl_space_from_domain(pw_space); + pw_space = isl_space_add_dims(pw_space, isl_dim_out, 1); + paff = isl_pw_aff_alloc_size(pw_space, var->n_row); + + for (i = 0; i < var->n_row; ++i) { + isl_pw_aff *paff_i; + + aff = isl_aff_alloc(isl_local_space_copy(ls)); + bset = isl_basic_set_alloc_space(isl_space_copy(space), 0, + 0, var->n_row - 1); + if (!aff || !bset) + goto error; + isl_int_set_si(aff->v->el[0], 1); + isl_seq_cpy(aff->v->el + 1, var->row[i], var->n_col); + isl_int_set_si(aff->v->el[1 + var->n_col], 0); + bset = select_minimum(bset, var, i); + paff_i = isl_pw_aff_alloc(isl_set_from_basic_set(bset), aff); + paff = isl_pw_aff_add_disjoint(paff, paff_i); + } + + isl_local_space_free(ls); + isl_space_free(space); + isl_mat_free(var); + return paff; +error: + isl_aff_free(aff); + isl_basic_set_free(bset); + isl_pw_aff_free(paff); + isl_local_space_free(ls); + isl_space_free(space); + isl_mat_free(var); + return NULL; +} + +/* Given a piecewise multi-affine expression of which the last input variable + * is the minimum of the bounds in "cst", plug in the value of the minimum. + * This minimum expression is given in "min_expr_pa". + * The set "min_expr" contains the same information, but in the form of a set. + * The variable is subsequently projected out. + * + * The implementation is similar to those of "split" and "split_domain". + * If the variable appears in a given expression, then minimum expression + * is plugged in. Otherwise, if the variable appears in the constraints + * and a split is required, then the domain is split. Otherwise, no split + * is performed. + */ +static __isl_give isl_pw_multi_aff *split_domain_pma( + __isl_take isl_pw_multi_aff *opt, __isl_take isl_pw_aff *min_expr_pa, + __isl_take isl_set *min_expr, __isl_take isl_mat *cst) +{ + int n_in; + int i; + isl_space *space; + isl_pw_multi_aff *res; + + if (!opt || !min_expr || !cst) + goto error; + + n_in = isl_pw_multi_aff_dim(opt, isl_dim_in); + space = isl_pw_multi_aff_get_space(opt); + space = isl_space_drop_dims(space, isl_dim_in, n_in - 1, 1); + res = isl_pw_multi_aff_empty(space); + + for (i = 0; i < opt->n; ++i) { + isl_pw_multi_aff *pma; + + pma = isl_pw_multi_aff_alloc(isl_set_copy(opt->p[i].set), + isl_multi_aff_copy(opt->p[i].maff)); + if (need_substitution(opt->p[i].maff)) + pma = isl_pw_multi_aff_substitute(pma, + isl_dim_in, n_in - 1, min_expr_pa); + else if (need_split_set(opt->p[i].set, cst)) + pma = isl_pw_multi_aff_intersect_domain(pma, + isl_set_copy(min_expr)); + pma = isl_pw_multi_aff_project_out(pma, + isl_dim_in, n_in - 1, 1); + + res = isl_pw_multi_aff_add_disjoint(res, pma); + } + + isl_pw_multi_aff_free(opt); + isl_pw_aff_free(min_expr_pa); + isl_set_free(min_expr); + isl_mat_free(cst); + return res; +error: + isl_pw_multi_aff_free(opt); + isl_pw_aff_free(min_expr_pa); + isl_set_free(min_expr); + isl_mat_free(cst); + return NULL; +} + +static __isl_give isl_pw_multi_aff *basic_map_partial_lexopt_pma( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max); + +/* This function is called from basic_map_partial_lexopt_symm. + * The last variable of "bmap" and "dom" corresponds to the minimum + * of the bounds in "cst". "map_space" is the space of the original + * input relation (of basic_map_partial_lexopt_symm) and "set_space" + * is the space of the original domain. + * + * We recursively call basic_map_partial_lexopt and then plug in + * the definition of the minimum in the result. + */ +static __isl_give union isl_lex_res basic_map_partial_lexopt_symm_pma_core( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max, __isl_take isl_mat *cst, + __isl_take isl_space *map_space, __isl_take isl_space *set_space) +{ + isl_pw_multi_aff *opt; + isl_pw_aff *min_expr_pa; + isl_set *min_expr; + union isl_lex_res res; + + min_expr = set_minimum(isl_basic_set_get_space(dom), isl_mat_copy(cst)); + min_expr_pa = set_minimum_pa(isl_basic_set_get_space(dom), + isl_mat_copy(cst)); + + opt = basic_map_partial_lexopt_pma(bmap, dom, empty, max); + + if (empty) { + *empty = split(*empty, + isl_set_copy(min_expr), isl_mat_copy(cst)); + *empty = isl_set_reset_space(*empty, set_space); + } + + opt = split_domain_pma(opt, min_expr_pa, min_expr, cst); + opt = isl_pw_multi_aff_reset_space(opt, map_space); + + res.pma = opt; + return res; +} + +static __isl_give isl_pw_multi_aff *basic_map_partial_lexopt_symm_pma( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max, int first, int second) +{ + return basic_map_partial_lexopt_symm(bmap, dom, empty, max, + first, second, &basic_map_partial_lexopt_symm_pma_core).pma; +} + +/* Recursive part of isl_basic_map_partial_lexopt_pw_multi_aff, after detecting + * equalities and removing redundant constraints. + * + * We first check if there are any parallel constraints (left). + * If not, we are in the base case. + * If there are parallel constraints, we replace them by a single + * constraint in basic_map_partial_lexopt_symm_pma and then call + * this function recursively to look for more parallel constraints. + */ +static __isl_give isl_pw_multi_aff *basic_map_partial_lexopt_pma( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max) +{ + int par = 0; + int first, second; + + if (!bmap) + goto error; + + if (bmap->ctx->opt->pip_symmetry) + par = parallel_constraints(bmap, &first, &second); + if (par < 0) + goto error; + if (!par) + return basic_map_partial_lexopt_base_pma(bmap, dom, empty, max); + + return basic_map_partial_lexopt_symm_pma(bmap, dom, empty, max, + first, second); +error: + isl_basic_set_free(dom); + isl_basic_map_free(bmap); + return NULL; +} + +/* Compute the lexicographic minimum (or maximum if "max" is set) + * of "bmap" over the domain "dom" and return the result as a piecewise + * multi-affine expression. + * If "empty" is not NULL, then *empty is assigned a set that + * contains those parts of the domain where there is no solution. + * If "bmap" is marked as rational (ISL_BASIC_MAP_RATIONAL), + * then we compute the rational optimum. Otherwise, we compute + * the integral optimum. + * + * We perform some preprocessing. As the PILP solver does not + * handle implicit equalities very well, we first make sure all + * the equalities are explicitly available. + * + * We also add context constraints to the basic map and remove + * redundant constraints. This is only needed because of the + * way we handle simple symmetries. In particular, we currently look + * for symmetries on the constraints, before we set up the main tableau. + * It is then no good to look for symmetries on possibly redundant constraints. + */ +__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexopt_pw_multi_aff( + __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom, + __isl_give isl_set **empty, int max) +{ + if (empty) + *empty = NULL; + if (!bmap || !dom) + goto error; + + isl_assert(bmap->ctx, + isl_basic_map_compatible_domain(bmap, dom), goto error); + + if (isl_basic_set_dim(dom, isl_dim_all) == 0) + return basic_map_partial_lexopt_pma(bmap, dom, empty, max); + + bmap = isl_basic_map_intersect_domain(bmap, isl_basic_set_copy(dom)); + bmap = isl_basic_map_detect_equalities(bmap); + bmap = isl_basic_map_remove_redundancies(bmap); + + return basic_map_partial_lexopt_pma(bmap, dom, empty, max); +error: + isl_basic_set_free(dom); + isl_basic_map_free(bmap); + return NULL; +} Index: lib/Analysis/isl/isl_tarjan.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_tarjan.h @@ -0,0 +1,42 @@ +#ifndef ISL_TARJAN_H +#define ISL_TARJAN_H + +/* Structure for representing the nodes in the graph being traversed + * using Tarjan's algorithm. + * index represents the order in which nodes are visited. + * min_index is the index of the root of a (sub)component. + * on_stack indicates whether the node is currently on the stack. + */ +struct isl_tarjan_node { + int index; + int min_index; + int on_stack; +}; + +/* Structure for representing the graph being traversed + * using Tarjan's algorithm. + * len is the number of nodes + * node is an array of nodes + * stack contains the nodes on the path from the root to the current node + * sp is the stack pointer + * index is the index of the last node visited + * order contains the elements of the components separated by -1 + * op represents the current position in order + */ +struct isl_tarjan_graph { + int len; + struct isl_tarjan_node *node; + int *stack; + int sp; + int index; + int *order; + int op; +}; + +struct isl_tarjan_graph *isl_tarjan_graph_init(isl_ctx *ctx, int len, + isl_bool (*follows)(int i, int j, void *user), void *user); +struct isl_tarjan_graph *isl_tarjan_graph_component(isl_ctx *ctx, int len, + int node, isl_bool (*follows)(int i, int j, void *user), void *user); +struct isl_tarjan_graph *isl_tarjan_graph_free(struct isl_tarjan_graph *g); + +#endif Index: lib/Analysis/isl/isl_tarjan.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_tarjan.c @@ -0,0 +1,159 @@ +/* + * Copyright 2010-2011 INRIA Saclay + * Copyright 2012 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include + +struct isl_tarjan_graph *isl_tarjan_graph_free(struct isl_tarjan_graph *g) +{ + if (!g) + return NULL; + free(g->node); + free(g->stack); + free(g->order); + free(g); + return NULL; +} + +static struct isl_tarjan_graph *isl_tarjan_graph_alloc(isl_ctx *ctx, int len) +{ + struct isl_tarjan_graph *g; + int i; + + g = isl_calloc_type(ctx, struct isl_tarjan_graph); + if (!g) + return NULL; + g->len = len; + g->node = isl_alloc_array(ctx, struct isl_tarjan_node, len); + if (len && !g->node) + goto error; + for (i = 0; i < len; ++i) + g->node[i].index = -1; + g->stack = isl_alloc_array(ctx, int, len); + if (len && !g->stack) + goto error; + g->order = isl_alloc_array(ctx, int, 2 * len); + if (len && !g->order) + goto error; + + g->sp = 0; + g->index = 0; + g->op = 0; + + return g; +error: + isl_tarjan_graph_free(g); + return NULL; +} + +/* Perform Tarjan's algorithm for computing the strongly connected components + * in the graph with g->len nodes and with edges defined by "follows". + */ +static isl_stat isl_tarjan_components(struct isl_tarjan_graph *g, int i, + isl_bool (*follows)(int i, int j, void *user), void *user) +{ + int j; + + g->node[i].index = g->index; + g->node[i].min_index = g->index; + g->node[i].on_stack = 1; + g->index++; + g->stack[g->sp++] = i; + + for (j = g->len - 1; j >= 0; --j) { + isl_bool f; + + if (j == i) + continue; + if (g->node[j].index >= 0 && + (!g->node[j].on_stack || + g->node[j].index > g->node[i].min_index)) + continue; + + f = follows(i, j, user); + if (f < 0) + return isl_stat_error; + if (!f) + continue; + + if (g->node[j].index < 0) { + isl_tarjan_components(g, j, follows, user); + if (g->node[j].min_index < g->node[i].min_index) + g->node[i].min_index = g->node[j].min_index; + } else if (g->node[j].index < g->node[i].min_index) + g->node[i].min_index = g->node[j].index; + } + + if (g->node[i].index != g->node[i].min_index) + return isl_stat_ok; + + do { + j = g->stack[--g->sp]; + g->node[j].on_stack = 0; + g->order[g->op++] = j; + } while (j != i); + g->order[g->op++] = -1; + + return isl_stat_ok; +} + +/* Decompose the graph with "len" nodes and edges defined by "follows" + * into strongly connected components (SCCs). + * follows(i, j, user) should return 1 if "i" follows "j" and 0 otherwise. + * It should return -1 on error. + * + * If SCC a contains a node i that follows a node j in another SCC b + * (i.e., follows(i, j, user) returns 1), then SCC a will appear after SCC b + * in the result. + */ +struct isl_tarjan_graph *isl_tarjan_graph_init(isl_ctx *ctx, int len, + isl_bool (*follows)(int i, int j, void *user), void *user) +{ + int i; + struct isl_tarjan_graph *g = NULL; + + g = isl_tarjan_graph_alloc(ctx, len); + if (!g) + return NULL; + for (i = len - 1; i >= 0; --i) { + if (g->node[i].index >= 0) + continue; + if (isl_tarjan_components(g, i, follows, user) < 0) + return isl_tarjan_graph_free(g); + } + + return g; +} + +/* Decompose the graph with "len" nodes and edges defined by "follows" + * into the strongly connected component (SCC) that contains "node" + * as well as all SCCs that are followed by this SCC. + * follows(i, j, user) should return 1 if "i" follows "j" and 0 otherwise. + * It should return -1 on error. + * + * The SCC containing "node" will appear as the last component + * in g->order. + */ +struct isl_tarjan_graph *isl_tarjan_graph_component(isl_ctx *ctx, int len, + int node, isl_bool (*follows)(int i, int j, void *user), void *user) +{ + struct isl_tarjan_graph *g; + + g = isl_tarjan_graph_alloc(ctx, len); + if (!g) + return NULL; + if (isl_tarjan_components(g, node, follows, user) < 0) + return isl_tarjan_graph_free(g); + + return g; +} Index: lib/Analysis/isl/isl_test.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_test.c @@ -0,0 +1,6634 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2010 INRIA Saclay + * Copyright 2012-2013 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, + * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array)) + +static char *srcdir; + +static char *get_filename(isl_ctx *ctx, const char *name, const char *suffix) { + char *filename; + int length; + char *pattern = "%s/test_inputs/%s.%s"; + + length = strlen(pattern) - 6 + strlen(srcdir) + strlen(name) + + strlen(suffix) + 1; + filename = isl_alloc_array(ctx, char, length); + + if (!filename) + return NULL; + + sprintf(filename, pattern, srcdir, name, suffix); + + return filename; +} + +void test_parse_map(isl_ctx *ctx, const char *str) +{ + isl_map *map; + + map = isl_map_read_from_str(ctx, str); + assert(map); + isl_map_free(map); +} + +int test_parse_map_equal(isl_ctx *ctx, const char *str, const char *str2) +{ + isl_map *map, *map2; + int equal; + + map = isl_map_read_from_str(ctx, str); + map2 = isl_map_read_from_str(ctx, str2); + equal = isl_map_is_equal(map, map2); + isl_map_free(map); + isl_map_free(map2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "maps not equal", + return -1); + + return 0; +} + +void test_parse_pwqp(isl_ctx *ctx, const char *str) +{ + isl_pw_qpolynomial *pwqp; + + pwqp = isl_pw_qpolynomial_read_from_str(ctx, str); + assert(pwqp); + isl_pw_qpolynomial_free(pwqp); +} + +static void test_parse_pwaff(isl_ctx *ctx, const char *str) +{ + isl_pw_aff *pwaff; + + pwaff = isl_pw_aff_read_from_str(ctx, str); + assert(pwaff); + isl_pw_aff_free(pwaff); +} + +/* Check that we can read an isl_multi_val from "str" without errors. + */ +static int test_parse_multi_val(isl_ctx *ctx, const char *str) +{ + isl_multi_val *mv; + + mv = isl_multi_val_read_from_str(ctx, str); + isl_multi_val_free(mv); + + return mv ? 0 : -1; +} + +/* Pairs of binary relation representations that should represent + * the same binary relations. + */ +struct { + const char *map1; + const char *map2; +} parse_map_equal_tests[] = { + { "{ [x,y] : [([x/2]+y)/3] >= 1 }", + "{ [x, y] : 2y >= 6 - x }" }, + { "{ [x,y] : x <= min(y, 2*y+3) }", + "{ [x,y] : x <= y, 2*y + 3 }" }, + { "{ [x,y] : x >= min(y, 2*y+3) }", + "{ [x, y] : (y <= x and y >= -3) or (2y <= -3 + x and y <= -4) }" }, + { "[n] -> { [c1] : c1>=0 and c1<=floord(n-4,3) }", + "[n] -> { [c1] : c1 >= 0 and 3c1 <= -4 + n }" }, + { "{ [i,j] -> [i] : i < j; [i,j] -> [j] : j <= i }", + "{ [i,j] -> [min(i,j)] }" }, + { "{ [i,j] : i != j }", + "{ [i,j] : i < j or i > j }" }, + { "{ [i,j] : (i+1)*2 >= j }", + "{ [i, j] : j <= 2 + 2i }" }, + { "{ [i] -> [i > 0 ? 4 : 5] }", + "{ [i] -> [5] : i <= 0; [i] -> [4] : i >= 1 }" }, + { "[N=2,M] -> { [i=[(M+N)/4]] }", + "[N, M] -> { [i] : N = 2 and 4i <= 2 + M and 4i >= -1 + M }" }, + { "{ [x] : x >= 0 }", + "{ [x] : x-0 >= 0 }" }, + { "{ [i] : ((i > 10)) }", + "{ [i] : i >= 11 }" }, + { "{ [i] -> [0] }", + "{ [i] -> [0 * i] }" }, + { "{ [a] -> [b] : (not false) }", + "{ [a] -> [b] : true }" }, + { "{ [i] : i/2 <= 5 }", + "{ [i] : i <= 10 }" }, + { "{Sym=[n] [i] : i <= n }", + "[n] -> { [i] : i <= n }" }, + { "{ [*] }", + "{ [a] }" }, + { "{ [i] : 2*floor(i/2) = i }", + "{ [i] : exists a : i = 2 a }" }, + { "{ [a] -> [b] : a = 5 implies b = 5 }", + "{ [a] -> [b] : a != 5 or b = 5 }" }, + { "{ [a] -> [a - 1 : a > 0] }", + "{ [a] -> [a - 1] : a > 0 }" }, + { "{ [a] -> [a - 1 : a > 0; a : a <= 0] }", + "{ [a] -> [a - 1] : a > 0; [a] -> [a] : a <= 0 }" }, + { "{ [a] -> [(a) * 2 : a >= 0; 0 : a < 0] }", + "{ [a] -> [2a] : a >= 0; [a] -> [0] : a < 0 }" }, + { "{ [a] -> [(a * 2) : a >= 0; 0 : a < 0] }", + "{ [a] -> [2a] : a >= 0; [a] -> [0] : a < 0 }" }, + { "{ [a] -> [(a * 2 : a >= 0); 0 : a < 0] }", + "{ [a] -> [2a] : a >= 0; [a] -> [0] : a < 0 }" }, + { "{ [a] -> [(a * 2 : a >= 0; 0 : a < 0)] }", + "{ [a] -> [2a] : a >= 0; [a] -> [0] : a < 0 }" }, + { "{ [a,b] -> [i,j] : a,b << i,j }", + "{ [a,b] -> [i,j] : a < i or (a = i and b < j) }" }, + { "{ [a,b] -> [i,j] : a,b <<= i,j }", + "{ [a,b] -> [i,j] : a < i or (a = i and b <= j) }" }, + { "{ [a,b] -> [i,j] : a,b >> i,j }", + "{ [a,b] -> [i,j] : a > i or (a = i and b > j) }" }, + { "{ [a,b] -> [i,j] : a,b >>= i,j }", + "{ [a,b] -> [i,j] : a > i or (a = i and b >= j) }" }, + { "{ [n] -> [i] : exists (a, b, c: 8b <= i - 32a and " + "8b >= -7 + i - 32 a and b >= 0 and b <= 3 and " + "8c < n - 32a and i < n and c >= 0 and " + "c <= 3 and c >= -4a) }", + "{ [n] -> [i] : 0 <= i < n }" }, + { "{ [x] -> [] : exists (a, b: 0 <= a <= 1 and 0 <= b <= 3 and " + "2b <= x - 8a and 2b >= -1 + x - 8a) }", + "{ [x] -> [] : 0 <= x <= 15 }" }, +}; + +int test_parse(struct isl_ctx *ctx) +{ + int i; + isl_map *map, *map2; + const char *str, *str2; + + if (test_parse_multi_val(ctx, "{ A[B[2] -> C[5, 7]] }") < 0) + return -1; + if (test_parse_multi_val(ctx, "[n] -> { [2] }") < 0) + return -1; + if (test_parse_multi_val(ctx, "{ A[4, infty, NaN, -1/2, 2/3] }") < 0) + return -1; + + str = "{ [i] -> [-i] }"; + map = isl_map_read_from_str(ctx, str); + assert(map); + isl_map_free(map); + + str = "{ A[i] -> L[([i/3])] }"; + map = isl_map_read_from_str(ctx, str); + assert(map); + isl_map_free(map); + + test_parse_map(ctx, "{[[s] -> A[i]] -> [[s+1] -> A[i]]}"); + test_parse_map(ctx, "{ [p1, y1, y2] -> [2, y1, y2] : " + "p1 = 1 && (y1 <= y2 || y2 = 0) }"); + + for (i = 0; i < ARRAY_SIZE(parse_map_equal_tests); ++i) { + str = parse_map_equal_tests[i].map1; + str2 = parse_map_equal_tests[i].map2; + if (test_parse_map_equal(ctx, str, str2) < 0) + return -1; + } + + str = "{[new,old] -> [new+1-2*[(new+1)/2],old+1-2*[(old+1)/2]]}"; + map = isl_map_read_from_str(ctx, str); + str = "{ [new, old] -> [o0, o1] : " + "exists (e0 = [(-1 - new + o0)/2], e1 = [(-1 - old + o1)/2]: " + "2e0 = -1 - new + o0 and 2e1 = -1 - old + o1 and o0 >= 0 and " + "o0 <= 1 and o1 >= 0 and o1 <= 1) }"; + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "{[new,old] -> [new+1-2*[(new+1)/2],old+1-2*[(old+1)/2]]}"; + map = isl_map_read_from_str(ctx, str); + str = "{[new,old] -> [(new+1)%2,(old+1)%2]}"; + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + test_parse_pwqp(ctx, "{ [i] -> i + [ (i + [i/3])/2 ] }"); + test_parse_map(ctx, "{ S1[i] -> [([i/10]),i%10] : 0 <= i <= 45 }"); + test_parse_pwaff(ctx, "{ [i] -> [i + 1] : i > 0; [a] -> [a] : a < 0 }"); + test_parse_pwqp(ctx, "{ [x] -> ([(x)/2] * [(x)/3]) }"); + + return 0; +} + +static int test_read(isl_ctx *ctx) +{ + char *filename; + FILE *input; + isl_basic_set *bset1, *bset2; + const char *str = "{[y]: Exists ( alpha : 2alpha = y)}"; + int equal; + + filename = get_filename(ctx, "set", "omega"); + assert(filename); + input = fopen(filename, "r"); + assert(input); + + bset1 = isl_basic_set_read_from_file(ctx, input); + bset2 = isl_basic_set_read_from_str(ctx, str); + + equal = isl_basic_set_is_equal(bset1, bset2); + + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + free(filename); + + fclose(input); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "read sets not equal", return -1); + + return 0; +} + +static int test_bounded(isl_ctx *ctx) +{ + isl_set *set; + int bounded; + + set = isl_set_read_from_str(ctx, "[n] -> {[i] : 0 <= i <= n }"); + bounded = isl_set_is_bounded(set); + isl_set_free(set); + + if (bounded < 0) + return -1; + if (!bounded) + isl_die(ctx, isl_error_unknown, + "set not considered bounded", return -1); + + set = isl_set_read_from_str(ctx, "{[n, i] : 0 <= i <= n }"); + bounded = isl_set_is_bounded(set); + assert(!bounded); + isl_set_free(set); + + if (bounded < 0) + return -1; + if (bounded) + isl_die(ctx, isl_error_unknown, + "set considered bounded", return -1); + + set = isl_set_read_from_str(ctx, "[n] -> {[i] : i <= n }"); + bounded = isl_set_is_bounded(set); + isl_set_free(set); + + if (bounded < 0) + return -1; + if (bounded) + isl_die(ctx, isl_error_unknown, + "set considered bounded", return -1); + + return 0; +} + +/* Construct the basic set { [i] : 5 <= i <= N } */ +static int test_construction(isl_ctx *ctx) +{ + isl_int v; + isl_space *dim; + isl_local_space *ls; + isl_basic_set *bset; + isl_constraint *c; + + isl_int_init(v); + + dim = isl_space_set_alloc(ctx, 1, 1); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_inequality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_param, 0, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_inequality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -5); + isl_constraint_set_constant(c, v); + bset = isl_basic_set_add_constraint(bset, c); + + isl_local_space_free(ls); + isl_basic_set_free(bset); + + isl_int_clear(v); + + return 0; +} + +static int test_dim(isl_ctx *ctx) +{ + const char *str; + isl_map *map1, *map2; + int equal; + + map1 = isl_map_read_from_str(ctx, + "[n] -> { [i] -> [j] : exists (a = [i/10] : i - 10a <= n ) }"); + map1 = isl_map_add_dims(map1, isl_dim_in, 1); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [i,k] -> [j] : exists (a = [i/10] : i - 10a <= n ) }"); + equal = isl_map_is_equal(map1, map2); + isl_map_free(map2); + + map1 = isl_map_project_out(map1, isl_dim_in, 0, 1); + map2 = isl_map_read_from_str(ctx, "[n] -> { [i] -> [j] : n >= 0 }"); + if (equal >= 0 && equal) + equal = isl_map_is_equal(map1, map2); + + isl_map_free(map1); + isl_map_free(map2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + + str = "[n] -> { [i] -> [] : exists a : 0 <= i <= n and i = 2 a }"; + map1 = isl_map_read_from_str(ctx, str); + str = "{ [i] -> [j] : exists a : 0 <= i <= j and i = 2 a }"; + map2 = isl_map_read_from_str(ctx, str); + map1 = isl_map_move_dims(map1, isl_dim_out, 0, isl_dim_param, 0, 1); + equal = isl_map_is_equal(map1, map2); + isl_map_free(map1); + isl_map_free(map2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + + return 0; +} + +struct { + __isl_give isl_val *(*op)(__isl_take isl_val *v); + const char *arg; + const char *res; +} val_un_tests[] = { + { &isl_val_neg, "0", "0" }, + { &isl_val_abs, "0", "0" }, + { &isl_val_2exp, "0", "1" }, + { &isl_val_floor, "0", "0" }, + { &isl_val_ceil, "0", "0" }, + { &isl_val_neg, "1", "-1" }, + { &isl_val_neg, "-1", "1" }, + { &isl_val_neg, "1/2", "-1/2" }, + { &isl_val_neg, "-1/2", "1/2" }, + { &isl_val_neg, "infty", "-infty" }, + { &isl_val_neg, "-infty", "infty" }, + { &isl_val_neg, "NaN", "NaN" }, + { &isl_val_abs, "1", "1" }, + { &isl_val_abs, "-1", "1" }, + { &isl_val_abs, "1/2", "1/2" }, + { &isl_val_abs, "-1/2", "1/2" }, + { &isl_val_abs, "infty", "infty" }, + { &isl_val_abs, "-infty", "infty" }, + { &isl_val_abs, "NaN", "NaN" }, + { &isl_val_floor, "1", "1" }, + { &isl_val_floor, "-1", "-1" }, + { &isl_val_floor, "1/2", "0" }, + { &isl_val_floor, "-1/2", "-1" }, + { &isl_val_floor, "infty", "infty" }, + { &isl_val_floor, "-infty", "-infty" }, + { &isl_val_floor, "NaN", "NaN" }, + { &isl_val_ceil, "1", "1" }, + { &isl_val_ceil, "-1", "-1" }, + { &isl_val_ceil, "1/2", "1" }, + { &isl_val_ceil, "-1/2", "0" }, + { &isl_val_ceil, "infty", "infty" }, + { &isl_val_ceil, "-infty", "-infty" }, + { &isl_val_ceil, "NaN", "NaN" }, + { &isl_val_2exp, "-3", "1/8" }, + { &isl_val_2exp, "-1", "1/2" }, + { &isl_val_2exp, "1", "2" }, + { &isl_val_2exp, "2", "4" }, + { &isl_val_2exp, "3", "8" }, + { &isl_val_inv, "1", "1" }, + { &isl_val_inv, "2", "1/2" }, + { &isl_val_inv, "1/2", "2" }, + { &isl_val_inv, "-2", "-1/2" }, + { &isl_val_inv, "-1/2", "-2" }, + { &isl_val_inv, "0", "NaN" }, + { &isl_val_inv, "NaN", "NaN" }, + { &isl_val_inv, "infty", "0" }, + { &isl_val_inv, "-infty", "0" }, +}; + +/* Perform some basic tests of unary operations on isl_val objects. + */ +static int test_un_val(isl_ctx *ctx) +{ + int i; + isl_val *v, *res; + __isl_give isl_val *(*fn)(__isl_take isl_val *v); + int ok; + + for (i = 0; i < ARRAY_SIZE(val_un_tests); ++i) { + v = isl_val_read_from_str(ctx, val_un_tests[i].arg); + res = isl_val_read_from_str(ctx, val_un_tests[i].res); + fn = val_un_tests[i].op; + v = fn(v); + if (isl_val_is_nan(res)) + ok = isl_val_is_nan(v); + else + ok = isl_val_eq(v, res); + isl_val_free(v); + isl_val_free(res); + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + } + + return 0; +} + +struct { + __isl_give isl_val *(*fn)(__isl_take isl_val *v1, + __isl_take isl_val *v2); +} val_bin_op[] = { + ['+'] = { &isl_val_add }, + ['-'] = { &isl_val_sub }, + ['*'] = { &isl_val_mul }, + ['/'] = { &isl_val_div }, + ['g'] = { &isl_val_gcd }, + ['m'] = { &isl_val_min }, + ['M'] = { &isl_val_max }, +}; + +struct { + const char *arg1; + unsigned char op; + const char *arg2; + const char *res; +} val_bin_tests[] = { + { "0", '+', "0", "0" }, + { "1", '+', "0", "1" }, + { "1", '+', "1", "2" }, + { "1", '-', "1", "0" }, + { "1", '*', "1", "1" }, + { "1", '/', "1", "1" }, + { "2", '*', "3", "6" }, + { "2", '*', "1/2", "1" }, + { "2", '*', "1/3", "2/3" }, + { "2/3", '*', "3/5", "2/5" }, + { "2/3", '*', "7/5", "14/15" }, + { "2", '/', "1/2", "4" }, + { "-2", '/', "-1/2", "4" }, + { "-2", '/', "1/2", "-4" }, + { "2", '/', "-1/2", "-4" }, + { "2", '/', "2", "1" }, + { "2", '/', "3", "2/3" }, + { "2/3", '/', "5/3", "2/5" }, + { "2/3", '/', "5/7", "14/15" }, + { "0", '/', "0", "NaN" }, + { "42", '/', "0", "NaN" }, + { "-42", '/', "0", "NaN" }, + { "infty", '/', "0", "NaN" }, + { "-infty", '/', "0", "NaN" }, + { "NaN", '/', "0", "NaN" }, + { "0", '/', "NaN", "NaN" }, + { "42", '/', "NaN", "NaN" }, + { "-42", '/', "NaN", "NaN" }, + { "infty", '/', "NaN", "NaN" }, + { "-infty", '/', "NaN", "NaN" }, + { "NaN", '/', "NaN", "NaN" }, + { "0", '/', "infty", "0" }, + { "42", '/', "infty", "0" }, + { "-42", '/', "infty", "0" }, + { "infty", '/', "infty", "NaN" }, + { "-infty", '/', "infty", "NaN" }, + { "NaN", '/', "infty", "NaN" }, + { "0", '/', "-infty", "0" }, + { "42", '/', "-infty", "0" }, + { "-42", '/', "-infty", "0" }, + { "infty", '/', "-infty", "NaN" }, + { "-infty", '/', "-infty", "NaN" }, + { "NaN", '/', "-infty", "NaN" }, + { "1", '-', "1/3", "2/3" }, + { "1/3", '+', "1/2", "5/6" }, + { "1/2", '+', "1/2", "1" }, + { "3/4", '-', "1/4", "1/2" }, + { "1/2", '-', "1/3", "1/6" }, + { "infty", '+', "42", "infty" }, + { "infty", '+', "infty", "infty" }, + { "42", '+', "infty", "infty" }, + { "infty", '-', "infty", "NaN" }, + { "infty", '*', "infty", "infty" }, + { "infty", '*', "-infty", "-infty" }, + { "-infty", '*', "infty", "-infty" }, + { "-infty", '*', "-infty", "infty" }, + { "0", '*', "infty", "NaN" }, + { "1", '*', "infty", "infty" }, + { "infty", '*', "0", "NaN" }, + { "infty", '*', "42", "infty" }, + { "42", '-', "infty", "-infty" }, + { "infty", '+', "-infty", "NaN" }, + { "4", 'g', "6", "2" }, + { "5", 'g', "6", "1" }, + { "42", 'm', "3", "3" }, + { "42", 'M', "3", "42" }, + { "3", 'm', "42", "3" }, + { "3", 'M', "42", "42" }, + { "42", 'm', "infty", "42" }, + { "42", 'M', "infty", "infty" }, + { "42", 'm', "-infty", "-infty" }, + { "42", 'M', "-infty", "42" }, + { "42", 'm', "NaN", "NaN" }, + { "42", 'M', "NaN", "NaN" }, + { "infty", 'm', "-infty", "-infty" }, + { "infty", 'M', "-infty", "infty" }, +}; + +/* Perform some basic tests of binary operations on isl_val objects. + */ +static int test_bin_val(isl_ctx *ctx) +{ + int i; + isl_val *v1, *v2, *res; + __isl_give isl_val *(*fn)(__isl_take isl_val *v1, + __isl_take isl_val *v2); + int ok; + + for (i = 0; i < ARRAY_SIZE(val_bin_tests); ++i) { + v1 = isl_val_read_from_str(ctx, val_bin_tests[i].arg1); + v2 = isl_val_read_from_str(ctx, val_bin_tests[i].arg2); + res = isl_val_read_from_str(ctx, val_bin_tests[i].res); + fn = val_bin_op[val_bin_tests[i].op].fn; + v1 = fn(v1, v2); + if (isl_val_is_nan(res)) + ok = isl_val_is_nan(v1); + else + ok = isl_val_eq(v1, res); + isl_val_free(v1); + isl_val_free(res); + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + } + + return 0; +} + +/* Perform some basic tests on isl_val objects. + */ +static int test_val(isl_ctx *ctx) +{ + if (test_un_val(ctx) < 0) + return -1; + if (test_bin_val(ctx) < 0) + return -1; + return 0; +} + +/* Sets described using existentially quantified variables that + * can also be described without. + */ +static const char *elimination_tests[] = { + "{ [i,j] : 2 * [i/2] + 3 * [j/4] <= 10 and 2 i = j }", + "{ [m, w] : exists a : w - 2m - 5 <= 3a <= m - 2w }", + "{ [m, w] : exists a : w >= 0 and a < m and -1 + w <= a <= 2m - w }", +}; + +/* Check that redundant existentially quantified variables are + * getting removed. + */ +static int test_elimination(isl_ctx *ctx) +{ + int i; + unsigned n; + isl_basic_set *bset; + + for (i = 0; i < ARRAY_SIZE(elimination_tests); ++i) { + bset = isl_basic_set_read_from_str(ctx, elimination_tests[i]); + n = isl_basic_set_dim(bset, isl_dim_div); + isl_basic_set_free(bset); + if (!bset) + return -1; + if (n != 0) + isl_die(ctx, isl_error_unknown, + "expecting no existentials", return -1); + } + + return 0; +} + +static int test_div(isl_ctx *ctx) +{ + const char *str; + int empty; + isl_int v; + isl_space *dim; + isl_set *set; + isl_local_space *ls; + struct isl_basic_set *bset; + struct isl_constraint *c; + + isl_int_init(v); + + /* test 1 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 1, 2); + + assert(bset && bset->n_div == 1); + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 2 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 1, 2); + + assert(bset && bset->n_div == 1); + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 3 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -3); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 4); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 1, 2); + + assert(bset && bset->n_div == 1); + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 4 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 2); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_constant(c, v); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 6); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 1, 2); + + assert(isl_basic_set_is_empty(bset)); + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 5 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 2, 1); + + assert(bset && bset->n_div == 0); + isl_basic_set_free(bset); + isl_local_space_free(ls); + + /* test 6 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 6); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 2, 1); + + assert(bset && bset->n_div == 1); + isl_basic_set_free(bset); + isl_local_space_free(ls); + + /* test 7 */ + /* This test is a bit tricky. We set up an equality + * a + 3b + 3c = 6 e0 + * Normalization of divs creates _two_ divs + * a = 3 e0 + * c - b - e0 = 2 e1 + * Afterwards e0 is removed again because it has coefficient -1 + * and we end up with the original equality and div again. + * Perhaps we can avoid the introduction of this temporary div. + */ + dim = isl_space_set_alloc(ctx, 0, 4); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + isl_int_set_si(v, -3); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + isl_int_set_si(v, 6); + isl_constraint_set_coefficient(c, isl_dim_set, 3, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 3, 1); + + /* Test disabled for now */ + /* + assert(bset && bset->n_div == 1); + */ + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 8 */ + dim = isl_space_set_alloc(ctx, 0, 5); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -3); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + isl_int_set_si(v, -3); + isl_constraint_set_coefficient(c, isl_dim_set, 3, v); + isl_int_set_si(v, 6); + isl_constraint_set_coefficient(c, isl_dim_set, 4, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + isl_int_set_si(v, 1); + isl_constraint_set_constant(c, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 4, 1); + + /* Test disabled for now */ + /* + assert(bset && bset->n_div == 1); + */ + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 9 */ + dim = isl_space_set_alloc(ctx, 0, 4); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 1, v); + isl_int_set_si(v, -2); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, -1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, 3); + isl_constraint_set_coefficient(c, isl_dim_set, 3, v); + isl_int_set_si(v, 2); + isl_constraint_set_constant(c, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 2, 2); + + bset = isl_basic_set_fix_si(bset, isl_dim_set, 0, 2); + + assert(!isl_basic_set_is_empty(bset)); + + isl_local_space_free(ls); + isl_basic_set_free(bset); + + /* test 10 */ + dim = isl_space_set_alloc(ctx, 0, 3); + bset = isl_basic_set_universe(isl_space_copy(dim)); + ls = isl_local_space_from_space(dim); + + c = isl_constraint_alloc_equality(isl_local_space_copy(ls)); + isl_int_set_si(v, 1); + isl_constraint_set_coefficient(c, isl_dim_set, 0, v); + isl_int_set_si(v, -2); + isl_constraint_set_coefficient(c, isl_dim_set, 2, v); + bset = isl_basic_set_add_constraint(bset, c); + + bset = isl_basic_set_project_out(bset, isl_dim_set, 2, 1); + + bset = isl_basic_set_fix_si(bset, isl_dim_set, 0, 2); + + isl_local_space_free(ls); + isl_basic_set_free(bset); + + isl_int_clear(v); + + str = "{ [i] : exists (e0, e1: 3e1 >= 1 + 2e0 and " + "8e1 <= -1 + 5i - 5e0 and 2e1 >= 1 + 2i - 5e0) }"; + set = isl_set_read_from_str(ctx, str); + set = isl_set_compute_divs(set); + isl_set_free(set); + if (!set) + return -1; + + if (test_elimination(ctx) < 0) + return -1; + + str = "{ [i,j,k] : 3 + i + 2j >= 0 and 2 * [(i+2j)/4] <= k }"; + set = isl_set_read_from_str(ctx, str); + set = isl_set_remove_divs_involving_dims(set, isl_dim_set, 0, 2); + set = isl_set_fix_si(set, isl_dim_set, 2, -3); + empty = isl_set_is_empty(set); + isl_set_free(set); + if (empty < 0) + return -1; + if (!empty) + isl_die(ctx, isl_error_unknown, + "result not as accurate as expected", return -1); + + return 0; +} + +void test_application_case(struct isl_ctx *ctx, const char *name) +{ + char *filename; + FILE *input; + struct isl_basic_set *bset1, *bset2; + struct isl_basic_map *bmap; + + filename = get_filename(ctx, name, "omega"); + assert(filename); + input = fopen(filename, "r"); + assert(input); + + bset1 = isl_basic_set_read_from_file(ctx, input); + bmap = isl_basic_map_read_from_file(ctx, input); + + bset1 = isl_basic_set_apply(bset1, bmap); + + bset2 = isl_basic_set_read_from_file(ctx, input); + + assert(isl_basic_set_is_equal(bset1, bset2) == 1); + + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + free(filename); + + fclose(input); +} + +static int test_application(isl_ctx *ctx) +{ + test_application_case(ctx, "application"); + test_application_case(ctx, "application2"); + + return 0; +} + +void test_affine_hull_case(struct isl_ctx *ctx, const char *name) +{ + char *filename; + FILE *input; + struct isl_basic_set *bset1, *bset2; + + filename = get_filename(ctx, name, "polylib"); + assert(filename); + input = fopen(filename, "r"); + assert(input); + + bset1 = isl_basic_set_read_from_file(ctx, input); + bset2 = isl_basic_set_read_from_file(ctx, input); + + bset1 = isl_basic_set_affine_hull(bset1); + + assert(isl_basic_set_is_equal(bset1, bset2) == 1); + + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + free(filename); + + fclose(input); +} + +int test_affine_hull(struct isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_basic_set *bset, *bset2; + int n; + int subset; + + test_affine_hull_case(ctx, "affine2"); + test_affine_hull_case(ctx, "affine"); + test_affine_hull_case(ctx, "affine3"); + + str = "[m] -> { [i0] : exists (e0, e1: e1 <= 1 + i0 and " + "m >= 3 and 4i0 <= 2 + m and e1 >= i0 and " + "e1 >= 0 and e1 <= 2 and e1 >= 1 + 2e0 and " + "2e1 <= 1 + m + 4e0 and 2e1 >= 2 - m + 4i0 - 4e0) }"; + set = isl_set_read_from_str(ctx, str); + bset = isl_set_affine_hull(set); + n = isl_basic_set_dim(bset, isl_dim_div); + isl_basic_set_free(bset); + if (n != 0) + isl_die(ctx, isl_error_unknown, "not expecting any divs", + return -1); + + /* Check that isl_map_affine_hull is not confused by + * the reordering of divs in isl_map_align_divs. + */ + str = "{ [a, b, c, 0] : exists (e0 = [(b)/32], e1 = [(c)/32]: " + "32e0 = b and 32e1 = c); " + "[a, 0, c, 0] : exists (e0 = [(c)/32]: 32e0 = c) }"; + set = isl_set_read_from_str(ctx, str); + bset = isl_set_affine_hull(set); + isl_basic_set_free(bset); + if (!bset) + return -1; + + str = "{ [a] : exists e0, e1, e2: 32e1 = 31 + 31a + 31e0 and " + "32e2 = 31 + 31e0 }"; + set = isl_set_read_from_str(ctx, str); + bset = isl_set_affine_hull(set); + str = "{ [a] : exists e : a = 32 e }"; + bset2 = isl_basic_set_read_from_str(ctx, str); + subset = isl_basic_set_is_subset(bset, bset2); + isl_basic_set_free(bset); + isl_basic_set_free(bset2); + if (subset < 0) + return -1; + if (!subset) + isl_die(ctx, isl_error_unknown, "not as accurate as expected", + return -1); + + return 0; +} + +/* Pairs of maps and the corresponding expected results of + * isl_map_plain_unshifted_simple_hull. + */ +struct { + const char *map; + const char *hull; +} plain_unshifted_simple_hull_tests[] = { + { "{ [i] -> [j] : i >= 1 and j >= 1 or i >= 2 and j <= 10 }", + "{ [i] -> [j] : i >= 1 }" }, + { "{ [n] -> [i,j,k] : (i mod 3 = 2 and j mod 4 = 2) or " + "(j mod 4 = 2 and k mod 6 = n) }", + "{ [n] -> [i,j,k] : j mod 4 = 2 }" }, +}; + +/* Basic tests for isl_map_plain_unshifted_simple_hull. + */ +static int test_plain_unshifted_simple_hull(isl_ctx *ctx) +{ + int i; + isl_map *map; + isl_basic_map *hull, *expected; + isl_bool equal; + + for (i = 0; i < ARRAY_SIZE(plain_unshifted_simple_hull_tests); ++i) { + const char *str; + str = plain_unshifted_simple_hull_tests[i].map; + map = isl_map_read_from_str(ctx, str); + str = plain_unshifted_simple_hull_tests[i].hull; + expected = isl_basic_map_read_from_str(ctx, str); + hull = isl_map_plain_unshifted_simple_hull(map); + equal = isl_basic_map_is_equal(hull, expected); + isl_basic_map_free(hull); + isl_basic_map_free(expected); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected hull", + return -1); + } + + return 0; +} + +/* Pairs of sets and the corresponding expected results of + * isl_set_unshifted_simple_hull. + */ +struct { + const char *set; + const char *hull; +} unshifted_simple_hull_tests[] = { + { "{ [0,x,y] : x <= -1; [1,x,y] : x <= y <= -x; [2,x,y] : x <= 1 }", + "{ [t,x,y] : 0 <= t <= 2 and x <= 1 }" }, +}; + +/* Basic tests for isl_set_unshifted_simple_hull. + */ +static int test_unshifted_simple_hull(isl_ctx *ctx) +{ + int i; + isl_set *set; + isl_basic_set *hull, *expected; + isl_bool equal; + + for (i = 0; i < ARRAY_SIZE(unshifted_simple_hull_tests); ++i) { + const char *str; + str = unshifted_simple_hull_tests[i].set; + set = isl_set_read_from_str(ctx, str); + str = unshifted_simple_hull_tests[i].hull; + expected = isl_basic_set_read_from_str(ctx, str); + hull = isl_set_unshifted_simple_hull(set); + equal = isl_basic_set_is_equal(hull, expected); + isl_basic_set_free(hull); + isl_basic_set_free(expected); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected hull", + return -1); + } + + return 0; +} + +static int test_simple_hull(struct isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_basic_set *bset; + isl_bool is_empty; + + str = "{ [x, y] : 3y <= 2x and y >= -2 + 2x and 2y >= 2 - x;" + "[y, x] : 3y <= 2x and y >= -2 + 2x and 2y >= 2 - x }"; + set = isl_set_read_from_str(ctx, str); + bset = isl_set_simple_hull(set); + is_empty = isl_basic_set_is_empty(bset); + isl_basic_set_free(bset); + + if (is_empty == isl_bool_error) + return -1; + + if (is_empty == isl_bool_false) + isl_die(ctx, isl_error_unknown, "Empty set should be detected", + return -1); + + if (test_plain_unshifted_simple_hull(ctx) < 0) + return -1; + if (test_unshifted_simple_hull(ctx) < 0) + return -1; + + return 0; +} + +void test_convex_hull_case(struct isl_ctx *ctx, const char *name) +{ + char *filename; + FILE *input; + struct isl_basic_set *bset1, *bset2; + struct isl_set *set; + + filename = get_filename(ctx, name, "polylib"); + assert(filename); + input = fopen(filename, "r"); + assert(input); + + bset1 = isl_basic_set_read_from_file(ctx, input); + bset2 = isl_basic_set_read_from_file(ctx, input); + + set = isl_basic_set_union(bset1, bset2); + bset1 = isl_set_convex_hull(set); + + bset2 = isl_basic_set_read_from_file(ctx, input); + + assert(isl_basic_set_is_equal(bset1, bset2) == 1); + + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + free(filename); + + fclose(input); +} + +struct { + const char *set; + const char *hull; +} convex_hull_tests[] = { + { "{ [i0, i1, i2] : (i2 = 1 and i0 = 0 and i1 >= 0) or " + "(i0 = 1 and i1 = 0 and i2 = 1) or " + "(i0 = 0 and i1 = 0 and i2 = 0) }", + "{ [i0, i1, i2] : i0 >= 0 and i2 >= i0 and i2 <= 1 and i1 >= 0 }" }, + { "[n] -> { [i0, i1, i0] : i0 <= -4 + n; " + "[i0, i0, i2] : n = 6 and i0 >= 0 and i2 <= 7 - i0 and " + "i2 <= 5 and i2 >= 4; " + "[3, i1, 3] : n = 5 and i1 <= 2 and i1 >= 0 }", + "[n] -> { [i0, i1, i2] : i2 <= -1 + n and 2i2 <= -6 + 3n - i0 and " + "i2 <= 5 + i0 and i2 >= i0 }" }, + { "{ [x, y] : 3y <= 2x and y >= -2 + 2x and 2y >= 2 - x }", + "{ [x, y] : 1 = 0 }" }, +}; + +static int test_convex_hull_algo(isl_ctx *ctx, int convex) +{ + int i; + int orig_convex = ctx->opt->convex; + ctx->opt->convex = convex; + + test_convex_hull_case(ctx, "convex0"); + test_convex_hull_case(ctx, "convex1"); + test_convex_hull_case(ctx, "convex2"); + test_convex_hull_case(ctx, "convex3"); + test_convex_hull_case(ctx, "convex4"); + test_convex_hull_case(ctx, "convex5"); + test_convex_hull_case(ctx, "convex6"); + test_convex_hull_case(ctx, "convex7"); + test_convex_hull_case(ctx, "convex8"); + test_convex_hull_case(ctx, "convex9"); + test_convex_hull_case(ctx, "convex10"); + test_convex_hull_case(ctx, "convex11"); + test_convex_hull_case(ctx, "convex12"); + test_convex_hull_case(ctx, "convex13"); + test_convex_hull_case(ctx, "convex14"); + test_convex_hull_case(ctx, "convex15"); + + for (i = 0; i < ARRAY_SIZE(convex_hull_tests); ++i) { + isl_set *set1, *set2; + int equal; + + set1 = isl_set_read_from_str(ctx, convex_hull_tests[i].set); + set2 = isl_set_read_from_str(ctx, convex_hull_tests[i].hull); + set1 = isl_set_from_basic_set(isl_set_convex_hull(set1)); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected convex hull", return -1); + } + + ctx->opt->convex = orig_convex; + + return 0; +} + +static int test_convex_hull(isl_ctx *ctx) +{ + if (test_convex_hull_algo(ctx, ISL_CONVEX_HULL_FM) < 0) + return -1; + if (test_convex_hull_algo(ctx, ISL_CONVEX_HULL_WRAP) < 0) + return -1; + return 0; +} + +void test_gist_case(struct isl_ctx *ctx, const char *name) +{ + char *filename; + FILE *input; + struct isl_basic_set *bset1, *bset2; + + filename = get_filename(ctx, name, "polylib"); + assert(filename); + input = fopen(filename, "r"); + assert(input); + + bset1 = isl_basic_set_read_from_file(ctx, input); + bset2 = isl_basic_set_read_from_file(ctx, input); + + bset1 = isl_basic_set_gist(bset1, bset2); + + bset2 = isl_basic_set_read_from_file(ctx, input); + + assert(isl_basic_set_is_equal(bset1, bset2) == 1); + + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + free(filename); + + fclose(input); +} + +/* Inputs to isl_map_plain_gist_basic_map, along with the expected output. + */ +struct { + const char *map; + const char *context; + const char *gist; +} plain_gist_tests[] = { + { "{ [i] -> [j] : i >= 1 and j >= 1 or i >= 2 and j <= 10 }", + "{ [i] -> [j] : i >= 1 }", + "{ [i] -> [j] : j >= 1 or i >= 2 and j <= 10 }" }, + { "{ [n] -> [i,j,k] : (i mod 3 = 2 and j mod 4 = 2) or " + "(j mod 4 = 2 and k mod 6 = n) }", + "{ [n] -> [i,j,k] : j mod 4 = 2 }", + "{ [n] -> [i,j,k] : (i mod 3 = 2) or (k mod 6 = n) }" }, + { "{ [i] -> [j] : i > j and (exists a,b : i <= 2a + 5b <= 2) }", + "{ [i] -> [j] : i > j }", + "{ [i] -> [j] : exists a,b : i <= 2a + 5b <= 2 }" }, +}; + +/* Basic tests for isl_map_plain_gist_basic_map. + */ +static int test_plain_gist(isl_ctx *ctx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(plain_gist_tests); ++i) { + const char *str; + int equal; + isl_map *map, *gist; + isl_basic_map *context; + + map = isl_map_read_from_str(ctx, plain_gist_tests[i].map); + str = plain_gist_tests[i].context; + context = isl_basic_map_read_from_str(ctx, str); + map = isl_map_plain_gist_basic_map(map, context); + gist = isl_map_read_from_str(ctx, plain_gist_tests[i].gist); + equal = isl_map_is_equal(map, gist); + isl_map_free(map); + isl_map_free(gist); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "incorrect gist result", return -1); + } + + return 0; +} + +struct { + const char *set; + const char *context; + const char *gist; +} gist_tests[] = { + { "{ [a, b, c] : a <= 15 and a >= 1 }", + "{ [a, b, c] : exists (e0 = floor((-1 + a)/16): a >= 1 and " + "c <= 30 and 32e0 >= -62 + 2a + 2b - c and b >= 0) }", + "{ [a, b, c] : a <= 15 }" }, + { "{ : }", "{ : 1 = 0 }", "{ : }" }, + { "{ : 1 = 0 }", "{ : 1 = 0 }", "{ : }" }, + { "[M] -> { [x] : exists (e0 = floor((-2 + x)/3): 3e0 = -2 + x) }", + "[M] -> { [3M] }" , "[M] -> { [x] : 1 = 0 }" }, + { "{ [m, n, a, b] : a <= 2147 + n }", + "{ [m, n, a, b] : (m >= 1 and n >= 1 and a <= 2148 - m and " + "b <= 2148 - n and b >= 0 and b >= 2149 - n - a) or " + "(n >= 1 and a >= 0 and b <= 2148 - n - a and " + "b >= 0) }", + "{ [m, n, ku, kl] }" }, + { "{ [a, a, b] : a >= 10 }", + "{ [a, b, c] : c >= a and c <= b and c >= 2 }", + "{ [a, a, b] : a >= 10 }" }, + { "{ [i, j] : i >= 0 and i + j >= 0 }", "{ [i, j] : i <= 0 }", + "{ [0, j] : j >= 0 }" }, + /* Check that no constraints on i6 are introduced in the gist */ + { "[t1] -> { [i4, i6] : exists (e0 = floor((1530 - 4t1 - 5i4)/20): " + "20e0 <= 1530 - 4t1 - 5i4 and 20e0 >= 1511 - 4t1 - 5i4 and " + "5e0 <= 381 - t1 and i4 <= 1) }", + "[t1] -> { [i4, i6] : exists (e0 = floor((-t1 + i6)/5): " + "5e0 = -t1 + i6 and i6 <= 6 and i6 >= 3) }", + "[t1] -> { [i4, i6] : exists (e0 = floor((1530 - 4t1 - 5i4)/20): " + "i4 <= 1 and 5e0 <= 381 - t1 and 20e0 <= 1530 - 4t1 - 5i4 and " + "20e0 >= 1511 - 4t1 - 5i4) }" }, + /* Check that no constraints on i6 are introduced in the gist */ + { "[t1, t2] -> { [i4, i5, i6] : exists (e0 = floor((1 + i4)/2), " + "e1 = floor((1530 - 4t1 - 5i4)/20), " + "e2 = floor((-4t1 - 5i4 + 10*floor((1 + i4)/2))/20), " + "e3 = floor((-1 + i4)/2): t2 = 0 and 2e3 = -1 + i4 and " + "20e2 >= -19 - 4t1 - 5i4 + 10e0 and 5e2 <= 1 - t1 and " + "2e0 <= 1 + i4 and 2e0 >= i4 and " + "20e1 <= 1530 - 4t1 - 5i4 and " + "20e1 >= 1511 - 4t1 - 5i4 and i4 <= 1 and " + "5e1 <= 381 - t1 and 20e2 <= -4t1 - 5i4 + 10e0) }", + "[t1, t2] -> { [i4, i5, i6] : exists (e0 = floor((-17 + i4)/2), " + "e1 = floor((-t1 + i6)/5): 5e1 = -t1 + i6 and " + "2e0 <= -17 + i4 and 2e0 >= -18 + i4 and " + "10e0 <= -91 + 5i4 + 4i6 and " + "10e0 >= -105 + 5i4 + 4i6) }", + "[t1, t2] -> { [i4, i5, i6] : exists (e0 = floor((381 - t1)/5), " + "e1 = floor((-1 + i4)/2): t2 = 0 and 2e1 = -1 + i4 and " + "i4 <= 1 and 5e0 <= 381 - t1 and 20e0 >= 1511 - 4t1 - 5i4) }" }, + { "{ [0, 0, q, p] : -5 <= q <= 5 and p >= 0 }", + "{ [a, b, q, p] : b >= 1 + a }", + "{ [a, b, q, p] : false }" }, + { "[n] -> { [x] : x = n && x mod 32 = 0 }", + "[n] -> { [x] : x mod 32 = 0 }", + "[n] -> { [x = n] }" }, + { "{ [x] : x mod 6 = 0 }", "{ [x] : x mod 3 = 0 }", + "{ [x] : x mod 2 = 0 }" }, + { "{ [x] : x mod 3200 = 0 }", "{ [x] : x mod 10000 = 0 }", + "{ [x] : x mod 128 = 0 }" }, + { "{ [x] : x mod 3200 = 0 }", "{ [x] : x mod 10 = 0 }", + "{ [x] : x mod 3200 = 0 }" }, + { "{ [a, b, c] : a mod 2 = 0 and a = c }", + "{ [a, b, c] : b mod 2 = 0 and b = c }", + "{ [a, b, c = a] }" }, + { "{ [a, b, c] : a mod 6 = 0 and a = c }", + "{ [a, b, c] : b mod 2 = 0 and b = c }", + "{ [a, b, c = a] : a mod 3 = 0 }" }, + { "{ [x] : 0 <= x <= 4 or 6 <= x <= 9 }", + "{ [x] : 1 <= x <= 3 or 7 <= x <= 8 }", + "{ [x] }" }, + { "{ [x,y] : x < 0 and 0 <= y <= 4 or x >= -2 and -x <= y <= 10 + x }", + "{ [x,y] : 1 <= y <= 3 }", + "{ [x,y] }" }, +}; + +/* Check that isl_set_gist behaves as expected. + * + * For the test cases in gist_tests, besides checking that the result + * is as expected, also check that applying the gist operation does + * not modify the input set (an earlier version of isl would do that) and + * that the test case is consistent, i.e., that the gist has the same + * intersection with the context as the input set. + */ +static int test_gist(struct isl_ctx *ctx) +{ + int i; + const char *str; + isl_basic_set *bset1, *bset2; + isl_map *map1, *map2; + int equal; + + for (i = 0; i < ARRAY_SIZE(gist_tests); ++i) { + int equal_input, equal_intersection; + isl_set *set1, *set2, *copy, *context; + + set1 = isl_set_read_from_str(ctx, gist_tests[i].set); + context = isl_set_read_from_str(ctx, gist_tests[i].context); + copy = isl_set_copy(set1); + set1 = isl_set_gist(set1, isl_set_copy(context)); + set2 = isl_set_read_from_str(ctx, gist_tests[i].gist); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + set1 = isl_set_read_from_str(ctx, gist_tests[i].set); + equal_input = isl_set_is_equal(set1, copy); + isl_set_free(copy); + set1 = isl_set_intersect(set1, isl_set_copy(context)); + set2 = isl_set_intersect(set2, context); + equal_intersection = isl_set_is_equal(set1, set2); + isl_set_free(set2); + isl_set_free(set1); + if (equal < 0 || equal_input < 0 || equal_intersection < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "incorrect gist result", return -1); + if (!equal_input) + isl_die(ctx, isl_error_unknown, + "gist modified input", return -1); + if (!equal_input) + isl_die(ctx, isl_error_unknown, + "inconsistent gist test case", return -1); + } + + test_gist_case(ctx, "gist1"); + + str = "[p0, p2, p3, p5, p6, p10] -> { [] : " + "exists (e0 = [(15 + p0 + 15p6 + 15p10)/16], e1 = [(p5)/8], " + "e2 = [(p6)/128], e3 = [(8p2 - p5)/128], " + "e4 = [(128p3 - p6)/4096]: 8e1 = p5 and 128e2 = p6 and " + "128e3 = 8p2 - p5 and 4096e4 = 128p3 - p6 and p2 >= 0 and " + "16e0 >= 16 + 16p6 + 15p10 and p2 <= 15 and p3 >= 0 and " + "p3 <= 31 and p6 >= 128p3 and p5 >= 8p2 and p10 >= 0 and " + "16e0 <= 15 + p0 + 15p6 + 15p10 and 16e0 >= p0 + 15p6 + 15p10 and " + "p10 <= 15 and p10 <= -1 + p0 - p6) }"; + bset1 = isl_basic_set_read_from_str(ctx, str); + str = "[p0, p2, p3, p5, p6, p10] -> { [] : exists (e0 = [(p5)/8], " + "e1 = [(p6)/128], e2 = [(8p2 - p5)/128], " + "e3 = [(128p3 - p6)/4096]: 8e0 = p5 and 128e1 = p6 and " + "128e2 = 8p2 - p5 and 4096e3 = 128p3 - p6 and p5 >= -7 and " + "p2 >= 0 and 8p2 <= -1 + p0 and p2 <= 15 and p3 >= 0 and " + "p3 <= 31 and 128p3 <= -1 + p0 and p6 >= -127 and " + "p5 <= -1 + p0 and p6 <= -1 + p0 and p6 >= 128p3 and " + "p0 >= 1 and p5 >= 8p2 and p10 >= 0 and p10 <= 15 ) }"; + bset2 = isl_basic_set_read_from_str(ctx, str); + bset1 = isl_basic_set_gist(bset1, bset2); + assert(bset1 && bset1->n_div == 0); + isl_basic_set_free(bset1); + + /* Check that the integer divisions of the second disjunct + * do not spread to the first disjunct. + */ + str = "[t1] -> { S_0[] -> A[o0] : (exists (e0 = [(-t1 + o0)/16]: " + "16e0 = -t1 + o0 and o0 >= 0 and o0 <= 15 and t1 >= 0)) or " + "(exists (e0 = [(-1 + t1)/16], " + "e1 = [(-16 + t1 - 16e0)/4294967296]: " + "4294967296e1 = -16 + t1 - o0 - 16e0 and " + "16e0 <= -1 + t1 and 16e0 >= -16 + t1 and o0 >= 0 and " + "o0 <= 4294967295 and t1 <= -1)) }"; + map1 = isl_map_read_from_str(ctx, str); + str = "[t1] -> { S_0[] -> A[o0] : t1 >= 0 and t1 <= 4294967295 }"; + map2 = isl_map_read_from_str(ctx, str); + map1 = isl_map_gist(map1, map2); + if (!map1) + return -1; + if (map1->n != 1) + isl_die(ctx, isl_error_unknown, "expecting single disjunct", + isl_map_free(map1); return -1); + if (isl_basic_map_dim(map1->p[0], isl_dim_div) != 1) + isl_die(ctx, isl_error_unknown, "expecting single div", + isl_map_free(map1); return -1); + isl_map_free(map1); + + if (test_plain_gist(ctx) < 0) + return -1; + + return 0; +} + +int test_coalesce_set(isl_ctx *ctx, const char *str, int check_one) +{ + isl_set *set, *set2; + int equal; + int one; + + set = isl_set_read_from_str(ctx, str); + set = isl_set_coalesce(set); + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set, set2); + one = set && set->n == 1; + isl_set_free(set); + isl_set_free(set2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "coalesced set not equal to input", return -1); + if (check_one && !one) + isl_die(ctx, isl_error_unknown, + "coalesced set should not be a union", return -1); + + return 0; +} + +/* Inputs for coalescing tests with unbounded wrapping. + * "str" is a string representation of the input set. + * "single_disjunct" is set if we expect the result to consist of + * a single disjunct. + */ +struct { + int single_disjunct; + const char *str; +} coalesce_unbounded_tests[] = { + { 1, "{ [x,y,z] : y + 2 >= 0 and x - y + 1 >= 0 and " + "-x - y + 1 >= 0 and -3 <= z <= 3;" + "[x,y,z] : -x+z + 20 >= 0 and -x-z + 20 >= 0 and " + "x-z + 20 >= 0 and x+z + 20 >= 0 and " + "-10 <= y <= 0}" }, + { 1, "{ [x,y] : 0 <= x,y <= 10; [5,y]: 4 <= y <= 11 }" }, + { 1, "{ [x,0,0] : -5 <= x <= 5; [0,y,1] : -5 <= y <= 5 }" }, + { 1, "{ [x,y] : 0 <= x <= 10 and 0 >= y >= -1 and x+y >= 0; [0,1] }" }, + { 1, "{ [x,y] : (0 <= x,y <= 4) or (2 <= x,y <= 5 and x + y <= 9) }" }, +}; + +/* Test the functionality of isl_set_coalesce with the bounded wrapping + * option turned off. + */ +int test_coalesce_unbounded_wrapping(isl_ctx *ctx) +{ + int i; + int r = 0; + int bounded; + + bounded = isl_options_get_coalesce_bounded_wrapping(ctx); + isl_options_set_coalesce_bounded_wrapping(ctx, 0); + + for (i = 0; i < ARRAY_SIZE(coalesce_unbounded_tests); ++i) { + const char *str = coalesce_unbounded_tests[i].str; + int check_one = coalesce_unbounded_tests[i].single_disjunct; + if (test_coalesce_set(ctx, str, check_one) >= 0) + continue; + r = -1; + break; + } + + isl_options_set_coalesce_bounded_wrapping(ctx, bounded); + + return r; +} + +/* Inputs for coalescing tests. + * "str" is a string representation of the input set. + * "single_disjunct" is set if we expect the result to consist of + * a single disjunct. + */ +struct { + int single_disjunct; + const char *str; +} coalesce_tests[] = { + { 1, "{[x,y]: x >= 0 & x <= 10 & y >= 0 & y <= 10 or " + "y >= x & x >= 2 & 5 >= y }" }, + { 1, "{[x,y]: y >= 0 & 2x + y <= 30 & y <= 10 & x >= 0 or " + "x + y >= 10 & y <= x & x + y <= 20 & y >= 0}" }, + { 0, "{[x,y]: y >= 0 & 2x + y <= 30 & y <= 10 & x >= 0 or " + "x + y >= 10 & y <= x & x + y <= 19 & y >= 0}" }, + { 1, "{[x,y]: y >= 0 & x <= 5 & y <= x or " + "y >= 0 & x >= 6 & x <= 10 & y <= x}" }, + { 0, "{[x,y]: y >= 0 & x <= 5 & y <= x or " + "y >= 0 & x >= 7 & x <= 10 & y <= x}" }, + { 0, "{[x,y]: y >= 0 & x <= 5 & y <= x or " + "y >= 0 & x >= 6 & x <= 10 & y + 1 <= x}" }, + { 1, "{[x,y]: y >= 0 & x <= 5 & y <= x or y >= 0 & x = 6 & y <= 6}" }, + { 0, "{[x,y]: y >= 0 & x <= 5 & y <= x or y >= 0 & x = 7 & y <= 6}" }, + { 1, "{[x,y]: y >= 0 & x <= 5 & y <= x or y >= 0 & x = 6 & y <= 5}" }, + { 0, "{[x,y]: y >= 0 & x <= 5 & y <= x or y >= 0 & x = 6 & y <= 7}" }, + { 1, "[n] -> { [i] : i = 1 and n >= 2 or 2 <= i and i <= n }" }, + { 0, "{[x,y] : x >= 0 and y >= 0 or 0 <= y and y <= 5 and x = -1}" }, + { 1, "[n] -> { [i] : 1 <= i and i <= n - 1 or 2 <= i and i <= n }" }, + { 0, "[n] -> { [[i0] -> [o0]] : exists (e0 = [(i0)/4], e1 = [(o0)/4], " + "e2 = [(n)/2], e3 = [(-2 + i0)/4], e4 = [(-2 + o0)/4], " + "e5 = [(-2n + i0)/4]: 2e2 = n and 4e3 = -2 + i0 and " + "4e4 = -2 + o0 and i0 >= 8 + 2n and o0 >= 2 + i0 and " + "o0 <= 56 + 2n and o0 <= -12 + 4n and i0 <= 57 + 2n and " + "i0 <= -11 + 4n and o0 >= 6 + 2n and 4e0 <= i0 and " + "4e0 >= -3 + i0 and 4e1 <= o0 and 4e1 >= -3 + o0 and " + "4e5 <= -2n + i0 and 4e5 >= -3 - 2n + i0);" + "[[i0] -> [o0]] : exists (e0 = [(i0)/4], e1 = [(o0)/4], " + "e2 = [(n)/2], e3 = [(-2 + i0)/4], e4 = [(-2 + o0)/4], " + "e5 = [(-2n + i0)/4]: 2e2 = n and 4e3 = -2 + i0 and " + "4e4 = -2 + o0 and 2e0 >= 3 + n and e0 <= -4 + n and " + "2e0 <= 27 + n and e1 <= -4 + n and 2e1 <= 27 + n and " + "2e1 >= 2 + n and e1 >= 1 + e0 and i0 >= 7 + 2n and " + "i0 <= -11 + 4n and i0 <= 57 + 2n and 4e0 <= -2 + i0 and " + "4e0 >= -3 + i0 and o0 >= 6 + 2n and o0 <= -11 + 4n and " + "o0 <= 57 + 2n and 4e1 <= -2 + o0 and 4e1 >= -3 + o0 and " + "4e5 <= -2n + i0 and 4e5 >= -3 - 2n + i0 ) }" }, + { 0, "[n, m] -> { [o0, o2, o3] : (o3 = 1 and o0 >= 1 + m and " + "o0 <= n + m and o2 <= m and o0 >= 2 + n and o2 >= 3) or " + "(o0 >= 2 + n and o0 >= 1 + m and o0 <= n + m and n >= 1 and " + "o3 <= -1 + o2 and o3 >= 1 - m + o2 and o3 >= 2 and o3 <= n) }" }, + { 0, "[M, N] -> { [[i0, i1, i2, i3, i4, i5, i6] -> " + "[o0, o1, o2, o3, o4, o5, o6]] : " + "(o6 <= -4 + 2M - 2N + i0 + i1 - i2 + i6 - o0 - o1 + o2 and " + "o3 <= -2 + i3 and o6 >= 2 + i0 + i3 + i6 - o0 - o3 and " + "o6 >= 2 - M + N + i3 + i4 + i6 - o3 - o4 and o0 <= -1 + i0 and " + "o4 >= 4 - 3M + 3N - i0 - i1 + i2 + 2i3 + i4 + o0 + o1 - o2 - 2o3 " + "and o6 <= -3 + 2M - 2N + i3 + i4 - i5 + i6 - o3 - o4 + o5 and " + "2o6 <= -5 + 5M - 5N + 2i0 + i1 - i2 - i5 + 2i6 - 2o0 - o1 + o2 + o5 " + "and o6 >= 2i0 + i1 + i6 - 2o0 - o1 and " + "3o6 <= -5 + 4M - 4N + 2i0 + i1 - i2 + 2i3 + i4 - i5 + 3i6 " + "- 2o0 - o1 + o2 - 2o3 - o4 + o5) or " + "(N >= 2 and o3 <= -1 + i3 and o0 <= -1 + i0 and " + "o6 >= i3 + i6 - o3 and M >= 0 and " + "2o6 >= 1 + i0 + i3 + 2i6 - o0 - o3 and " + "o6 >= 1 - M + i0 + i6 - o0 and N >= 2M and o6 >= i0 + i6 - o0) }" }, + { 0, "[M, N] -> { [o0] : (o0 = 0 and M >= 1 and N >= 2) or " + "(o0 = 0 and M >= 1 and N >= 2M and N >= 2 + M) or " + "(o0 = 0 and M >= 2 and N >= 3) or " + "(M = 0 and o0 = 0 and N >= 3) }" }, + { 0, "{ [i0, i1, i2, i3] : (i1 = 10i0 and i0 >= 1 and 10i0 <= 100 and " + "i3 <= 9 + 10 i2 and i3 >= 1 + 10i2 and i3 >= 0) or " + "(i1 <= 9 + 10i0 and i1 >= 1 + 10i0 and i2 >= 0 and " + "i0 >= 0 and i1 <= 100 and i3 <= 9 + 10i2 and i3 >= 1 + 10i2) }" }, + { 0, "[M] -> { [i1] : (i1 >= 2 and i1 <= M) or (i1 = M and M >= 1) }" }, + { 0, "{[x,y] : x,y >= 0; [x,y] : 10 <= x <= 20 and y >= -1 }" }, + { 1, "{ [x, y] : (x >= 1 and y >= 1 and x <= 2 and y <= 2) or " + "(y = 3 and x = 1) }" }, + { 1, "[M] -> { [i0, i1, i2, i3, i4] : (i1 >= 3 and i4 >= 2 + i2 and " + "i2 >= 2 and i0 >= 2 and i3 >= 1 + i2 and i0 <= M and " + "i1 <= M and i3 <= M and i4 <= M) or " + "(i1 >= 2 and i4 >= 1 + i2 and i2 >= 2 and i0 >= 2 and " + "i3 >= 1 + i2 and i0 <= M and i1 <= -1 + M and i3 <= M and " + "i4 <= -1 + M) }" }, + { 1, "{ [x, y] : (x >= 0 and y >= 0 and x <= 10 and y <= 10) or " + "(x >= 1 and y >= 1 and x <= 11 and y <= 11) }" }, + { 0, "{[x,0] : x >= 0; [x,1] : x <= 20}" }, + { 1, "{ [x, 1 - x] : 0 <= x <= 1; [0,0] }" }, + { 1, "{ [0,0]; [i,i] : 1 <= i <= 10 }" }, + { 0, "{ [0,0]; [i,j] : 1 <= i,j <= 10 }" }, + { 1, "{ [0,0]; [i,2i] : 1 <= i <= 10 }" }, + { 0, "{ [0,0]; [i,2i] : 2 <= i <= 10 }" }, + { 0, "{ [1,0]; [i,2i] : 1 <= i <= 10 }" }, + { 0, "{ [0,1]; [i,2i] : 1 <= i <= 10 }" }, + { 0, "{ [a, b] : exists e : 2e = a and " + "a >= 0 and (a <= 3 or (b <= 0 and b >= -4 + a)) }" }, + { 0, "{ [i, j, i', j'] : i <= 2 and j <= 2 and " + "j' >= -1 + 2i + j - 2i' and i' <= -1 + i and " + "j >= 1 and j' <= i + j - i' and i >= 1; " + "[1, 1, 1, 1] }" }, + { 1, "{ [i,j] : exists a,b : i = 2a and j = 3b; " + "[i,j] : exists a : j = 3a }" }, + { 1, "{ [a, b, c] : (c <= 7 - b and b <= 1 and b >= 0 and " + "c >= 3 + b and b <= 3 + 8a and b >= -26 + 8a and " + "a >= 3) or " + "(b <= 1 and c <= 7 and b >= 0 and c >= 4 + b and " + "b <= 3 + 8a and b >= -26 + 8a and a >= 3) }" }, + { 1, "{ [a, 0, c] : c >= 1 and c <= 29 and c >= -1 + 8a and " + "c <= 6 + 8a and a >= 3; " + "[a, -1, c] : c >= 1 and c <= 30 and c >= 8a and " + "c <= 7 + 8a and a >= 3 and a <= 4 }" }, + { 1, "{ [x,y] : 0 <= x <= 2 and y >= 0 and x + 2y <= 4; " + "[x,0] : 3 <= x <= 4 }" }, + { 1, "{ [x,y] : 0 <= x <= 3 and y >= 0 and x + 3y <= 6; " + "[x,0] : 4 <= x <= 5 }" }, + { 0, "{ [x,y] : 0 <= x <= 2 and y >= 0 and x + 2y <= 4; " + "[x,0] : 3 <= x <= 5 }" }, + { 0, "{ [x,y] : 0 <= x <= 2 and y >= 0 and x + y <= 4; " + "[x,0] : 3 <= x <= 4 }" }, + { 1, "{ [i0, i1] : i0 <= 122 and i0 >= 1 and 128i1 >= -249 + i0 and " + "i1 <= 0; " + "[i0, 0] : i0 >= 123 and i0 <= 124 }" }, + { 1, "{ [0,0]; [1,1] }" }, + { 1, "[n] -> { [k] : 16k <= -1 + n and k >= 1; [0] : n >= 2 }" }, + { 1, "{ [k, ii, k - ii] : ii >= -6 + k and ii <= 6 and ii >= 1 and " + "ii <= k;" + "[k, 0, k] : k <= 6 and k >= 1 }" }, + { 1, "{ [i,j] : i = 4 j and 0 <= i <= 100;" + "[i,j] : 1 <= i <= 100 and i >= 4j + 1 and i <= 4j + 2 }" }, + { 1, "{ [x,y] : x % 2 = 0 and y % 2 = 0; [x,x] : x % 2 = 0 }" }, + { 1, "[n] -> { [1] : n >= 0;" + "[x] : exists (e0 = floor((x)/2): x >= 2 and " + "2e0 >= -1 + x and 2e0 <= x and 2e0 <= n) }" }, + { 1, "[n] -> { [x, y] : exists (e0 = floor((x)/2), e1 = floor((y)/3): " + "3e1 = y and x >= 2 and 2e0 >= -1 + x and " + "2e0 <= x and 2e0 <= n);" + "[1, y] : exists (e0 = floor((y)/3): 3e0 = y and " + "n >= 0) }" }, + { 1, "[t1] -> { [i0] : (exists (e0 = floor((63t1)/64): " + "128e0 >= -134 + 127t1 and t1 >= 2 and " + "64e0 <= 63t1 and 64e0 >= -63 + 63t1)) or " + "t1 = 1 }" }, + { 1, "{ [i, i] : exists (e0 = floor((1 + 2i)/3): 3e0 <= 2i and " + "3e0 >= -1 + 2i and i <= 9 and i >= 1);" + "[0, 0] }" }, + { 1, "{ [t1] : exists (e0 = floor((-11 + t1)/2): 2e0 = -11 + t1 and " + "t1 >= 13 and t1 <= 16);" + "[t1] : t1 <= 15 and t1 >= 12 }" }, + { 1, "{ [x,y] : x = 3y and 0 <= y <= 2; [-3,-1] }" }, + { 1, "{ [x,y] : 2x = 3y and 0 <= y <= 4; [-3,-2] }" }, + { 0, "{ [x,y] : 2x = 3y and 0 <= y <= 4; [-2,-2] }" }, + { 0, "{ [x,y] : 2x = 3y and 0 <= y <= 4; [-3,-1] }" }, + { 1, "{ [i] : exists j : i = 4 j and 0 <= i <= 100;" + "[i] : exists j : 1 <= i <= 100 and i >= 4j + 1 and " + "i <= 4j + 2 }" }, + { 1, "{ [c0] : (exists (e0 : c0 - 1 <= 3e0 <= c0)) or " + "(exists (e0 : 3e0 = -2 + c0)) }" }, + { 0, "[n, b0, t0] -> " + "{ [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12] : " + "(exists (e0 = floor((-32b0 + i4)/1048576), " + "e1 = floor((i8)/32): 1048576e0 = -32b0 + i4 and 32e1 = i8 and " + "n <= 2147483647 and b0 <= 32767 and b0 >= 0 and " + "32b0 <= -2 + n and t0 <= 31 and t0 >= 0 and i0 >= 8 + n and " + "3i4 <= -96 + 3t0 + i0 and 3i4 >= -95 - n + 3t0 + i0 and " + "i8 >= -157 + i0 - 4i4 and i8 >= 0 and " + "i8 <= -33 + i0 - 4i4 and 3i8 <= -91 + 4n - i0)) or " + "(exists (e0 = floor((-32b0 + i4)/1048576), " + "e1 = floor((i8)/32): 1048576e0 = -32b0 + i4 and 32e1 = i8 and " + "n <= 2147483647 and b0 <= 32767 and b0 >= 0 and " + "32b0 <= -2 + n and t0 <= 31 and t0 >= 0 and i0 <= 7 + n and " + "4i4 <= -3 + i0 and 3i4 <= -96 + 3t0 + i0 and " + "3i4 >= -95 - n + 3t0 + i0 and i8 >= -157 + i0 - 4i4 and " + "i8 >= 0 and i8 <= -4 + i0 - 3i4 and i8 <= -41 + i0));" + "[i0, i1, i2, i3, 0, i5, i6, i7, i8, i9, i10, i11, i12] : " + "(exists (e0 = floor((i8)/32): b0 = 0 and 32e0 = i8 and " + "n <= 2147483647 and t0 <= 31 and t0 >= 0 and i0 >= 11 and " + "i0 >= 96 - 3t0 and i0 <= 95 + n - 3t0 and i0 <= 7 + n and " + "i8 >= -40 + i0 and i8 <= -10 + i0)) }" }, + { 0, "{ [i0, i1, i2] : " + "(exists (e0, e1 = floor((i0)/32), e2 = floor((i1)/32): " + "32e1 = i0 and 32e2 = i1 and i1 >= -31 + i0 and " + "i1 <= 31 + i0 and i2 >= -30 + i0 and i2 >= -30 + i1 and " + "32e0 >= -30 + i0 and 32e0 >= -30 + i1 and " + "32e0 >= -31 + i2 and 32e0 <= 30 + i2 and 32e0 <= 31 + i1 and " + "32e0 <= 31 + i0)) or " + "i0 >= 0 }" }, + { 1, "{ [a, b, c] : 2b = 1 + a and 2c = 2 + a; [0, 0, 0] }" }, + { 1, "{ [a, a, b, c] : 32*floor((a)/32) = a and 2*floor((b)/2) = b and " + "2*floor((c)/2) = c and 0 <= a <= 192;" + "[224, 224, b, c] : 2*floor((b)/2) = b and 2*floor((c)/2) = c }" + }, + { 1, "[n] -> { [a,b] : (exists e : 1 <= a <= 7e and 9e <= b <= n) or " + "(0 <= a <= b <= n) }" }, + { 1, "{ [a, b] : 0 <= a <= 2 and b >= 0 and " + "((0 < b <= 13) or (2*floor((a + b)/2) >= -5 + a + 2b)) }" }, + { 1, "{ [a] : (2 <= a <= 5) or (a mod 2 = 1 and 1 <= a <= 5) }" }, + { 1, "{ [a, b, c] : (b = -1 + a and 0 < a <= 3 and " + "9*floor((-4a + 2c)/9) <= -3 - 4a + 2c) or " + "(exists (e0 = floor((-16 + 2c)/9): a = 4 and " + "b = 3 and 9e0 <= -19 + 2c)) }" }, + { 0, "{ [a, b, c] : (b <= 2 and b <= -2 + a) or " + "(b = -1 + a and 0 < a <= 3 and " + "9*floor((-4a + 2c)/9) <= -3 - 4a + 2c) or " + "(exists (e0 = floor((-16 + 2c)/9): a = 4 and " + "b = 3 and 9e0 <= -19 + 2c)) }" }, + { 1, "{ [y, x] : (x - y) mod 3 = 2 and 2 <= y <= 200 and 0 <= x <= 2;" + "[1, 0] }" }, + { 1, "{ [x, y] : (x - y) mod 3 = 2 and 2 <= y <= 200 and 0 <= x <= 2;" + "[0, 1] }" }, + { 1, "{ [1, y] : -1 <= y <= 1; [x, -x] : 0 <= x <= 1 }" }, + { 1, "{ [1, y] : 0 <= y <= 1; [x, -x] : 0 <= x <= 1 }" }, + { 1, "{ [x, y] : 0 <= x <= 10 and x - 4*floor(x/4) <= 1 and y <= 0; " + "[x, y] : 0 <= x <= 10 and x - 4*floor(x/4) > 1 and y <= 0; " + "[x, y] : 0 <= x <= 10 and x - 5*floor(x/5) <= 1 and 0 < y; " + "[x, y] : 0 <= x <= 10 and x - 5*floor(x/5) > 1 and 0 < y }" }, + { 1, "{ [x, 0] : 0 <= x <= 10 and x mod 2 = 0; " + "[x, 0] : 0 <= x <= 10 and x mod 2 = 1; " + "[x, y] : 0 <= x <= 10 and 1 <= y <= 10 }" }, +}; + +/* A specialized coalescing test case that would result + * in a segmentation fault or a failed assertion in earlier versions of isl. + */ +static int test_coalesce_special(struct isl_ctx *ctx) +{ + const char *str; + isl_map *map1, *map2; + + str = "[y] -> { [S_L220_OUT[] -> T7[]] -> " + "[[S_L309_IN[] -> T11[]] -> ce_imag2[1, o1]] : " + "(y = 201 and o1 <= 239 and o1 >= 212) or " + "(exists (e0 = [(y)/3]: 3e0 = y and y <= 198 and y >= 3 and " + "o1 <= 239 and o1 >= 212)) or " + "(exists (e0 = [(y)/3]: 3e0 = y and y <= 201 and y >= 3 and " + "o1 <= 241 and o1 >= 240));" + "[S_L220_OUT[] -> T7[]] -> " + "[[S_L309_IN[] -> T11[]] -> ce_imag2[0, o1]] : " + "(y = 2 and o1 <= 241 and o1 >= 212) or " + "(exists (e0 = [(-2 + y)/3]: 3e0 = -2 + y and y <= 200 and " + "y >= 5 and o1 <= 241 and o1 >= 212)) }"; + map1 = isl_map_read_from_str(ctx, str); + map1 = isl_map_align_divs(map1); + map1 = isl_map_coalesce(map1); + str = "[y] -> { [S_L220_OUT[] -> T7[]] -> " + "[[S_L309_IN[] -> T11[]] -> ce_imag2[o0, o1]] : " + "exists (e0 = [(-1 - y + o0)/3]: 3e0 = -1 - y + o0 and " + "y <= 201 and o0 <= 2 and o1 >= 212 and o1 <= 241 and " + "o0 >= 3 - y and o0 <= -2 + y and o0 >= 0) }"; + map2 = isl_map_read_from_str(ctx, str); + map2 = isl_map_union(map2, map1); + map2 = isl_map_align_divs(map2); + map2 = isl_map_coalesce(map2); + isl_map_free(map2); + if (!map2) + return -1; + + return 0; +} + +/* Test the functionality of isl_set_coalesce. + * That is, check that the output is always equal to the input + * and in some cases that the result consists of a single disjunct. + */ +static int test_coalesce(struct isl_ctx *ctx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coalesce_tests); ++i) { + const char *str = coalesce_tests[i].str; + int check_one = coalesce_tests[i].single_disjunct; + if (test_coalesce_set(ctx, str, check_one) < 0) + return -1; + } + + if (test_coalesce_unbounded_wrapping(ctx) < 0) + return -1; + if (test_coalesce_special(ctx) < 0) + return -1; + + return 0; +} + +/* Construct a representation of the graph on the right of Figure 1 + * in "Computing the Transitive Closure of a Union of + * Affine Integer Tuple Relations". + */ +static __isl_give isl_map *cocoa_fig_1_right_graph(isl_ctx *ctx) +{ + isl_set *dom; + isl_map *up, *right; + + dom = isl_set_read_from_str(ctx, + "{ [x,y] : x >= 0 and -2 x + 3 y >= 0 and x <= 3 and " + "2 x - 3 y + 3 >= 0 }"); + right = isl_map_read_from_str(ctx, + "{ [x,y] -> [x2,y2] : x2 = x + 1 and y2 = y }"); + up = isl_map_read_from_str(ctx, + "{ [x,y] -> [x2,y2] : x2 = x and y2 = y + 1 }"); + right = isl_map_intersect_domain(right, isl_set_copy(dom)); + right = isl_map_intersect_range(right, isl_set_copy(dom)); + up = isl_map_intersect_domain(up, isl_set_copy(dom)); + up = isl_map_intersect_range(up, dom); + return isl_map_union(up, right); +} + +/* Construct a representation of the power of the graph + * on the right of Figure 1 in "Computing the Transitive Closure of + * a Union of Affine Integer Tuple Relations". + */ +static __isl_give isl_map *cocoa_fig_1_right_power(isl_ctx *ctx) +{ + return isl_map_read_from_str(ctx, + "{ [1] -> [[0,0] -> [0,1]]; [2] -> [[0,0] -> [1,1]]; " + " [1] -> [[0,1] -> [1,1]]; [1] -> [[2,2] -> [3,2]]; " + " [2] -> [[2,2] -> [3,3]]; [1] -> [[3,2] -> [3,3]] }"); +} + +/* Construct a representation of the transitive closure of the graph + * on the right of Figure 1 in "Computing the Transitive Closure of + * a Union of Affine Integer Tuple Relations". + */ +static __isl_give isl_map *cocoa_fig_1_right_tc(isl_ctx *ctx) +{ + return isl_set_unwrap(isl_map_range(cocoa_fig_1_right_power(ctx))); +} + +static int test_closure(isl_ctx *ctx) +{ + const char *str; + isl_map *map, *map2; + int exact, equal; + + /* COCOA example 1 */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i + 1 and j2 = j + 1 and " + "1 <= i and i < n and 1 <= j and j < n or " + "i2 = i + 1 and j2 = j - 1 and " + "1 <= i and i < n and 2 <= j and j <= n }"); + map = isl_map_power(map, &exact); + assert(exact); + isl_map_free(map); + + /* COCOA example 1 */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i + 1 and j2 = j + 1 and " + "1 <= i and i < n and 1 <= j and j < n or " + "i2 = i + 1 and j2 = j - 1 and " + "1 <= i and i < n and 2 <= j and j <= n }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : exists (k1,k2,k : " + "1 <= i and i < n and 1 <= j and j <= n and " + "2 <= i2 and i2 <= n and 1 <= j2 and j2 <= n and " + "i2 = i + k1 + k2 and j2 = j + k1 - k2 and " + "k1 >= 0 and k2 >= 0 and k1 + k2 = k and k >= 1 )}"); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map2); + isl_map_free(map); + + map = isl_map_read_from_str(ctx, + "[n] -> { [x] -> [y] : y = x + 1 and 0 <= x and x <= n and " + " 0 <= y and y <= n }"); + map = isl_map_transitive_closure(map, &exact); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [x] -> [y] : y > x and 0 <= x and x <= n and " + " 0 <= y and y <= n }"); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map2); + isl_map_free(map); + + /* COCOA example 2 */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i + 2 and j2 = j + 2 and " + "1 <= i and i < n - 1 and 1 <= j and j < n - 1 or " + "i2 = i + 2 and j2 = j - 2 and " + "1 <= i and i < n - 1 and 3 <= j and j <= n }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : exists (k1,k2,k : " + "1 <= i and i < n - 1 and 1 <= j and j <= n and " + "3 <= i2 and i2 <= n and 1 <= j2 and j2 <= n and " + "i2 = i + 2 k1 + 2 k2 and j2 = j + 2 k1 - 2 k2 and " + "k1 >= 0 and k2 >= 0 and k1 + k2 = k and k >= 1) }"); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + /* COCOA Fig.2 left */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i + 2 and j2 = j and " + "i <= 2 j - 3 and i <= n - 2 and j <= 2 i - 1 and " + "j <= n or " + "i2 = i and j2 = j + 2 and i <= 2 j - 1 and i <= n and " + "j <= 2 i - 3 and j <= n - 2 or " + "i2 = i + 1 and j2 = j + 1 and i <= 2 j - 1 and " + "i <= n - 1 and j <= 2 i - 1 and j <= n - 1 }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + isl_map_free(map); + + /* COCOA Fig.2 right */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i + 3 and j2 = j and " + "i <= 2 j - 4 and i <= n - 3 and j <= 2 i - 1 and " + "j <= n or " + "i2 = i and j2 = j + 3 and i <= 2 j - 1 and i <= n and " + "j <= 2 i - 4 and j <= n - 3 or " + "i2 = i + 1 and j2 = j + 1 and i <= 2 j - 1 and " + "i <= n - 1 and j <= 2 i - 1 and j <= n - 1 }"); + map = isl_map_power(map, &exact); + assert(exact); + isl_map_free(map); + + /* COCOA Fig.2 right */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i + 3 and j2 = j and " + "i <= 2 j - 4 and i <= n - 3 and j <= 2 i - 1 and " + "j <= n or " + "i2 = i and j2 = j + 3 and i <= 2 j - 1 and i <= n and " + "j <= 2 i - 4 and j <= n - 3 or " + "i2 = i + 1 and j2 = j + 1 and i <= 2 j - 1 and " + "i <= n - 1 and j <= 2 i - 1 and j <= n - 1 }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : exists (k1,k2,k3,k : " + "i <= 2 j - 1 and i <= n and j <= 2 i - 1 and " + "j <= n and 3 + i + 2 j <= 3 n and " + "3 + 2 i + j <= 3n and i2 <= 2 j2 -1 and i2 <= n and " + "i2 <= 3 j2 - 4 and j2 <= 2 i2 -1 and j2 <= n and " + "13 + 4 j2 <= 11 i2 and i2 = i + 3 k1 + k3 and " + "j2 = j + 3 k2 + k3 and k1 >= 0 and k2 >= 0 and " + "k3 >= 0 and k1 + k2 + k3 = k and k > 0) }"); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map2); + isl_map_free(map); + + map = cocoa_fig_1_right_graph(ctx); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = cocoa_fig_1_right_tc(ctx); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map2); + isl_map_free(map); + + map = cocoa_fig_1_right_graph(ctx); + map = isl_map_power(map, &exact); + map2 = cocoa_fig_1_right_power(ctx); + equal = isl_map_is_equal(map, map2); + isl_map_free(map2); + isl_map_free(map); + if (equal < 0) + return -1; + if (!exact) + isl_die(ctx, isl_error_unknown, "power not exact", return -1); + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected power", return -1); + + /* COCOA Theorem 1 counter example */ + map = isl_map_read_from_str(ctx, + "{ [i,j] -> [i2,j2] : i = 0 and 0 <= j and j <= 1 and " + "i2 = 1 and j2 = j or " + "i = 0 and j = 0 and i2 = 0 and j2 = 1 }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + isl_map_free(map); + + map = isl_map_read_from_str(ctx, + "[m,n] -> { [i,j] -> [i2,j2] : i2 = i and j2 = j + 2 and " + "1 <= i,i2 <= n and 1 <= j,j2 <= m or " + "i2 = i + 1 and 3 <= j2 - j <= 4 and " + "1 <= i,i2 <= n and 1 <= j,j2 <= m }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + isl_map_free(map); + + /* Kelly et al 1996, fig 12 */ + map = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : i2 = i and j2 = j + 1 and " + "1 <= i,j,j+1 <= n or " + "j = n and j2 = 1 and i2 = i + 1 and " + "1 <= i,i+1 <= n }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [i,j] -> [i2,j2] : 1 <= j < j2 <= n and " + "1 <= i <= n and i = i2 or " + "1 <= i < i2 <= n and 1 <= j <= n and " + "1 <= j2 <= n }"); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map2); + isl_map_free(map); + + /* Omega's closure4 */ + map = isl_map_read_from_str(ctx, + "[m,n] -> { [x,y] -> [x2,y2] : x2 = x and y2 = y + 1 and " + "1 <= x,y <= 10 or " + "x2 = x + 1 and y2 = y and " + "1 <= x <= 20 && 5 <= y <= 15 }"); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + isl_map_free(map); + + map = isl_map_read_from_str(ctx, + "[n] -> { [x] -> [y]: 1 <= n <= y - x <= 10 }"); + map = isl_map_transitive_closure(map, &exact); + assert(!exact); + map2 = isl_map_read_from_str(ctx, + "[n] -> { [x] -> [y] : 1 <= n <= 10 and y >= n + x }"); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "[n, m] -> { [i0, i1, i2, i3] -> [o0, o1, o2, o3] : " + "i3 = 1 and o0 = i0 and o1 = -1 + i1 and o2 = -1 + i2 and " + "o3 = -2 + i2 and i1 <= -1 + i0 and i1 >= 1 - m + i0 and " + "i1 >= 2 and i1 <= n and i2 >= 3 and i2 <= 1 + n and i2 <= m }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "{[0] -> [1]; [2] -> [3]}"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_transitive_closure(map, &exact); + assert(exact); + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "[n] -> { [[i0, i1, 1, 0, i0] -> [i5, 1]] -> " + "[[i0, -1 + i1, 2, 0, i0] -> [-1 + i5, 2]] : " + "exists (e0 = [(3 - n)/3]: i5 >= 2 and i1 >= 2 and " + "3i0 <= -1 + n and i1 <= -1 + n and i5 <= -1 + n and " + "3e0 >= 1 - n and 3e0 <= 2 - n and 3i0 >= -2 + n); " + "[[i0, i1, 2, 0, i0] -> [i5, 1]] -> " + "[[i0, i1, 1, 0, i0] -> [-1 + i5, 2]] : " + "exists (e0 = [(3 - n)/3]: i5 >= 2 and i1 >= 1 and " + "3i0 <= -1 + n and i1 <= -1 + n and i5 <= -1 + n and " + "3e0 >= 1 - n and 3e0 <= 2 - n and 3i0 >= -2 + n); " + "[[i0, i1, 1, 0, i0] -> [i5, 2]] -> " + "[[i0, -1 + i1, 2, 0, i0] -> [i5, 1]] : " + "exists (e0 = [(3 - n)/3]: i1 >= 2 and i5 >= 1 and " + "3i0 <= -1 + n and i1 <= -1 + n and i5 <= -1 + n and " + "3e0 >= 1 - n and 3e0 <= 2 - n and 3i0 >= -2 + n); " + "[[i0, i1, 2, 0, i0] -> [i5, 2]] -> " + "[[i0, i1, 1, 0, i0] -> [i5, 1]] : " + "exists (e0 = [(3 - n)/3]: i5 >= 1 and i1 >= 1 and " + "3i0 <= -1 + n and i1 <= -1 + n and i5 <= -1 + n and " + "3e0 >= 1 - n and 3e0 <= 2 - n and 3i0 >= -2 + n) }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_transitive_closure(map, NULL); + assert(map); + isl_map_free(map); + + return 0; +} + +static int test_lex(struct isl_ctx *ctx) +{ + isl_space *dim; + isl_map *map; + int empty; + + dim = isl_space_set_alloc(ctx, 0, 0); + map = isl_map_lex_le(dim); + empty = isl_map_is_empty(map); + isl_map_free(map); + + if (empty < 0) + return -1; + if (empty) + isl_die(ctx, isl_error_unknown, + "expecting non-empty result", return -1); + + return 0; +} + +static int test_lexmin(struct isl_ctx *ctx) +{ + int equal; + const char *str; + isl_basic_map *bmap; + isl_map *map, *map2; + isl_set *set; + isl_set *set2; + isl_pw_multi_aff *pma; + + str = "[p0, p1] -> { [] -> [] : " + "exists (e0 = [(2p1)/3], e1, e2, e3 = [(3 - p1 + 3e0)/3], " + "e4 = [(p1)/3], e5 = [(p1 + 3e4)/3]: " + "3e0 >= -2 + 2p1 and 3e0 >= p1 and 3e3 >= 1 - p1 + 3e0 and " + "3e0 <= 2p1 and 3e3 >= -2 + p1 and 3e3 <= -1 + p1 and p1 >= 3 and " + "3e5 >= -2 + 2p1 and 3e5 >= p1 and 3e5 <= -1 + p1 + 3e4 and " + "3e4 <= p1 and 3e4 >= -2 + p1 and e3 <= -1 + e0 and " + "3e4 >= 6 - p1 + 3e1 and 3e1 >= p1 and 3e5 >= -2 + p1 + 3e4 and " + "2e4 >= 3 - p1 + 2e1 and e4 <= e1 and 3e3 <= 2 - p1 + 3e0 and " + "e5 >= 1 + e1 and 3e4 >= 6 - 2p1 + 3e1 and " + "p0 >= 2 and p1 >= p0 and 3e2 >= p1 and 3e4 >= 6 - p1 + 3e2 and " + "e2 <= e1 and e3 >= 1 and e4 <= e2) }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_lexmin(map); + isl_map_free(map); + + str = "[C] -> { [obj,a,b,c] : obj <= 38 a + 7 b + 10 c and " + "a + b <= 1 and c <= 10 b and c <= C and a,b,c,C >= 0 }"; + set = isl_set_read_from_str(ctx, str); + set = isl_set_lexmax(set); + str = "[C] -> { [obj,a,b,c] : C = 8 }"; + set2 = isl_set_read_from_str(ctx, str); + set = isl_set_intersect(set, set2); + assert(!isl_set_is_empty(set)); + isl_set_free(set); + + str = "{ [x] -> [y] : x <= y <= 10; [x] -> [5] : -8 <= x <= 8 }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_lexmin(map); + str = "{ [x] -> [5] : 6 <= x <= 8; " + "[x] -> [x] : x <= 5 or (9 <= x <= 10) }"; + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "{ [x] -> [y] : 4y = x or 4y = -1 + x or 4y = -2 + x }"; + map = isl_map_read_from_str(ctx, str); + map2 = isl_map_copy(map); + map = isl_map_lexmin(map); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "{ [x] -> [y] : x = 4y; [x] -> [y] : x = 2y }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_lexmin(map); + str = "{ [x] -> [y] : (4y = x and x >= 0) or " + "(exists (e0 = [(x)/4], e1 = [(-2 + x)/4]: 2y = x and " + "4e1 = -2 + x and 4e0 <= -1 + x and 4e0 >= -3 + x)) or " + "(exists (e0 = [(x)/4]: 2y = x and 4e0 = x and x <= -4)) }"; + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "{ [i] -> [i', j] : j = i - 8i' and i' >= 0 and i' <= 7 and " + " 8i' <= i and 8i' >= -7 + i }"; + bmap = isl_basic_map_read_from_str(ctx, str); + pma = isl_basic_map_lexmin_pw_multi_aff(isl_basic_map_copy(bmap)); + map2 = isl_map_from_pw_multi_aff(pma); + map = isl_map_from_basic_map(bmap); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "{ T[a] -> S[b, c] : a = 4b-2c and c >= b }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_lexmin(map); + str = "{ T[a] -> S[b, c] : 2b = a and 2c = a }"; + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + /* Check that empty pieces are properly combined. */ + str = "[K, N] -> { [x, y] -> [a, b] : K+2<=N<=K+4 and x>=4 and " + "2N-6<=x=N and a>=x+1 }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_lexmin(map); + str = "[K, N] -> { [x, y] -> [1 + x, N] : x >= -6 + 2N and " + "x <= -5 + 2N and x >= -1 + 3K - N and x <= -2 + K + N and " + "x >= 4 }"; + map2 = isl_map_read_from_str(ctx, str); + assert(isl_map_is_equal(map, map2)); + isl_map_free(map); + isl_map_free(map2); + + str = "[i] -> { [i', j] : j = i - 8i' and i' >= 0 and i' <= 7 and " + " 8i' <= i and 8i' >= -7 + i }"; + set = isl_set_read_from_str(ctx, str); + pma = isl_set_lexmin_pw_multi_aff(isl_set_copy(set)); + set2 = isl_set_from_pw_multi_aff(pma); + equal = isl_set_is_equal(set, set2); + isl_set_free(set); + isl_set_free(set2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected difference between set and " + "piecewise affine expression", return -1); + + return 0; +} + +struct { + const char *set; + const char *obj; + __isl_give isl_val *(*fn)(__isl_keep isl_set *set, + __isl_keep isl_aff *obj); + const char *res; +} opt_tests[] = { + { "{ [-1]; [1] }", "{ [x] -> [x] }", &isl_set_min_val, "-1" }, + { "{ [-1]; [1] }", "{ [x] -> [x] }", &isl_set_max_val, "1" }, + { "{ [a, b] : 0 <= a, b <= 100 and b mod 2 = 0}", + "{ [a, b] -> [floor((b - 2*floor((-a)/4))/5)] }", + &isl_set_max_val, "30" }, + +}; + +/* Perform basic isl_set_min_val and isl_set_max_val tests. + * In particular, check the results on non-convex inputs. + */ +static int test_min(struct isl_ctx *ctx) +{ + int i; + isl_set *set; + isl_aff *obj; + isl_val *val, *res; + isl_bool ok; + + for (i = 0; i < ARRAY_SIZE(opt_tests); ++i) { + set = isl_set_read_from_str(ctx, opt_tests[i].set); + obj = isl_aff_read_from_str(ctx, opt_tests[i].obj); + res = isl_val_read_from_str(ctx, opt_tests[i].res); + val = opt_tests[i].fn(set, obj); + ok = isl_val_eq(res, val); + isl_val_free(res); + isl_val_free(val); + isl_aff_free(obj); + isl_set_free(set); + + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, + "unexpected optimum", return -1); + } + + return 0; +} + +struct must_may { + isl_map *must; + isl_map *may; +}; + +static isl_stat collect_must_may(__isl_take isl_map *dep, int must, + void *dep_user, void *user) +{ + struct must_may *mm = (struct must_may *)user; + + if (must) + mm->must = isl_map_union(mm->must, dep); + else + mm->may = isl_map_union(mm->may, dep); + + return isl_stat_ok; +} + +static int common_space(void *first, void *second) +{ + int depth = *(int *)first; + return 2 * depth; +} + +static int map_is_equal(__isl_keep isl_map *map, const char *str) +{ + isl_map *map2; + int equal; + + if (!map) + return -1; + + map2 = isl_map_read_from_str(map->ctx, str); + equal = isl_map_is_equal(map, map2); + isl_map_free(map2); + + return equal; +} + +static int map_check_equal(__isl_keep isl_map *map, const char *str) +{ + int equal; + + equal = map_is_equal(map, str); + if (equal < 0) + return -1; + if (!equal) + isl_die(isl_map_get_ctx(map), isl_error_unknown, + "result not as expected", return -1); + return 0; +} + +static int test_dep(struct isl_ctx *ctx) +{ + const char *str; + isl_space *dim; + isl_map *map; + isl_access_info *ai; + isl_flow *flow; + int depth; + struct must_may mm; + + depth = 3; + + str = "{ [2,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_alloc(map, &depth, &common_space, 2); + + str = "{ [0,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 1, &depth); + + str = "{ [1,i,0] -> [5] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 1, &depth); + + flow = isl_access_info_compute_flow(ai); + dim = isl_space_alloc(ctx, 0, 3, 3); + mm.must = isl_map_empty(isl_space_copy(dim)); + mm.may = isl_map_empty(dim); + + isl_flow_foreach(flow, collect_must_may, &mm); + + str = "{ [0,i,0] -> [2,i,0] : (0 <= i <= 4) or (6 <= i <= 10); " + " [1,10,0] -> [2,5,0] }"; + assert(map_is_equal(mm.must, str)); + str = "{ [i,j,k] -> [l,m,n] : 1 = 0 }"; + assert(map_is_equal(mm.may, str)); + + isl_map_free(mm.must); + isl_map_free(mm.may); + isl_flow_free(flow); + + + str = "{ [2,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_alloc(map, &depth, &common_space, 2); + + str = "{ [0,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 1, &depth); + + str = "{ [1,i,0] -> [5] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + flow = isl_access_info_compute_flow(ai); + dim = isl_space_alloc(ctx, 0, 3, 3); + mm.must = isl_map_empty(isl_space_copy(dim)); + mm.may = isl_map_empty(dim); + + isl_flow_foreach(flow, collect_must_may, &mm); + + str = "{ [0,i,0] -> [2,i,0] : (0 <= i <= 4) or (6 <= i <= 10) }"; + assert(map_is_equal(mm.must, str)); + str = "{ [0,5,0] -> [2,5,0]; [1,i,0] -> [2,5,0] : 0 <= i <= 10 }"; + assert(map_is_equal(mm.may, str)); + + isl_map_free(mm.must); + isl_map_free(mm.may); + isl_flow_free(flow); + + + str = "{ [2,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_alloc(map, &depth, &common_space, 2); + + str = "{ [0,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + str = "{ [1,i,0] -> [5] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + flow = isl_access_info_compute_flow(ai); + dim = isl_space_alloc(ctx, 0, 3, 3); + mm.must = isl_map_empty(isl_space_copy(dim)); + mm.may = isl_map_empty(dim); + + isl_flow_foreach(flow, collect_must_may, &mm); + + str = "{ [0,i,0] -> [2,i,0] : 0 <= i <= 10; " + " [1,i,0] -> [2,5,0] : 0 <= i <= 10 }"; + assert(map_is_equal(mm.may, str)); + str = "{ [i,j,k] -> [l,m,n] : 1 = 0 }"; + assert(map_is_equal(mm.must, str)); + + isl_map_free(mm.must); + isl_map_free(mm.may); + isl_flow_free(flow); + + + str = "{ [0,i,2] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_alloc(map, &depth, &common_space, 2); + + str = "{ [0,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + str = "{ [0,i,1] -> [5] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + flow = isl_access_info_compute_flow(ai); + dim = isl_space_alloc(ctx, 0, 3, 3); + mm.must = isl_map_empty(isl_space_copy(dim)); + mm.may = isl_map_empty(dim); + + isl_flow_foreach(flow, collect_must_may, &mm); + + str = "{ [0,i,0] -> [0,i,2] : 0 <= i <= 10; " + " [0,i,1] -> [0,5,2] : 0 <= i <= 5 }"; + assert(map_is_equal(mm.may, str)); + str = "{ [i,j,k] -> [l,m,n] : 1 = 0 }"; + assert(map_is_equal(mm.must, str)); + + isl_map_free(mm.must); + isl_map_free(mm.may); + isl_flow_free(flow); + + + str = "{ [0,i,1] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_alloc(map, &depth, &common_space, 2); + + str = "{ [0,i,0] -> [i] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + str = "{ [0,i,2] -> [5] : 0 <= i <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 0, &depth); + + flow = isl_access_info_compute_flow(ai); + dim = isl_space_alloc(ctx, 0, 3, 3); + mm.must = isl_map_empty(isl_space_copy(dim)); + mm.may = isl_map_empty(dim); + + isl_flow_foreach(flow, collect_must_may, &mm); + + str = "{ [0,i,0] -> [0,i,1] : 0 <= i <= 10; " + " [0,i,2] -> [0,5,1] : 0 <= i <= 4 }"; + assert(map_is_equal(mm.may, str)); + str = "{ [i,j,k] -> [l,m,n] : 1 = 0 }"; + assert(map_is_equal(mm.must, str)); + + isl_map_free(mm.must); + isl_map_free(mm.may); + isl_flow_free(flow); + + + depth = 5; + + str = "{ [1,i,0,0,0] -> [i,j] : 0 <= i <= 10 and 0 <= j <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_alloc(map, &depth, &common_space, 1); + + str = "{ [0,i,0,j,0] -> [i,j] : 0 <= i <= 10 and 0 <= j <= 10 }"; + map = isl_map_read_from_str(ctx, str); + ai = isl_access_info_add_source(ai, map, 1, &depth); + + flow = isl_access_info_compute_flow(ai); + dim = isl_space_alloc(ctx, 0, 5, 5); + mm.must = isl_map_empty(isl_space_copy(dim)); + mm.may = isl_map_empty(dim); + + isl_flow_foreach(flow, collect_must_may, &mm); + + str = "{ [0,i,0,j,0] -> [1,i,0,0,0] : 0 <= i,j <= 10 }"; + assert(map_is_equal(mm.must, str)); + str = "{ [0,0,0,0,0] -> [0,0,0,0,0] : 1 = 0 }"; + assert(map_is_equal(mm.may, str)); + + isl_map_free(mm.must); + isl_map_free(mm.may); + isl_flow_free(flow); + + return 0; +} + +/* Check that the dependence analysis proceeds without errors. + * Earlier versions of isl would break down during the analysis + * due to the use of the wrong spaces. + */ +static int test_flow(isl_ctx *ctx) +{ + const char *str; + isl_union_map *access, *schedule; + isl_union_map *must_dep, *may_dep; + int r; + + str = "{ S0[j] -> i[]; S1[j,i] -> i[]; S2[] -> i[]; S3[] -> i[] }"; + access = isl_union_map_read_from_str(ctx, str); + str = "{ S0[j] -> [0,j,0,0] : 0 <= j < 10; " + "S1[j,i] -> [0,j,1,i] : 0 <= j < i < 10; " + "S2[] -> [1,0,0,0]; " + "S3[] -> [-1,0,0,0] }"; + schedule = isl_union_map_read_from_str(ctx, str); + r = isl_union_map_compute_flow(access, isl_union_map_copy(access), + isl_union_map_copy(access), schedule, + &must_dep, &may_dep, NULL, NULL); + isl_union_map_free(may_dep); + isl_union_map_free(must_dep); + + return r; +} + +struct { + const char *map; + int sv; +} sv_tests[] = { + { "[N] -> { [i] -> [f] : 0 <= i <= N and 0 <= i - 10 f <= 9 }", 1 }, + { "[N] -> { [i] -> [f] : 0 <= i <= N and 0 <= i - 10 f <= 10 }", 0 }, + { "{ [i] -> [3*floor(i/2) + 5*floor(i/3)] }", 1 }, + { "{ S1[i] -> [i] : 0 <= i <= 9; S2[i] -> [i] : 0 <= i <= 9 }", 1 }, + { "{ [i] -> S1[i] : 0 <= i <= 9; [i] -> S2[i] : 0 <= i <= 9 }", 0 }, + { "{ A[i] -> [i]; B[i] -> [i]; B[i] -> [i + 1] }", 0 }, + { "{ A[i] -> [i]; B[i] -> [i] : i < 0; B[i] -> [i + 1] : i > 0 }", 1 }, + { "{ A[i] -> [i]; B[i] -> A[i] : i < 0; B[i] -> [i + 1] : i > 0 }", 1 }, + { "{ A[i] -> [i]; B[i] -> [j] : i - 1 <= j <= i }", 0 }, +}; + +int test_sv(isl_ctx *ctx) +{ + isl_union_map *umap; + int i; + int sv; + + for (i = 0; i < ARRAY_SIZE(sv_tests); ++i) { + umap = isl_union_map_read_from_str(ctx, sv_tests[i].map); + sv = isl_union_map_is_single_valued(umap); + isl_union_map_free(umap); + if (sv < 0) + return -1; + if (sv_tests[i].sv && !sv) + isl_die(ctx, isl_error_internal, + "map not detected as single valued", return -1); + if (!sv_tests[i].sv && sv) + isl_die(ctx, isl_error_internal, + "map detected as single valued", return -1); + } + + return 0; +} + +struct { + const char *str; + int bijective; +} bijective_tests[] = { + { "[N,M]->{[i,j] -> [i]}", 0 }, + { "[N,M]->{[i,j] -> [i] : j=i}", 1 }, + { "[N,M]->{[i,j] -> [i] : j=0}", 1 }, + { "[N,M]->{[i,j] -> [i] : j=N}", 1 }, + { "[N,M]->{[i,j] -> [j,i]}", 1 }, + { "[N,M]->{[i,j] -> [i+j]}", 0 }, + { "[N,M]->{[i,j] -> []}", 0 }, + { "[N,M]->{[i,j] -> [i,j,N]}", 1 }, + { "[N,M]->{[i,j] -> [2i]}", 0 }, + { "[N,M]->{[i,j] -> [i,i]}", 0 }, + { "[N,M]->{[i,j] -> [2i,i]}", 0 }, + { "[N,M]->{[i,j] -> [2i,j]}", 1 }, + { "[N,M]->{[i,j] -> [x,y] : 2x=i & y =j}", 1 }, +}; + +static int test_bijective(struct isl_ctx *ctx) +{ + isl_map *map; + int i; + int bijective; + + for (i = 0; i < ARRAY_SIZE(bijective_tests); ++i) { + map = isl_map_read_from_str(ctx, bijective_tests[i].str); + bijective = isl_map_is_bijective(map); + isl_map_free(map); + if (bijective < 0) + return -1; + if (bijective_tests[i].bijective && !bijective) + isl_die(ctx, isl_error_internal, + "map not detected as bijective", return -1); + if (!bijective_tests[i].bijective && bijective) + isl_die(ctx, isl_error_internal, + "map detected as bijective", return -1); + } + + return 0; +} + +/* Inputs for isl_pw_qpolynomial_gist tests. + * "pwqp" is the input, "set" is the context and "gist" is the expected result. + */ +struct { + const char *pwqp; + const char *set; + const char *gist; +} pwqp_gist_tests[] = { + { "{ [i] -> i }", "{ [k] : exists a : k = 2a }", "{ [i] -> i }" }, + { "{ [i] -> i + [ (i + [i/3])/2 ] }", "{ [10] }", "{ [i] -> 16 }" }, + { "{ [i] -> ([(i)/2]) }", "{ [k] : exists a : k = 2a+1 }", + "{ [i] -> -1/2 + 1/2 * i }" }, + { "{ [i] -> i^2 : i != 0 }", "{ [i] : i != 0 }", "{ [i] -> i^2 }" }, +}; + +static int test_pwqp(struct isl_ctx *ctx) +{ + int i; + const char *str; + isl_set *set; + isl_pw_qpolynomial *pwqp1, *pwqp2; + int equal; + + str = "{ [i,j,k] -> 1 + 9 * [i/5] + 7 * [j/11] + 4 * [k/13] }"; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + + pwqp1 = isl_pw_qpolynomial_move_dims(pwqp1, isl_dim_param, 0, + isl_dim_in, 1, 1); + + str = "[j] -> { [i,k] -> 1 + 9 * [i/5] + 7 * [j/11] + 4 * [k/13] }"; + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + + pwqp1 = isl_pw_qpolynomial_sub(pwqp1, pwqp2); + + assert(isl_pw_qpolynomial_is_zero(pwqp1)); + + isl_pw_qpolynomial_free(pwqp1); + + for (i = 0; i < ARRAY_SIZE(pwqp_gist_tests); ++i) { + str = pwqp_gist_tests[i].pwqp; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + str = pwqp_gist_tests[i].set; + set = isl_set_read_from_str(ctx, str); + pwqp1 = isl_pw_qpolynomial_gist(pwqp1, set); + str = pwqp_gist_tests[i].gist; + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + pwqp1 = isl_pw_qpolynomial_sub(pwqp1, pwqp2); + equal = isl_pw_qpolynomial_is_zero(pwqp1); + isl_pw_qpolynomial_free(pwqp1); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + } + + str = "{ [i] -> ([([i/2] + [i/2])/5]) }"; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + str = "{ [i] -> ([(2 * [i/2])/5]) }"; + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + + pwqp1 = isl_pw_qpolynomial_sub(pwqp1, pwqp2); + + assert(isl_pw_qpolynomial_is_zero(pwqp1)); + + isl_pw_qpolynomial_free(pwqp1); + + str = "{ [x] -> ([x/2] + [(x+1)/2]) }"; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + str = "{ [x] -> x }"; + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + + pwqp1 = isl_pw_qpolynomial_sub(pwqp1, pwqp2); + + assert(isl_pw_qpolynomial_is_zero(pwqp1)); + + isl_pw_qpolynomial_free(pwqp1); + + str = "{ [i] -> ([i/2]) : i >= 0; [i] -> ([i/3]) : i < 0 }"; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + pwqp1 = isl_pw_qpolynomial_coalesce(pwqp1); + pwqp1 = isl_pw_qpolynomial_sub(pwqp1, pwqp2); + assert(isl_pw_qpolynomial_is_zero(pwqp1)); + isl_pw_qpolynomial_free(pwqp1); + + str = "{ [a,b,a] -> (([(2*[a/3]+b)/5]) * ([(2*[a/3]+b)/5])) }"; + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + str = "{ [a,b,c] -> (([(2*[a/3]+b)/5]) * ([(2*[c/3]+b)/5])) }"; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + set = isl_set_read_from_str(ctx, "{ [a,b,a] }"); + pwqp1 = isl_pw_qpolynomial_intersect_domain(pwqp1, set); + equal = isl_pw_qpolynomial_plain_is_equal(pwqp1, pwqp2); + isl_pw_qpolynomial_free(pwqp1); + isl_pw_qpolynomial_free(pwqp2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + str = "{ [a,b,c] -> (([(2*[a/3]+1)/5]) * ([(2*[c/3]+1)/5])) : b = 1 }"; + pwqp2 = isl_pw_qpolynomial_read_from_str(ctx, str); + str = "{ [a,b,c] -> (([(2*[a/3]+b)/5]) * ([(2*[c/3]+b)/5])) }"; + pwqp1 = isl_pw_qpolynomial_read_from_str(ctx, str); + pwqp1 = isl_pw_qpolynomial_fix_val(pwqp1, isl_dim_set, 1, + isl_val_one(ctx)); + equal = isl_pw_qpolynomial_plain_is_equal(pwqp1, pwqp2); + isl_pw_qpolynomial_free(pwqp1); + isl_pw_qpolynomial_free(pwqp2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + return 0; +} + +static int test_split_periods(isl_ctx *ctx) +{ + const char *str; + isl_pw_qpolynomial *pwqp; + + str = "{ [U,V] -> 1/3 * U + 2/3 * V - [(U + 2V)/3] + [U/2] : " + "U + 2V + 3 >= 0 and - U -2V >= 0 and - U + 10 >= 0 and " + "U >= 0; [U,V] -> U^2 : U >= 100 }"; + pwqp = isl_pw_qpolynomial_read_from_str(ctx, str); + + pwqp = isl_pw_qpolynomial_split_periods(pwqp, 2); + + isl_pw_qpolynomial_free(pwqp); + + if (!pwqp) + return -1; + + return 0; +} + +static int test_union(isl_ctx *ctx) +{ + const char *str; + isl_union_set *uset1, *uset2; + isl_union_map *umap1, *umap2; + int equal; + + str = "{ [i] : 0 <= i <= 1 }"; + uset1 = isl_union_set_read_from_str(ctx, str); + str = "{ [1] -> [0] }"; + umap1 = isl_union_map_read_from_str(ctx, str); + + umap2 = isl_union_set_lex_gt_union_set(isl_union_set_copy(uset1), uset1); + equal = isl_union_map_is_equal(umap1, umap2); + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "union maps not equal", + return -1); + + str = "{ A[i] -> B[i]; B[i] -> C[i]; A[0] -> C[1] }"; + umap1 = isl_union_map_read_from_str(ctx, str); + str = "{ A[i]; B[i] }"; + uset1 = isl_union_set_read_from_str(ctx, str); + + uset2 = isl_union_map_domain(umap1); + + equal = isl_union_set_is_equal(uset1, uset2); + + isl_union_set_free(uset1); + isl_union_set_free(uset2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "union sets not equal", + return -1); + + return 0; +} + +/* Check that computing a bound of a non-zero polynomial over an unbounded + * domain does not produce a rational value. + * In particular, check that the upper bound is infinity. + */ +static int test_bound_unbounded_domain(isl_ctx *ctx) +{ + const char *str; + isl_pw_qpolynomial *pwqp; + isl_pw_qpolynomial_fold *pwf, *pwf2; + isl_bool equal; + + str = "{ [m,n] -> -m * n }"; + pwqp = isl_pw_qpolynomial_read_from_str(ctx, str); + pwf = isl_pw_qpolynomial_bound(pwqp, isl_fold_max, NULL); + str = "{ infty }"; + pwqp = isl_pw_qpolynomial_read_from_str(ctx, str); + pwf2 = isl_pw_qpolynomial_bound(pwqp, isl_fold_max, NULL); + equal = isl_pw_qpolynomial_fold_plain_is_equal(pwf, pwf2); + isl_pw_qpolynomial_fold_free(pwf); + isl_pw_qpolynomial_fold_free(pwf2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "expecting infinite polynomial bound", return -1); + + return 0; +} + +static int test_bound(isl_ctx *ctx) +{ + const char *str; + unsigned dim; + isl_pw_qpolynomial *pwqp; + isl_pw_qpolynomial_fold *pwf; + + if (test_bound_unbounded_domain(ctx) < 0) + return -1; + + str = "{ [[a, b, c, d] -> [e]] -> 0 }"; + pwqp = isl_pw_qpolynomial_read_from_str(ctx, str); + pwf = isl_pw_qpolynomial_bound(pwqp, isl_fold_max, NULL); + dim = isl_pw_qpolynomial_fold_dim(pwf, isl_dim_in); + isl_pw_qpolynomial_fold_free(pwf); + if (dim != 4) + isl_die(ctx, isl_error_unknown, "unexpected input dimension", + return -1); + + str = "{ [[x]->[x]] -> 1 : exists a : x = 2 a }"; + pwqp = isl_pw_qpolynomial_read_from_str(ctx, str); + pwf = isl_pw_qpolynomial_bound(pwqp, isl_fold_max, NULL); + dim = isl_pw_qpolynomial_fold_dim(pwf, isl_dim_in); + isl_pw_qpolynomial_fold_free(pwf); + if (dim != 1) + isl_die(ctx, isl_error_unknown, "unexpected input dimension", + return -1); + + return 0; +} + +static int test_lift(isl_ctx *ctx) +{ + const char *str; + isl_basic_map *bmap; + isl_basic_set *bset; + + str = "{ [i0] : exists e0 : i0 = 4e0 }"; + bset = isl_basic_set_read_from_str(ctx, str); + bset = isl_basic_set_lift(bset); + bmap = isl_basic_map_from_range(bset); + bset = isl_basic_map_domain(bmap); + isl_basic_set_free(bset); + + return 0; +} + +struct { + const char *set1; + const char *set2; + int subset; +} subset_tests[] = { + { "{ [112, 0] }", + "{ [i0, i1] : exists (e0 = [(i0 - i1)/16], e1: " + "16e0 <= i0 - i1 and 16e0 >= -15 + i0 - i1 and " + "16e1 <= i1 and 16e0 >= -i1 and 16e1 >= -i0 + i1) }", 1 }, + { "{ [65] }", + "{ [i] : exists (e0 = [(255i)/256], e1 = [(127i + 65e0)/191], " + "e2 = [(3i + 61e1)/65], e3 = [(52i + 12e2)/61], " + "e4 = [(2i + e3)/3], e5 = [(4i + e3)/4], e6 = [(8i + e3)/12]: " + "3e4 = 2i + e3 and 4e5 = 4i + e3 and 12e6 = 8i + e3 and " + "i <= 255 and 64e3 >= -45 + 67i and i >= 0 and " + "256e0 <= 255i and 256e0 >= -255 + 255i and " + "191e1 <= 127i + 65e0 and 191e1 >= -190 + 127i + 65e0 and " + "65e2 <= 3i + 61e1 and 65e2 >= -64 + 3i + 61e1 and " + "61e3 <= 52i + 12e2 and 61e3 >= -60 + 52i + 12e2) }", 1 }, + { "{ [i] : 0 <= i <= 10 }", "{ rat: [i] : 0 <= i <= 10 }", 1 }, + { "{ rat: [i] : 0 <= i <= 10 }", "{ [i] : 0 <= i <= 10 }", 0 }, + { "{ rat: [0] }", "{ [i] : 0 <= i <= 10 }", 1 }, + { "{ rat: [(1)/2] }", "{ [i] : 0 <= i <= 10 }", 0 }, + { "{ [t, i] : (exists (e0 = [(2 + t)/4]: 4e0 <= 2 + t and " + "4e0 >= -1 + t and i >= 57 and i <= 62 and " + "4e0 <= 62 + t - i and 4e0 >= -61 + t + i and " + "t >= 0 and t <= 511 and 4e0 <= -57 + t + i and " + "4e0 >= 58 + t - i and i >= 58 + t and i >= 62 - t)) }", + "{ [i0, i1] : (exists (e0 = [(4 + i0)/4]: 4e0 <= 62 + i0 - i1 and " + "4e0 >= 1 + i0 and i0 >= 0 and i0 <= 511 and " + "4e0 <= -57 + i0 + i1)) or " + "(exists (e0 = [(2 + i0)/4]: 4e0 <= i0 and " + "4e0 >= 58 + i0 - i1 and i0 >= 2 and i0 <= 511 and " + "4e0 >= -61 + i0 + i1)) or " + "(i1 <= 66 - i0 and i0 >= 2 and i1 >= 59 + i0) }", 1 }, + { "[a, b] -> { : a = 0 and b = -1 }", "[b, a] -> { : b >= -10 }", 1 }, +}; + +static int test_subset(isl_ctx *ctx) +{ + int i; + isl_set *set1, *set2; + int subset; + + for (i = 0; i < ARRAY_SIZE(subset_tests); ++i) { + set1 = isl_set_read_from_str(ctx, subset_tests[i].set1); + set2 = isl_set_read_from_str(ctx, subset_tests[i].set2); + subset = isl_set_is_subset(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + if (subset < 0) + return -1; + if (subset != subset_tests[i].subset) + isl_die(ctx, isl_error_unknown, + "incorrect subset result", return -1); + } + + return 0; +} + +struct { + const char *minuend; + const char *subtrahend; + const char *difference; +} subtract_domain_tests[] = { + { "{ A[i] -> B[i] }", "{ A[i] }", "{ }" }, + { "{ A[i] -> B[i] }", "{ B[i] }", "{ A[i] -> B[i] }" }, + { "{ A[i] -> B[i] }", "{ A[i] : i > 0 }", "{ A[i] -> B[i] : i <= 0 }" }, +}; + +static int test_subtract(isl_ctx *ctx) +{ + int i; + isl_union_map *umap1, *umap2; + isl_union_pw_multi_aff *upma1, *upma2; + isl_union_set *uset; + int equal; + + for (i = 0; i < ARRAY_SIZE(subtract_domain_tests); ++i) { + umap1 = isl_union_map_read_from_str(ctx, + subtract_domain_tests[i].minuend); + uset = isl_union_set_read_from_str(ctx, + subtract_domain_tests[i].subtrahend); + umap2 = isl_union_map_read_from_str(ctx, + subtract_domain_tests[i].difference); + umap1 = isl_union_map_subtract_domain(umap1, uset); + equal = isl_union_map_is_equal(umap1, umap2); + isl_union_map_free(umap1); + isl_union_map_free(umap2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "incorrect subtract domain result", return -1); + } + + for (i = 0; i < ARRAY_SIZE(subtract_domain_tests); ++i) { + upma1 = isl_union_pw_multi_aff_read_from_str(ctx, + subtract_domain_tests[i].minuend); + uset = isl_union_set_read_from_str(ctx, + subtract_domain_tests[i].subtrahend); + upma2 = isl_union_pw_multi_aff_read_from_str(ctx, + subtract_domain_tests[i].difference); + upma1 = isl_union_pw_multi_aff_subtract_domain(upma1, uset); + equal = isl_union_pw_multi_aff_plain_is_equal(upma1, upma2); + isl_union_pw_multi_aff_free(upma1); + isl_union_pw_multi_aff_free(upma2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "incorrect subtract domain result", return -1); + } + + return 0; +} + +/* Check that intersecting the empty basic set with another basic set + * does not increase the number of constraints. In particular, + * the empty basic set should maintain its canonical representation. + */ +static int test_intersect(isl_ctx *ctx) +{ + int n1, n2; + isl_basic_set *bset1, *bset2; + + bset1 = isl_basic_set_read_from_str(ctx, "{ [a,b,c] : 1 = 0 }"); + bset2 = isl_basic_set_read_from_str(ctx, "{ [1,2,3] }"); + n1 = isl_basic_set_n_constraint(bset1); + bset1 = isl_basic_set_intersect(bset1, bset2); + n2 = isl_basic_set_n_constraint(bset1); + isl_basic_set_free(bset1); + if (!bset1) + return -1; + if (n1 != n2) + isl_die(ctx, isl_error_unknown, + "number of constraints of empty set changed", + return -1); + + return 0; +} + +int test_factorize(isl_ctx *ctx) +{ + const char *str; + isl_basic_set *bset; + isl_factorizer *f; + + str = "{ [i0, i1, i2, i3, i4, i5, i6, i7] : 3i5 <= 2 - 2i0 and " + "i0 >= -2 and i6 >= 1 + i3 and i7 >= 0 and 3i5 >= -2i0 and " + "2i4 <= i2 and i6 >= 1 + 2i0 + 3i1 and i4 <= -1 and " + "i6 >= 1 + 2i0 + 3i5 and i6 <= 2 + 2i0 + 3i5 and " + "3i5 <= 2 - 2i0 - i2 + 3i4 and i6 <= 2 + 2i0 + 3i1 and " + "i0 <= -1 and i7 <= i2 + i3 - 3i4 - i6 and " + "3i5 >= -2i0 - i2 + 3i4 }"; + bset = isl_basic_set_read_from_str(ctx, str); + f = isl_basic_set_factorizer(bset); + isl_basic_set_free(bset); + isl_factorizer_free(f); + if (!f) + isl_die(ctx, isl_error_unknown, + "failed to construct factorizer", return -1); + + str = "{ [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12] : " + "i12 <= 2 + i0 - i11 and 2i8 >= -i4 and i11 >= i1 and " + "3i5 <= -i2 and 2i11 >= -i4 - 2i7 and i11 <= 3 + i0 + 3i9 and " + "i11 <= -i4 - 2i7 and i12 >= -i10 and i2 >= -2 and " + "i11 >= i1 + 3i10 and i11 >= 1 + i0 + 3i9 and " + "i11 <= 1 - i4 - 2i8 and 6i6 <= 6 - i2 and 3i6 >= 1 - i2 and " + "i11 <= 2 + i1 and i12 <= i4 + i11 and i12 >= i0 - i11 and " + "3i5 >= -2 - i2 and i12 >= -1 + i4 + i11 and 3i3 <= 3 - i2 and " + "9i6 <= 11 - i2 + 6i5 and 3i3 >= 1 - i2 and " + "9i6 <= 5 - i2 + 6i3 and i12 <= -1 and i2 <= 0 }"; + bset = isl_basic_set_read_from_str(ctx, str); + f = isl_basic_set_factorizer(bset); + isl_basic_set_free(bset); + isl_factorizer_free(f); + if (!f) + isl_die(ctx, isl_error_unknown, + "failed to construct factorizer", return -1); + + return 0; +} + +static isl_stat check_injective(__isl_take isl_map *map, void *user) +{ + int *injective = user; + + *injective = isl_map_is_injective(map); + isl_map_free(map); + + if (*injective < 0 || !*injective) + return isl_stat_error; + + return isl_stat_ok; +} + +int test_one_schedule(isl_ctx *ctx, const char *d, const char *w, + const char *r, const char *s, int tilable, int parallel) +{ + int i; + isl_union_set *D; + isl_union_map *W, *R, *S; + isl_union_map *empty; + isl_union_map *dep_raw, *dep_war, *dep_waw, *dep; + isl_union_map *validity, *proximity, *coincidence; + isl_union_map *schedule; + isl_union_map *test; + isl_union_set *delta; + isl_union_set *domain; + isl_set *delta_set; + isl_set *slice; + isl_set *origin; + isl_schedule_constraints *sc; + isl_schedule *sched; + int is_nonneg, is_parallel, is_tilable, is_injection, is_complete; + + D = isl_union_set_read_from_str(ctx, d); + W = isl_union_map_read_from_str(ctx, w); + R = isl_union_map_read_from_str(ctx, r); + S = isl_union_map_read_from_str(ctx, s); + + W = isl_union_map_intersect_domain(W, isl_union_set_copy(D)); + R = isl_union_map_intersect_domain(R, isl_union_set_copy(D)); + + empty = isl_union_map_empty(isl_union_map_get_space(S)); + isl_union_map_compute_flow(isl_union_map_copy(R), + isl_union_map_copy(W), empty, + isl_union_map_copy(S), + &dep_raw, NULL, NULL, NULL); + isl_union_map_compute_flow(isl_union_map_copy(W), + isl_union_map_copy(W), + isl_union_map_copy(R), + isl_union_map_copy(S), + &dep_waw, &dep_war, NULL, NULL); + + dep = isl_union_map_union(dep_waw, dep_war); + dep = isl_union_map_union(dep, dep_raw); + validity = isl_union_map_copy(dep); + coincidence = isl_union_map_copy(dep); + proximity = isl_union_map_copy(dep); + + sc = isl_schedule_constraints_on_domain(isl_union_set_copy(D)); + sc = isl_schedule_constraints_set_validity(sc, validity); + sc = isl_schedule_constraints_set_coincidence(sc, coincidence); + sc = isl_schedule_constraints_set_proximity(sc, proximity); + sched = isl_schedule_constraints_compute_schedule(sc); + schedule = isl_schedule_get_map(sched); + isl_schedule_free(sched); + isl_union_map_free(W); + isl_union_map_free(R); + isl_union_map_free(S); + + is_injection = 1; + isl_union_map_foreach_map(schedule, &check_injective, &is_injection); + + domain = isl_union_map_domain(isl_union_map_copy(schedule)); + is_complete = isl_union_set_is_subset(D, domain); + isl_union_set_free(D); + isl_union_set_free(domain); + + test = isl_union_map_reverse(isl_union_map_copy(schedule)); + test = isl_union_map_apply_range(test, dep); + test = isl_union_map_apply_range(test, schedule); + + delta = isl_union_map_deltas(test); + if (isl_union_set_n_set(delta) == 0) { + is_tilable = 1; + is_parallel = 1; + is_nonneg = 1; + isl_union_set_free(delta); + } else { + delta_set = isl_set_from_union_set(delta); + + slice = isl_set_universe(isl_set_get_space(delta_set)); + for (i = 0; i < tilable; ++i) + slice = isl_set_lower_bound_si(slice, isl_dim_set, i, 0); + is_tilable = isl_set_is_subset(delta_set, slice); + isl_set_free(slice); + + slice = isl_set_universe(isl_set_get_space(delta_set)); + for (i = 0; i < parallel; ++i) + slice = isl_set_fix_si(slice, isl_dim_set, i, 0); + is_parallel = isl_set_is_subset(delta_set, slice); + isl_set_free(slice); + + origin = isl_set_universe(isl_set_get_space(delta_set)); + for (i = 0; i < isl_set_dim(origin, isl_dim_set); ++i) + origin = isl_set_fix_si(origin, isl_dim_set, i, 0); + + delta_set = isl_set_union(delta_set, isl_set_copy(origin)); + delta_set = isl_set_lexmin(delta_set); + + is_nonneg = isl_set_is_equal(delta_set, origin); + + isl_set_free(origin); + isl_set_free(delta_set); + } + + if (is_nonneg < 0 || is_parallel < 0 || is_tilable < 0 || + is_injection < 0 || is_complete < 0) + return -1; + if (!is_complete) + isl_die(ctx, isl_error_unknown, + "generated schedule incomplete", return -1); + if (!is_injection) + isl_die(ctx, isl_error_unknown, + "generated schedule not injective on each statement", + return -1); + if (!is_nonneg) + isl_die(ctx, isl_error_unknown, + "negative dependences in generated schedule", + return -1); + if (!is_tilable) + isl_die(ctx, isl_error_unknown, + "generated schedule not as tilable as expected", + return -1); + if (!is_parallel) + isl_die(ctx, isl_error_unknown, + "generated schedule not as parallel as expected", + return -1); + + return 0; +} + +/* Compute a schedule for the given instance set, validity constraints, + * proximity constraints and context and return a corresponding union map + * representation. + */ +static __isl_give isl_union_map *compute_schedule_with_context(isl_ctx *ctx, + const char *domain, const char *validity, const char *proximity, + const char *context) +{ + isl_set *con; + isl_union_set *dom; + isl_union_map *dep; + isl_union_map *prox; + isl_schedule_constraints *sc; + isl_schedule *schedule; + isl_union_map *sched; + + con = isl_set_read_from_str(ctx, context); + dom = isl_union_set_read_from_str(ctx, domain); + dep = isl_union_map_read_from_str(ctx, validity); + prox = isl_union_map_read_from_str(ctx, proximity); + sc = isl_schedule_constraints_on_domain(dom); + sc = isl_schedule_constraints_set_context(sc, con); + sc = isl_schedule_constraints_set_validity(sc, dep); + sc = isl_schedule_constraints_set_proximity(sc, prox); + schedule = isl_schedule_constraints_compute_schedule(sc); + sched = isl_schedule_get_map(schedule); + isl_schedule_free(schedule); + + return sched; +} + +/* Compute a schedule for the given instance set, validity constraints and + * proximity constraints and return a corresponding union map representation. + */ +static __isl_give isl_union_map *compute_schedule(isl_ctx *ctx, + const char *domain, const char *validity, const char *proximity) +{ + return compute_schedule_with_context(ctx, domain, validity, proximity, + "{ : }"); +} + +/* Check that a schedule can be constructed on the given domain + * with the given validity and proximity constraints. + */ +static int test_has_schedule(isl_ctx *ctx, const char *domain, + const char *validity, const char *proximity) +{ + isl_union_map *sched; + + sched = compute_schedule(ctx, domain, validity, proximity); + if (!sched) + return -1; + + isl_union_map_free(sched); + return 0; +} + +int test_special_schedule(isl_ctx *ctx, const char *domain, + const char *validity, const char *proximity, const char *expected_sched) +{ + isl_union_map *sched1, *sched2; + int equal; + + sched1 = compute_schedule(ctx, domain, validity, proximity); + sched2 = isl_union_map_read_from_str(ctx, expected_sched); + + equal = isl_union_map_is_equal(sched1, sched2); + isl_union_map_free(sched1); + isl_union_map_free(sched2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected schedule", + return -1); + + return 0; +} + +/* Check that the schedule map is properly padded, even after being + * reconstructed from the band forest. + */ +static int test_padded_schedule(isl_ctx *ctx) +{ + const char *str; + isl_union_set *D; + isl_union_map *validity, *proximity; + isl_schedule_constraints *sc; + isl_schedule *sched; + isl_union_map *map1, *map2; + isl_band_list *list; + int equal; + + str = "[N] -> { S0[i] : 0 <= i <= N; S1[i, j] : 0 <= i, j <= N }"; + D = isl_union_set_read_from_str(ctx, str); + validity = isl_union_map_empty(isl_union_set_get_space(D)); + proximity = isl_union_map_copy(validity); + sc = isl_schedule_constraints_on_domain(D); + sc = isl_schedule_constraints_set_validity(sc, validity); + sc = isl_schedule_constraints_set_proximity(sc, proximity); + sched = isl_schedule_constraints_compute_schedule(sc); + map1 = isl_schedule_get_map(sched); + list = isl_schedule_get_band_forest(sched); + isl_band_list_free(list); + map2 = isl_schedule_get_map(sched); + isl_schedule_free(sched); + equal = isl_union_map_is_equal(map1, map2); + isl_union_map_free(map1); + isl_union_map_free(map2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "reconstructed schedule map not the same as original", + return -1); + + return 0; +} + +/* Check that conditional validity constraints are also taken into + * account across bands. + * In particular, try to make sure that live ranges D[1,0]->C[2,1] and + * D[2,0]->C[3,0] are not local in the outer band of the generated schedule + * and then check that the adjacent order constraint C[2,1]->D[2,0] + * is enforced by the rest of the schedule. + */ +static int test_special_conditional_schedule_constraints(isl_ctx *ctx) +{ + const char *str; + isl_union_set *domain; + isl_union_map *validity, *proximity, *condition; + isl_union_map *sink, *source, *dep; + isl_schedule_constraints *sc; + isl_schedule *schedule; + isl_union_access_info *access; + isl_union_flow *flow; + int empty; + + str = "[n] -> { C[k, i] : k <= -1 + n and i >= 0 and i <= -1 + k; " + "A[k] : k >= 1 and k <= -1 + n; " + "B[k, i] : k <= -1 + n and i >= 0 and i <= -1 + k; " + "D[k, i] : k <= -1 + n and i >= 0 and i <= -1 + k }"; + domain = isl_union_set_read_from_str(ctx, str); + sc = isl_schedule_constraints_on_domain(domain); + str = "[n] -> { D[k, i] -> C[1 + k, k - i] : " + "k <= -2 + n and i >= 1 and i <= -1 + k; " + "D[k, i] -> C[1 + k, i] : " + "k <= -2 + n and i >= 1 and i <= -1 + k; " + "D[k, 0] -> C[1 + k, k] : k >= 1 and k <= -2 + n; " + "D[k, 0] -> C[1 + k, 0] : k >= 1 and k <= -2 + n }"; + validity = isl_union_map_read_from_str(ctx, str); + sc = isl_schedule_constraints_set_validity(sc, validity); + str = "[n] -> { C[k, i] -> D[k, i] : " + "0 <= i <= -1 + k and k <= -1 + n }"; + proximity = isl_union_map_read_from_str(ctx, str); + sc = isl_schedule_constraints_set_proximity(sc, proximity); + str = "[n] -> { [D[k, i] -> a[]] -> [C[1 + k, k - i] -> b[]] : " + "i <= -1 + k and i >= 1 and k <= -2 + n; " + "[B[k, i] -> c[]] -> [B[k, 1 + i] -> c[]] : " + "k <= -1 + n and i >= 0 and i <= -2 + k }"; + condition = isl_union_map_read_from_str(ctx, str); + str = "[n] -> { [B[k, i] -> e[]] -> [D[k, i] -> a[]] : " + "i >= 0 and i <= -1 + k and k <= -1 + n; " + "[C[k, i] -> b[]] -> [D[k', -1 + k - i] -> a[]] : " + "i >= 0 and i <= -1 + k and k <= -1 + n and " + "k' <= -1 + n and k' >= k - i and k' >= 1 + k; " + "[C[k, i] -> b[]] -> [D[k, -1 + k - i] -> a[]] : " + "i >= 0 and i <= -1 + k and k <= -1 + n; " + "[B[k, i] -> c[]] -> [A[k'] -> d[]] : " + "k <= -1 + n and i >= 0 and i <= -1 + k and " + "k' >= 1 and k' <= -1 + n and k' >= 1 + k }"; + validity = isl_union_map_read_from_str(ctx, str); + sc = isl_schedule_constraints_set_conditional_validity(sc, condition, + validity); + schedule = isl_schedule_constraints_compute_schedule(sc); + str = "{ D[2,0] -> [] }"; + sink = isl_union_map_read_from_str(ctx, str); + access = isl_union_access_info_from_sink(sink); + str = "{ C[2,1] -> [] }"; + source = isl_union_map_read_from_str(ctx, str); + access = isl_union_access_info_set_must_source(access, source); + access = isl_union_access_info_set_schedule(access, schedule); + flow = isl_union_access_info_compute_flow(access); + dep = isl_union_flow_get_must_dependence(flow); + isl_union_flow_free(flow); + empty = isl_union_map_is_empty(dep); + isl_union_map_free(dep); + + if (empty < 0) + return -1; + if (empty) + isl_die(ctx, isl_error_unknown, + "conditional validity not respected", return -1); + + return 0; +} + +/* Input for testing of schedule construction based on + * conditional constraints. + * + * domain is the iteration domain + * flow are the flow dependences, which determine the validity and + * proximity constraints + * condition are the conditions on the conditional validity constraints + * conditional_validity are the conditional validity constraints + * outer_band_n is the expected number of members in the outer band + */ +struct { + const char *domain; + const char *flow; + const char *condition; + const char *conditional_validity; + int outer_band_n; +} live_range_tests[] = { + /* Contrived example that illustrates that we need to keep + * track of tagged condition dependences and + * tagged conditional validity dependences + * in isl_sched_edge separately. + * In particular, the conditional validity constraints on A + * cannot be satisfied, + * but they can be ignored because there are no corresponding + * condition constraints. However, we do have an additional + * conditional validity constraint that maps to the same + * dependence relation + * as the condition constraint on B. If we did not make a distinction + * between tagged condition and tagged conditional validity + * dependences, then we + * could end up treating this shared dependence as an condition + * constraint on A, forcing a localization of the conditions, + * which is impossible. + */ + { "{ S[i] : 0 <= 1 < 100; T[i] : 0 <= 1 < 100 }", + "{ S[i] -> S[i+1] : 0 <= i < 99 }", + "{ [S[i] -> B[]] -> [S[i+1] -> B[]] : 0 <= i < 99 }", + "{ [S[i] -> A[]] -> [T[i'] -> A[]] : 0 <= i', i < 100 and i != i';" + "[T[i] -> A[]] -> [S[i'] -> A[]] : 0 <= i', i < 100 and i != i';" + "[S[i] -> A[]] -> [S[i+1] -> A[]] : 0 <= i < 99 }", + 1 + }, + /* TACO 2013 Fig. 7 */ + { "[n] -> { S1[i,j] : 0 <= i,j < n; S2[i,j] : 0 <= i,j < n }", + "[n] -> { S1[i,j] -> S2[i,j] : 0 <= i,j < n;" + "S2[i,j] -> S2[i,j+1] : 0 <= i < n and 0 <= j < n - 1 }", + "[n] -> { [S1[i,j] -> t[]] -> [S2[i,j] -> t[]] : 0 <= i,j < n;" + "[S2[i,j] -> x1[]] -> [S2[i,j+1] -> x1[]] : " + "0 <= i < n and 0 <= j < n - 1 }", + "[n] -> { [S2[i,j] -> t[]] -> [S1[i,j'] -> t[]] : " + "0 <= i < n and 0 <= j < j' < n;" + "[S2[i,j] -> t[]] -> [S1[i',j'] -> t[]] : " + "0 <= i < i' < n and 0 <= j,j' < n;" + "[S2[i,j] -> x1[]] -> [S2[i,j'] -> x1[]] : " + "0 <= i,j,j' < n and j < j' }", + 2 + }, + /* TACO 2013 Fig. 7, without tags */ + { "[n] -> { S1[i,j] : 0 <= i,j < n; S2[i,j] : 0 <= i,j < n }", + "[n] -> { S1[i,j] -> S2[i,j] : 0 <= i,j < n;" + "S2[i,j] -> S2[i,j+1] : 0 <= i < n and 0 <= j < n - 1 }", + "[n] -> { S1[i,j] -> S2[i,j] : 0 <= i,j < n;" + "S2[i,j] -> S2[i,j+1] : 0 <= i < n and 0 <= j < n - 1 }", + "[n] -> { S2[i,j] -> S1[i,j'] : 0 <= i < n and 0 <= j < j' < n;" + "S2[i,j] -> S1[i',j'] : 0 <= i < i' < n and 0 <= j,j' < n;" + "S2[i,j] -> S2[i,j'] : 0 <= i,j,j' < n and j < j' }", + 1 + }, + /* TACO 2013 Fig. 12 */ + { "{ S1[i,0] : 0 <= i <= 1; S2[i,j] : 0 <= i <= 1 and 1 <= j <= 2;" + "S3[i,3] : 0 <= i <= 1 }", + "{ S1[i,0] -> S2[i,1] : 0 <= i <= 1;" + "S2[i,1] -> S2[i,2] : 0 <= i <= 1;" + "S2[i,2] -> S3[i,3] : 0 <= i <= 1 }", + "{ [S1[i,0]->t[]] -> [S2[i,1]->t[]] : 0 <= i <= 1;" + "[S2[i,1]->t[]] -> [S2[i,2]->t[]] : 0 <= i <= 1;" + "[S2[i,2]->t[]] -> [S3[i,3]->t[]] : 0 <= i <= 1 }", + "{ [S2[i,1]->t[]] -> [S2[i,2]->t[]] : 0 <= i <= 1;" + "[S2[0,j]->t[]] -> [S2[1,j']->t[]] : 1 <= j,j' <= 2;" + "[S2[0,j]->t[]] -> [S1[1,0]->t[]] : 1 <= j <= 2;" + "[S3[0,3]->t[]] -> [S2[1,j]->t[]] : 1 <= j <= 2;" + "[S3[0,3]->t[]] -> [S1[1,0]->t[]] }", + 1 + } +}; + +/* Test schedule construction based on conditional constraints. + * In particular, check the number of members in the outer band node + * as an indication of whether tiling is possible or not. + */ +static int test_conditional_schedule_constraints(isl_ctx *ctx) +{ + int i; + isl_union_set *domain; + isl_union_map *condition; + isl_union_map *flow; + isl_union_map *validity; + isl_schedule_constraints *sc; + isl_schedule *schedule; + isl_schedule_node *node; + int n_member; + + if (test_special_conditional_schedule_constraints(ctx) < 0) + return -1; + + for (i = 0; i < ARRAY_SIZE(live_range_tests); ++i) { + domain = isl_union_set_read_from_str(ctx, + live_range_tests[i].domain); + flow = isl_union_map_read_from_str(ctx, + live_range_tests[i].flow); + condition = isl_union_map_read_from_str(ctx, + live_range_tests[i].condition); + validity = isl_union_map_read_from_str(ctx, + live_range_tests[i].conditional_validity); + sc = isl_schedule_constraints_on_domain(domain); + sc = isl_schedule_constraints_set_validity(sc, + isl_union_map_copy(flow)); + sc = isl_schedule_constraints_set_proximity(sc, flow); + sc = isl_schedule_constraints_set_conditional_validity(sc, + condition, validity); + schedule = isl_schedule_constraints_compute_schedule(sc); + node = isl_schedule_get_root(schedule); + while (node && + isl_schedule_node_get_type(node) != isl_schedule_node_band) + node = isl_schedule_node_first_child(node); + n_member = isl_schedule_node_band_n_member(node); + isl_schedule_node_free(node); + isl_schedule_free(schedule); + + if (!schedule) + return -1; + if (n_member != live_range_tests[i].outer_band_n) + isl_die(ctx, isl_error_unknown, + "unexpected number of members in outer band", + return -1); + } + return 0; +} + +/* Check that the schedule computed for the given instance set and + * dependence relation strongly satisfies the dependences. + * In particular, check that no instance is scheduled before + * or together with an instance on which it depends. + * Earlier versions of isl would produce a schedule that + * only weakly satisfies the dependences. + */ +static int test_strongly_satisfying_schedule(isl_ctx *ctx) +{ + const char *domain, *dep; + isl_union_map *D, *schedule; + isl_map *map, *ge; + int empty; + + domain = "{ B[i0, i1] : 0 <= i0 <= 1 and 0 <= i1 <= 11; " + "A[i0] : 0 <= i0 <= 1 }"; + dep = "{ B[i0, i1] -> B[i0, 1 + i1] : 0 <= i0 <= 1 and 0 <= i1 <= 10; " + "B[0, 11] -> A[1]; A[i0] -> B[i0, 0] : 0 <= i0 <= 1 }"; + schedule = compute_schedule(ctx, domain, dep, dep); + D = isl_union_map_read_from_str(ctx, dep); + D = isl_union_map_apply_domain(D, isl_union_map_copy(schedule)); + D = isl_union_map_apply_range(D, schedule); + map = isl_map_from_union_map(D); + ge = isl_map_lex_ge(isl_space_domain(isl_map_get_space(map))); + map = isl_map_intersect(map, ge); + empty = isl_map_is_empty(map); + isl_map_free(map); + + if (empty < 0) + return -1; + if (!empty) + isl_die(ctx, isl_error_unknown, + "dependences not strongly satisfied", return -1); + + return 0; +} + +/* Compute a schedule for input where the instance set constraints + * conflict with the context constraints. + * Earlier versions of isl did not properly handle this situation. + */ +static int test_conflicting_context_schedule(isl_ctx *ctx) +{ + isl_union_map *schedule; + const char *domain, *context; + + domain = "[n] -> { A[] : n >= 0 }"; + context = "[n] -> { : n < 0 }"; + schedule = compute_schedule_with_context(ctx, + domain, "{}", "{}", context); + isl_union_map_free(schedule); + + if (!schedule) + return -1; + + return 0; +} + +/* Check that the dependence carrying step is not confused by + * a bound on the coefficient size. + * In particular, force the scheduler to move to a dependence carrying + * step by demanding outer coincidence and bound the size of + * the coefficients. Earlier versions of isl would take this + * bound into account while carrying dependences, breaking + * fundamental assumptions. + * On the other hand, the dependence carrying step now tries + * to prevent loop coalescing by default, so check that indeed + * no loop coalescing occurs by comparing the computed schedule + * to the expected non-coalescing schedule. + */ +static int test_bounded_coefficients_schedule(isl_ctx *ctx) +{ + const char *domain, *dep; + isl_union_set *I; + isl_union_map *D; + isl_schedule_constraints *sc; + isl_schedule *schedule; + isl_union_map *sched1, *sched2; + isl_bool equal; + + domain = "{ C[i0, i1] : 2 <= i0 <= 3999 and 0 <= i1 <= -1 + i0 }"; + dep = "{ C[i0, i1] -> C[i0, 1 + i1] : i0 <= 3999 and i1 >= 0 and " + "i1 <= -2 + i0; " + "C[i0, -1 + i0] -> C[1 + i0, 0] : i0 <= 3998 and i0 >= 1 }"; + I = isl_union_set_read_from_str(ctx, domain); + D = isl_union_map_read_from_str(ctx, dep); + sc = isl_schedule_constraints_on_domain(I); + sc = isl_schedule_constraints_set_validity(sc, isl_union_map_copy(D)); + sc = isl_schedule_constraints_set_coincidence(sc, D); + isl_options_set_schedule_outer_coincidence(ctx, 1); + isl_options_set_schedule_max_coefficient(ctx, 20); + schedule = isl_schedule_constraints_compute_schedule(sc); + isl_options_set_schedule_max_coefficient(ctx, -1); + isl_options_set_schedule_outer_coincidence(ctx, 0); + sched1 = isl_schedule_get_map(schedule); + isl_schedule_free(schedule); + + sched2 = isl_union_map_read_from_str(ctx, "{ C[x,y] -> [x,y] }"); + equal = isl_union_map_is_equal(sched1, sched2); + isl_union_map_free(sched1); + isl_union_map_free(sched2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected schedule", return -1); + + return 0; +} + +/* Check that a set of schedule constraints that only allow for + * a coalescing schedule still produces a schedule even if the user + * request a non-coalescing schedule. Earlier versions of isl + * would not handle this case correctly. + */ +static int test_coalescing_schedule(isl_ctx *ctx) +{ + const char *domain, *dep; + isl_union_set *I; + isl_union_map *D; + isl_schedule_constraints *sc; + isl_schedule *schedule; + int treat_coalescing; + + domain = "{ S[a, b] : 0 <= a <= 1 and 0 <= b <= 1 }"; + dep = "{ S[a, b] -> S[a + b, 1 - b] }"; + I = isl_union_set_read_from_str(ctx, domain); + D = isl_union_map_read_from_str(ctx, dep); + sc = isl_schedule_constraints_on_domain(I); + sc = isl_schedule_constraints_set_validity(sc, D); + treat_coalescing = isl_options_get_schedule_treat_coalescing(ctx); + isl_options_set_schedule_treat_coalescing(ctx, 1); + schedule = isl_schedule_constraints_compute_schedule(sc); + isl_options_set_schedule_treat_coalescing(ctx, treat_coalescing); + isl_schedule_free(schedule); + if (!schedule) + return -1; + return 0; +} + +int test_schedule(isl_ctx *ctx) +{ + const char *D, *W, *R, *V, *P, *S; + int max_coincidence; + int treat_coalescing; + + /* Handle resulting schedule with zero bands. */ + if (test_one_schedule(ctx, "{[]}", "{}", "{}", "{[] -> []}", 0, 0) < 0) + return -1; + + /* Jacobi */ + D = "[T,N] -> { S1[t,i] : 1 <= t <= T and 2 <= i <= N - 1 }"; + W = "{ S1[t,i] -> a[t,i] }"; + R = "{ S1[t,i] -> a[t-1,i]; S1[t,i] -> a[t-1,i-1]; " + "S1[t,i] -> a[t-1,i+1] }"; + S = "{ S1[t,i] -> [t,i] }"; + if (test_one_schedule(ctx, D, W, R, S, 2, 0) < 0) + return -1; + + /* Fig. 5 of CC2008 */ + D = "[N] -> { S_0[i, j] : i >= 0 and i <= -1 + N and j >= 2 and " + "j <= -1 + N }"; + W = "[N] -> { S_0[i, j] -> a[i, j] : i >= 0 and i <= -1 + N and " + "j >= 2 and j <= -1 + N }"; + R = "[N] -> { S_0[i, j] -> a[j, i] : i >= 0 and i <= -1 + N and " + "j >= 2 and j <= -1 + N; " + "S_0[i, j] -> a[i, -1 + j] : i >= 0 and i <= -1 + N and " + "j >= 2 and j <= -1 + N }"; + S = "[N] -> { S_0[i, j] -> [0, i, 0, j, 0] }"; + if (test_one_schedule(ctx, D, W, R, S, 2, 0) < 0) + return -1; + + D = "{ S1[i] : 0 <= i <= 10; S2[i] : 0 <= i <= 9 }"; + W = "{ S1[i] -> a[i] }"; + R = "{ S2[i] -> a[i+1] }"; + S = "{ S1[i] -> [0,i]; S2[i] -> [1,i] }"; + if (test_one_schedule(ctx, D, W, R, S, 1, 1) < 0) + return -1; + + D = "{ S1[i] : 0 <= i < 10; S2[i] : 0 <= i < 10 }"; + W = "{ S1[i] -> a[i] }"; + R = "{ S2[i] -> a[9-i] }"; + S = "{ S1[i] -> [0,i]; S2[i] -> [1,i] }"; + if (test_one_schedule(ctx, D, W, R, S, 1, 1) < 0) + return -1; + + D = "[N] -> { S1[i] : 0 <= i < N; S2[i] : 0 <= i < N }"; + W = "{ S1[i] -> a[i] }"; + R = "[N] -> { S2[i] -> a[N-1-i] }"; + S = "{ S1[i] -> [0,i]; S2[i] -> [1,i] }"; + if (test_one_schedule(ctx, D, W, R, S, 1, 1) < 0) + return -1; + + D = "{ S1[i] : 0 < i < 10; S2[i] : 0 <= i < 10 }"; + W = "{ S1[i] -> a[i]; S2[i] -> b[i] }"; + R = "{ S2[i] -> a[i]; S1[i] -> b[i-1] }"; + S = "{ S1[i] -> [i,0]; S2[i] -> [i,1] }"; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + + D = "[N] -> { S1[i] : 1 <= i <= N; S2[i,j] : 1 <= i,j <= N }"; + W = "{ S1[i] -> a[0,i]; S2[i,j] -> a[i,j] }"; + R = "{ S2[i,j] -> a[i-1,j] }"; + S = "{ S1[i] -> [0,i,0]; S2[i,j] -> [1,i,j] }"; + if (test_one_schedule(ctx, D, W, R, S, 2, 1) < 0) + return -1; + + D = "[N] -> { S1[i] : 1 <= i <= N; S2[i,j] : 1 <= i,j <= N }"; + W = "{ S1[i] -> a[i,0]; S2[i,j] -> a[i,j] }"; + R = "{ S2[i,j] -> a[i,j-1] }"; + S = "{ S1[i] -> [0,i,0]; S2[i,j] -> [1,i,j] }"; + if (test_one_schedule(ctx, D, W, R, S, 2, 1) < 0) + return -1; + + D = "[N] -> { S_0[]; S_1[i] : i >= 0 and i <= -1 + N; S_2[] }"; + W = "[N] -> { S_0[] -> a[0]; S_2[] -> b[0]; " + "S_1[i] -> a[1 + i] : i >= 0 and i <= -1 + N }"; + R = "[N] -> { S_2[] -> a[N]; S_1[i] -> a[i] : i >= 0 and i <= -1 + N }"; + S = "[N] -> { S_1[i] -> [1, i, 0]; S_2[] -> [2, 0, 1]; " + "S_0[] -> [0, 0, 0] }"; + if (test_one_schedule(ctx, D, W, R, S, 1, 0) < 0) + return -1; + ctx->opt->schedule_parametric = 0; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + ctx->opt->schedule_parametric = 1; + + D = "[N] -> { S1[i] : 1 <= i <= N; S2[i] : 1 <= i <= N; " + "S3[i,j] : 1 <= i,j <= N; S4[i] : 1 <= i <= N }"; + W = "{ S1[i] -> a[i,0]; S2[i] -> a[0,i]; S3[i,j] -> a[i,j] }"; + R = "[N] -> { S3[i,j] -> a[i-1,j]; S3[i,j] -> a[i,j-1]; " + "S4[i] -> a[i,N] }"; + S = "{ S1[i] -> [0,i,0]; S2[i] -> [1,i,0]; S3[i,j] -> [2,i,j]; " + "S4[i] -> [4,i,0] }"; + max_coincidence = isl_options_get_schedule_maximize_coincidence(ctx); + isl_options_set_schedule_maximize_coincidence(ctx, 0); + if (test_one_schedule(ctx, D, W, R, S, 2, 0) < 0) + return -1; + isl_options_set_schedule_maximize_coincidence(ctx, max_coincidence); + + D = "[N] -> { S_0[i, j] : i >= 1 and i <= N and j >= 1 and j <= N }"; + W = "[N] -> { S_0[i, j] -> s[0] : i >= 1 and i <= N and j >= 1 and " + "j <= N }"; + R = "[N] -> { S_0[i, j] -> s[0] : i >= 1 and i <= N and j >= 1 and " + "j <= N; " + "S_0[i, j] -> a[i, j] : i >= 1 and i <= N and j >= 1 and " + "j <= N }"; + S = "[N] -> { S_0[i, j] -> [0, i, 0, j, 0] }"; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + + D = "[N] -> { S_0[t] : t >= 0 and t <= -1 + N; " + " S_2[t] : t >= 0 and t <= -1 + N; " + " S_1[t, i] : t >= 0 and t <= -1 + N and i >= 0 and " + "i <= -1 + N }"; + W = "[N] -> { S_0[t] -> a[t, 0] : t >= 0 and t <= -1 + N; " + " S_2[t] -> b[t] : t >= 0 and t <= -1 + N; " + " S_1[t, i] -> a[t, 1 + i] : t >= 0 and t <= -1 + N and " + "i >= 0 and i <= -1 + N }"; + R = "[N] -> { S_1[t, i] -> a[t, i] : t >= 0 and t <= -1 + N and " + "i >= 0 and i <= -1 + N; " + " S_2[t] -> a[t, N] : t >= 0 and t <= -1 + N }"; + S = "[N] -> { S_2[t] -> [0, t, 2]; S_1[t, i] -> [0, t, 1, i, 0]; " + " S_0[t] -> [0, t, 0] }"; + + if (test_one_schedule(ctx, D, W, R, S, 2, 1) < 0) + return -1; + ctx->opt->schedule_parametric = 0; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + ctx->opt->schedule_parametric = 1; + + D = "[N] -> { S1[i,j] : 0 <= i,j < N; S2[i,j] : 0 <= i,j < N }"; + S = "{ S1[i,j] -> [0,i,j]; S2[i,j] -> [1,i,j] }"; + if (test_one_schedule(ctx, D, "{}", "{}", S, 2, 2) < 0) + return -1; + + D = "[M, N] -> { S_1[i] : i >= 0 and i <= -1 + M; " + "S_0[i, j] : i >= 0 and i <= -1 + M and j >= 0 and j <= -1 + N }"; + W = "[M, N] -> { S_0[i, j] -> a[j] : i >= 0 and i <= -1 + M and " + "j >= 0 and j <= -1 + N; " + "S_1[i] -> b[0] : i >= 0 and i <= -1 + M }"; + R = "[M, N] -> { S_0[i, j] -> a[0] : i >= 0 and i <= -1 + M and " + "j >= 0 and j <= -1 + N; " + "S_1[i] -> b[0] : i >= 0 and i <= -1 + M }"; + S = "[M, N] -> { S_1[i] -> [1, i, 0]; S_0[i, j] -> [0, i, 0, j, 0] }"; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + + D = "{ S_0[i] : i >= 0 }"; + W = "{ S_0[i] -> a[i] : i >= 0 }"; + R = "{ S_0[i] -> a[0] : i >= 0 }"; + S = "{ S_0[i] -> [0, i, 0] }"; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + + D = "{ S_0[i] : i >= 0; S_1[i] : i >= 0 }"; + W = "{ S_0[i] -> a[i] : i >= 0; S_1[i] -> b[i] : i >= 0 }"; + R = "{ S_0[i] -> b[0] : i >= 0; S_1[i] -> a[i] : i >= 0 }"; + S = "{ S_1[i] -> [0, i, 1]; S_0[i] -> [0, i, 0] }"; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + + D = "[n] -> { S_0[j, k] : j <= -1 + n and j >= 0 and " + "k <= -1 + n and k >= 0 }"; + W = "[n] -> { S_0[j, k] -> B[j] : j <= -1 + n and j >= 0 and " "k <= -1 + n and k >= 0 }"; + R = "[n] -> { S_0[j, k] -> B[j] : j <= -1 + n and j >= 0 and " + "k <= -1 + n and k >= 0; " + "S_0[j, k] -> B[k] : j <= -1 + n and j >= 0 and " + "k <= -1 + n and k >= 0; " + "S_0[j, k] -> A[k] : j <= -1 + n and j >= 0 and " + "k <= -1 + n and k >= 0 }"; + S = "[n] -> { S_0[j, k] -> [2, j, k] }"; + ctx->opt->schedule_outer_coincidence = 1; + if (test_one_schedule(ctx, D, W, R, S, 0, 0) < 0) + return -1; + ctx->opt->schedule_outer_coincidence = 0; + + D = "{Stmt_for_body24[i0, i1, i2, i3]:" + "i0 >= 0 and i0 <= 1 and i1 >= 0 and i1 <= 6 and i2 >= 2 and " + "i2 <= 6 - i1 and i3 >= 0 and i3 <= -1 + i2;" + "Stmt_for_body24[i0, i1, 1, 0]:" + "i0 >= 0 and i0 <= 1 and i1 >= 0 and i1 <= 5;" + "Stmt_for_body7[i0, i1, i2]:" + "i0 >= 0 and i0 <= 1 and i1 >= 0 and i1 <= 7 and i2 >= 0 and " + "i2 <= 7 }"; + + V = "{Stmt_for_body24[0, i1, i2, i3] -> " + "Stmt_for_body24[1, i1, i2, i3]:" + "i3 >= 0 and i3 <= -1 + i2 and i1 >= 0 and i2 <= 6 - i1 and " + "i2 >= 1;" + "Stmt_for_body24[0, i1, i2, i3] -> " + "Stmt_for_body7[1, 1 + i1 + i3, 1 + i1 + i2]:" + "i3 <= -1 + i2 and i2 <= 6 - i1 and i2 >= 1 and i1 >= 0 and " + "i3 >= 0;" + "Stmt_for_body24[0, i1, i2, i3] ->" + "Stmt_for_body7[1, i1, 1 + i1 + i3]:" + "i3 >= 0 and i2 <= 6 - i1 and i1 >= 0 and i3 <= -1 + i2;" + "Stmt_for_body7[0, i1, i2] -> Stmt_for_body7[1, i1, i2]:" + "(i2 >= 1 + i1 and i2 <= 6 and i1 >= 0 and i1 <= 4) or " + "(i2 >= 3 and i2 <= 7 and i1 >= 1 and i2 >= 1 + i1) or " + "(i2 >= 0 and i2 <= i1 and i2 >= -7 + i1 and i1 <= 7);" + "Stmt_for_body7[0, i1, 1 + i1] -> Stmt_for_body7[1, i1, 1 + i1]:" + "i1 <= 6 and i1 >= 0;" + "Stmt_for_body7[0, 0, 7] -> Stmt_for_body7[1, 0, 7];" + "Stmt_for_body7[i0, i1, i2] -> " + "Stmt_for_body24[i0, o1, -1 + i2 - o1, -1 + i1 - o1]:" + "i0 >= 0 and i0 <= 1 and o1 >= 0 and i2 >= 1 + i1 and " + "o1 <= -2 + i2 and i2 <= 7 and o1 <= -1 + i1;" + "Stmt_for_body7[i0, i1, i2] -> " + "Stmt_for_body24[i0, i1, o2, -1 - i1 + i2]:" + "i0 >= 0 and i0 <= 1 and i1 >= 0 and o2 >= -i1 + i2 and " + "o2 >= 1 and o2 <= 6 - i1 and i2 >= 1 + i1 }"; + P = V; + S = "{ Stmt_for_body24[i0, i1, i2, i3] -> " + "[i0, 5i0 + i1, 6i0 + i1 + i2, 1 + 6i0 + i1 + i2 + i3, 1];" + "Stmt_for_body7[i0, i1, i2] -> [0, 5i0, 6i0 + i1, 6i0 + i2, 0] }"; + + treat_coalescing = isl_options_get_schedule_treat_coalescing(ctx); + isl_options_set_schedule_treat_coalescing(ctx, 0); + if (test_special_schedule(ctx, D, V, P, S) < 0) + return -1; + isl_options_set_schedule_treat_coalescing(ctx, treat_coalescing); + + D = "{ S_0[i, j] : i >= 1 and i <= 10 and j >= 1 and j <= 8 }"; + V = "{ S_0[i, j] -> S_0[i, 1 + j] : i >= 1 and i <= 10 and " + "j >= 1 and j <= 7;" + "S_0[i, j] -> S_0[1 + i, j] : i >= 1 and i <= 9 and " + "j >= 1 and j <= 8 }"; + P = "{ }"; + S = "{ S_0[i, j] -> [i + j, j] }"; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_FEAUTRIER; + if (test_special_schedule(ctx, D, V, P, S) < 0) + return -1; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_ISL; + + /* Fig. 1 from Feautrier's "Some Efficient Solutions..." pt. 2, 1992 */ + D = "[N] -> { S_0[i, j] : i >= 0 and i <= -1 + N and " + "j >= 0 and j <= -1 + i }"; + V = "[N] -> { S_0[i, j] -> S_0[i, 1 + j] : j <= -2 + i and " + "i <= -1 + N and j >= 0;" + "S_0[i, -1 + i] -> S_0[1 + i, 0] : i >= 1 and " + "i <= -2 + N }"; + P = "{ }"; + S = "{ S_0[i, j] -> [i, j] }"; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_FEAUTRIER; + if (test_special_schedule(ctx, D, V, P, S) < 0) + return -1; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_ISL; + + /* Test both algorithms on a case with only proximity dependences. */ + D = "{ S[i,j] : 0 <= i <= 10 }"; + V = "{ }"; + P = "{ S[i,j] -> S[i+1,j] : 0 <= i,j <= 10 }"; + S = "{ S[i, j] -> [j, i] }"; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_FEAUTRIER; + if (test_special_schedule(ctx, D, V, P, S) < 0) + return -1; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_ISL; + if (test_special_schedule(ctx, D, V, P, S) < 0) + return -1; + + D = "{ A[a]; B[] }"; + V = "{}"; + P = "{ A[a] -> B[] }"; + if (test_has_schedule(ctx, D, V, P) < 0) + return -1; + + if (test_padded_schedule(ctx) < 0) + return -1; + + /* Check that check for progress is not confused by rational + * solution. + */ + D = "[N] -> { S0[i, j] : i >= 0 and i <= N and j >= 0 and j <= N }"; + V = "[N] -> { S0[i0, -1 + N] -> S0[2 + i0, 0] : i0 >= 0 and " + "i0 <= -2 + N; " + "S0[i0, i1] -> S0[i0, 1 + i1] : i0 >= 0 and " + "i0 <= N and i1 >= 0 and i1 <= -1 + N }"; + P = "{}"; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_FEAUTRIER; + if (test_has_schedule(ctx, D, V, P) < 0) + return -1; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_ISL; + + /* Check that we allow schedule rows that are only non-trivial + * on some full-dimensional domains. + */ + D = "{ S1[j] : 0 <= j <= 1; S0[]; S2[k] : 0 <= k <= 1 }"; + V = "{ S0[] -> S1[j] : 0 <= j <= 1; S2[0] -> S0[];" + "S1[j] -> S2[1] : 0 <= j <= 1 }"; + P = "{}"; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_FEAUTRIER; + if (test_has_schedule(ctx, D, V, P) < 0) + return -1; + ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_ISL; + + if (test_conditional_schedule_constraints(ctx) < 0) + return -1; + + if (test_strongly_satisfying_schedule(ctx) < 0) + return -1; + + if (test_conflicting_context_schedule(ctx) < 0) + return -1; + + if (test_bounded_coefficients_schedule(ctx) < 0) + return -1; + if (test_coalescing_schedule(ctx) < 0) + return -1; + + return 0; +} + +/* Perform scheduling tests using the whole component scheduler. + */ +static int test_schedule_whole(isl_ctx *ctx) +{ + int whole; + int r; + + whole = isl_options_get_schedule_whole_component(ctx); + isl_options_set_schedule_whole_component(ctx, 1); + r = test_schedule(ctx); + isl_options_set_schedule_whole_component(ctx, whole); + + return r; +} + +/* Perform scheduling tests using the incremental scheduler. + */ +static int test_schedule_incremental(isl_ctx *ctx) +{ + int whole; + int r; + + whole = isl_options_get_schedule_whole_component(ctx); + isl_options_set_schedule_whole_component(ctx, 0); + r = test_schedule(ctx); + isl_options_set_schedule_whole_component(ctx, whole); + + return r; +} + +int test_plain_injective(isl_ctx *ctx, const char *str, int injective) +{ + isl_union_map *umap; + int test; + + umap = isl_union_map_read_from_str(ctx, str); + test = isl_union_map_plain_is_injective(umap); + isl_union_map_free(umap); + if (test < 0) + return -1; + if (test == injective) + return 0; + if (injective) + isl_die(ctx, isl_error_unknown, + "map not detected as injective", return -1); + else + isl_die(ctx, isl_error_unknown, + "map detected as injective", return -1); +} + +int test_injective(isl_ctx *ctx) +{ + const char *str; + + if (test_plain_injective(ctx, "{S[i,j] -> A[0]; T[i,j] -> B[1]}", 0)) + return -1; + if (test_plain_injective(ctx, "{S[] -> A[0]; T[] -> B[0]}", 1)) + return -1; + if (test_plain_injective(ctx, "{S[] -> A[0]; T[] -> A[1]}", 1)) + return -1; + if (test_plain_injective(ctx, "{S[] -> A[0]; T[] -> A[0]}", 0)) + return -1; + if (test_plain_injective(ctx, "{S[i] -> A[i,0]; T[i] -> A[i,1]}", 1)) + return -1; + if (test_plain_injective(ctx, "{S[i] -> A[i]; T[i] -> A[i]}", 0)) + return -1; + if (test_plain_injective(ctx, "{S[] -> A[0,0]; T[] -> A[0,1]}", 1)) + return -1; + if (test_plain_injective(ctx, "{S[] -> A[0,0]; T[] -> A[1,0]}", 1)) + return -1; + + str = "{S[] -> A[0,0]; T[] -> A[0,1]; U[] -> A[1,0]}"; + if (test_plain_injective(ctx, str, 1)) + return -1; + str = "{S[] -> A[0,0]; T[] -> A[0,1]; U[] -> A[0,0]}"; + if (test_plain_injective(ctx, str, 0)) + return -1; + + return 0; +} + +static int aff_plain_is_equal(__isl_keep isl_aff *aff, const char *str) +{ + isl_aff *aff2; + int equal; + + if (!aff) + return -1; + + aff2 = isl_aff_read_from_str(isl_aff_get_ctx(aff), str); + equal = isl_aff_plain_is_equal(aff, aff2); + isl_aff_free(aff2); + + return equal; +} + +static int aff_check_plain_equal(__isl_keep isl_aff *aff, const char *str) +{ + int equal; + + equal = aff_plain_is_equal(aff, str); + if (equal < 0) + return -1; + if (!equal) + isl_die(isl_aff_get_ctx(aff), isl_error_unknown, + "result not as expected", return -1); + return 0; +} + +struct { + __isl_give isl_aff *(*fn)(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); +} aff_bin_op[] = { + ['+'] = { &isl_aff_add }, + ['-'] = { &isl_aff_sub }, + ['*'] = { &isl_aff_mul }, + ['/'] = { &isl_aff_div }, +}; + +struct { + const char *arg1; + unsigned char op; + const char *arg2; + const char *res; +} aff_bin_tests[] = { + { "{ [i] -> [i] }", '+', "{ [i] -> [i] }", + "{ [i] -> [2i] }" }, + { "{ [i] -> [i] }", '-', "{ [i] -> [i] }", + "{ [i] -> [0] }" }, + { "{ [i] -> [i] }", '*', "{ [i] -> [2] }", + "{ [i] -> [2i] }" }, + { "{ [i] -> [2] }", '*', "{ [i] -> [i] }", + "{ [i] -> [2i] }" }, + { "{ [i] -> [i] }", '/', "{ [i] -> [2] }", + "{ [i] -> [i/2] }" }, + { "{ [i] -> [2i] }", '/', "{ [i] -> [2] }", + "{ [i] -> [i] }" }, + { "{ [i] -> [i] }", '+', "{ [i] -> [NaN] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [i] }", '-', "{ [i] -> [NaN] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [i] }", '*', "{ [i] -> [NaN] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [2] }", '*', "{ [i] -> [NaN] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [i] }", '/', "{ [i] -> [NaN] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [2] }", '/', "{ [i] -> [NaN] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [NaN] }", '+', "{ [i] -> [i] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [NaN] }", '-', "{ [i] -> [i] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [NaN] }", '*', "{ [i] -> [2] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [NaN] }", '*', "{ [i] -> [i] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [NaN] }", '/', "{ [i] -> [2] }", + "{ [i] -> [NaN] }" }, + { "{ [i] -> [NaN] }", '/', "{ [i] -> [i] }", + "{ [i] -> [NaN] }" }, +}; + +/* Perform some basic tests of binary operations on isl_aff objects. + */ +static int test_bin_aff(isl_ctx *ctx) +{ + int i; + isl_aff *aff1, *aff2, *res; + __isl_give isl_aff *(*fn)(__isl_take isl_aff *aff1, + __isl_take isl_aff *aff2); + int ok; + + for (i = 0; i < ARRAY_SIZE(aff_bin_tests); ++i) { + aff1 = isl_aff_read_from_str(ctx, aff_bin_tests[i].arg1); + aff2 = isl_aff_read_from_str(ctx, aff_bin_tests[i].arg2); + res = isl_aff_read_from_str(ctx, aff_bin_tests[i].res); + fn = aff_bin_op[aff_bin_tests[i].op].fn; + aff1 = fn(aff1, aff2); + if (isl_aff_is_nan(res)) + ok = isl_aff_is_nan(aff1); + else + ok = isl_aff_plain_is_equal(aff1, res); + isl_aff_free(aff1); + isl_aff_free(res); + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + } + + return 0; +} + +struct { + __isl_give isl_union_pw_multi_aff *(*fn)( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + const char *arg1; + const char *arg2; + const char *res; +} upma_bin_tests[] = { + { &isl_union_pw_multi_aff_add, "{ A[] -> [0]; B[0] -> [1] }", + "{ B[x] -> [2] : x >= 0 }", "{ B[0] -> [3] }" }, + { &isl_union_pw_multi_aff_union_add, "{ A[] -> [0]; B[0] -> [1] }", + "{ B[x] -> [2] : x >= 0 }", + "{ A[] -> [0]; B[0] -> [3]; B[x] -> [2] : x >= 1 }" }, + { &isl_union_pw_multi_aff_pullback_union_pw_multi_aff, + "{ A[] -> B[0]; C[x] -> B[1] : x < 10; C[y] -> B[2] : y >= 10 }", + "{ D[i] -> A[] : i < 0; D[i] -> C[i + 5] : i >= 0 }", + "{ D[i] -> B[0] : i < 0; D[i] -> B[1] : 0 <= i < 5; " + "D[i] -> B[2] : i >= 5 }" }, + { &isl_union_pw_multi_aff_union_add, "{ B[x] -> A[1] : x <= 0 }", + "{ B[x] -> C[2] : x > 0 }", + "{ B[x] -> A[1] : x <= 0; B[x] -> C[2] : x > 0 }" }, + { &isl_union_pw_multi_aff_union_add, "{ B[x] -> A[1] : x <= 0 }", + "{ B[x] -> A[2] : x >= 0 }", + "{ B[x] -> A[1] : x < 0; B[x] -> A[2] : x > 0; B[0] -> A[3] }" }, +}; + +/* Perform some basic tests of binary operations on + * isl_union_pw_multi_aff objects. + */ +static int test_bin_upma(isl_ctx *ctx) +{ + int i; + isl_union_pw_multi_aff *upma1, *upma2, *res; + int ok; + + for (i = 0; i < ARRAY_SIZE(upma_bin_tests); ++i) { + upma1 = isl_union_pw_multi_aff_read_from_str(ctx, + upma_bin_tests[i].arg1); + upma2 = isl_union_pw_multi_aff_read_from_str(ctx, + upma_bin_tests[i].arg2); + res = isl_union_pw_multi_aff_read_from_str(ctx, + upma_bin_tests[i].res); + upma1 = upma_bin_tests[i].fn(upma1, upma2); + ok = isl_union_pw_multi_aff_plain_is_equal(upma1, res); + isl_union_pw_multi_aff_free(upma1); + isl_union_pw_multi_aff_free(res); + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + } + + return 0; +} + +struct { + __isl_give isl_union_pw_multi_aff *(*fn)( + __isl_take isl_union_pw_multi_aff *upma1, + __isl_take isl_union_pw_multi_aff *upma2); + const char *arg1; + const char *arg2; +} upma_bin_fail_tests[] = { + { &isl_union_pw_multi_aff_union_add, "{ B[x] -> A[1] : x <= 0 }", + "{ B[x] -> C[2] : x >= 0 }" }, +}; + +/* Perform some basic tests of binary operations on + * isl_union_pw_multi_aff objects that are expected to fail. + */ +static int test_bin_upma_fail(isl_ctx *ctx) +{ + int i, n; + isl_union_pw_multi_aff *upma1, *upma2; + int on_error; + + on_error = isl_options_get_on_error(ctx); + isl_options_set_on_error(ctx, ISL_ON_ERROR_CONTINUE); + n = ARRAY_SIZE(upma_bin_fail_tests); + for (i = 0; i < n; ++i) { + upma1 = isl_union_pw_multi_aff_read_from_str(ctx, + upma_bin_fail_tests[i].arg1); + upma2 = isl_union_pw_multi_aff_read_from_str(ctx, + upma_bin_fail_tests[i].arg2); + upma1 = upma_bin_fail_tests[i].fn(upma1, upma2); + isl_union_pw_multi_aff_free(upma1); + if (upma1) + break; + } + isl_options_set_on_error(ctx, on_error); + if (i < n) + isl_die(ctx, isl_error_unknown, + "operation not expected to succeed", return -1); + + return 0; +} + +int test_aff(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_space *space; + isl_local_space *ls; + isl_aff *aff; + int zero, equal; + + if (test_bin_aff(ctx) < 0) + return -1; + if (test_bin_upma(ctx) < 0) + return -1; + if (test_bin_upma_fail(ctx) < 0) + return -1; + + space = isl_space_set_alloc(ctx, 0, 1); + ls = isl_local_space_from_space(space); + aff = isl_aff_zero_on_domain(ls); + + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1); + aff = isl_aff_scale_down_ui(aff, 3); + aff = isl_aff_floor(aff); + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1); + aff = isl_aff_scale_down_ui(aff, 2); + aff = isl_aff_floor(aff); + aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1); + + str = "{ [10] }"; + set = isl_set_read_from_str(ctx, str); + aff = isl_aff_gist(aff, set); + + aff = isl_aff_add_constant_si(aff, -16); + zero = isl_aff_plain_is_zero(aff); + isl_aff_free(aff); + + if (zero < 0) + return -1; + if (!zero) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + aff = isl_aff_read_from_str(ctx, "{ [-1] }"); + aff = isl_aff_scale_down_ui(aff, 64); + aff = isl_aff_floor(aff); + equal = aff_check_plain_equal(aff, "{ [-1] }"); + isl_aff_free(aff); + if (equal < 0) + return -1; + + return 0; +} + +int test_dim_max(isl_ctx *ctx) +{ + int equal; + const char *str; + isl_set *set1, *set2; + isl_set *set; + isl_map *map; + isl_pw_aff *pwaff; + + str = "[N] -> { [i] : 0 <= i <= min(N,10) }"; + set = isl_set_read_from_str(ctx, str); + pwaff = isl_set_dim_max(set, 0); + set1 = isl_set_from_pw_aff(pwaff); + str = "[N] -> { [10] : N >= 10; [N] : N <= 9 and N >= 0 }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + str = "[N] -> { [i] : 0 <= i <= max(2N,N+6) }"; + set = isl_set_read_from_str(ctx, str); + pwaff = isl_set_dim_max(set, 0); + set1 = isl_set_from_pw_aff(pwaff); + str = "[N] -> { [6 + N] : -6 <= N <= 5; [2N] : N >= 6 }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + str = "[N] -> { [i] : 0 <= i <= 2N or 0 <= i <= N+6 }"; + set = isl_set_read_from_str(ctx, str); + pwaff = isl_set_dim_max(set, 0); + set1 = isl_set_from_pw_aff(pwaff); + str = "[N] -> { [6 + N] : -6 <= N <= 5; [2N] : N >= 6 }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + str = "[N,M] -> { [i,j] -> [([i/16]), i%16, ([j/16]), j%16] : " + "0 <= i < N and 0 <= j < M }"; + map = isl_map_read_from_str(ctx, str); + set = isl_map_range(map); + + pwaff = isl_set_dim_max(isl_set_copy(set), 0); + set1 = isl_set_from_pw_aff(pwaff); + str = "[N,M] -> { [([(N-1)/16])] : M,N > 0 }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + + pwaff = isl_set_dim_max(isl_set_copy(set), 3); + set1 = isl_set_from_pw_aff(pwaff); + str = "[N,M] -> { [t] : t = min(M-1,15) and M,N > 0 }"; + set2 = isl_set_read_from_str(ctx, str); + if (equal >= 0 && equal) + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + + isl_set_free(set); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + /* Check that solutions are properly merged. */ + str = "[n] -> { [a, b, c] : c >= -4a - 2b and " + "c <= -1 + n - 4a - 2b and c >= -2b and " + "4a >= -4 + n and c >= 0 }"; + set = isl_set_read_from_str(ctx, str); + pwaff = isl_set_dim_min(set, 2); + set1 = isl_set_from_pw_aff(pwaff); + str = "[n] -> { [(0)] : n >= 1 }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + /* Check that empty solution lie in the right space. */ + str = "[n] -> { [t,a] : 1 = 0 }"; + set = isl_set_read_from_str(ctx, str); + pwaff = isl_set_dim_max(set, 0); + set1 = isl_set_from_pw_aff(pwaff); + str = "[n] -> { [t] : 1 = 0 }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + return 0; +} + +/* Is "pma" obviously equal to the isl_pw_multi_aff represented by "str"? + */ +static int pw_multi_aff_plain_is_equal(__isl_keep isl_pw_multi_aff *pma, + const char *str) +{ + isl_ctx *ctx; + isl_pw_multi_aff *pma2; + int equal; + + if (!pma) + return -1; + + ctx = isl_pw_multi_aff_get_ctx(pma); + pma2 = isl_pw_multi_aff_read_from_str(ctx, str); + equal = isl_pw_multi_aff_plain_is_equal(pma, pma2); + isl_pw_multi_aff_free(pma2); + + return equal; +} + +/* Check that "pma" is obviously equal to the isl_pw_multi_aff + * represented by "str". + */ +static int pw_multi_aff_check_plain_equal(__isl_keep isl_pw_multi_aff *pma, + const char *str) +{ + int equal; + + equal = pw_multi_aff_plain_is_equal(pma, str); + if (equal < 0) + return -1; + if (!equal) + isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_unknown, + "result not as expected", return -1); + return 0; +} + +/* Basic test for isl_pw_multi_aff_product. + * + * Check that multiple pieces are properly handled. + */ +static int test_product_pma(isl_ctx *ctx) +{ + int equal; + const char *str; + isl_pw_multi_aff *pma1, *pma2; + + str = "{ A[i] -> B[1] : i < 0; A[i] -> B[2] : i >= 0 }"; + pma1 = isl_pw_multi_aff_read_from_str(ctx, str); + str = "{ C[] -> D[] }"; + pma2 = isl_pw_multi_aff_read_from_str(ctx, str); + pma1 = isl_pw_multi_aff_product(pma1, pma2); + str = "{ [A[i] -> C[]] -> [B[(1)] -> D[]] : i < 0;" + "[A[i] -> C[]] -> [B[(2)] -> D[]] : i >= 0 }"; + equal = pw_multi_aff_check_plain_equal(pma1, str); + isl_pw_multi_aff_free(pma1); + if (equal < 0) + return -1; + + return 0; +} + +int test_product(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_union_set *uset1, *uset2; + int ok; + + str = "{ A[i] }"; + set = isl_set_read_from_str(ctx, str); + set = isl_set_product(set, isl_set_copy(set)); + ok = isl_set_is_wrapping(set); + isl_set_free(set); + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + str = "{ [] }"; + uset1 = isl_union_set_read_from_str(ctx, str); + uset1 = isl_union_set_product(uset1, isl_union_set_copy(uset1)); + str = "{ [[] -> []] }"; + uset2 = isl_union_set_read_from_str(ctx, str); + ok = isl_union_set_is_equal(uset1, uset2); + isl_union_set_free(uset1); + isl_union_set_free(uset2); + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + if (test_product_pma(ctx) < 0) + return -1; + + return 0; +} + +/* Check that two sets are not considered disjoint just because + * they have a different set of (named) parameters. + */ +static int test_disjoint(isl_ctx *ctx) +{ + const char *str; + isl_set *set, *set2; + int disjoint; + + str = "[n] -> { [[]->[]] }"; + set = isl_set_read_from_str(ctx, str); + str = "{ [[]->[]] }"; + set2 = isl_set_read_from_str(ctx, str); + disjoint = isl_set_is_disjoint(set, set2); + isl_set_free(set); + isl_set_free(set2); + if (disjoint < 0) + return -1; + if (disjoint) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + return 0; +} + +int test_equal(isl_ctx *ctx) +{ + const char *str; + isl_set *set, *set2; + int equal; + + str = "{ S_6[i] }"; + set = isl_set_read_from_str(ctx, str); + str = "{ S_7[i] }"; + set2 = isl_set_read_from_str(ctx, str); + equal = isl_set_is_equal(set, set2); + isl_set_free(set); + isl_set_free(set2); + if (equal < 0) + return -1; + if (equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + return 0; +} + +static int test_plain_fixed(isl_ctx *ctx, __isl_take isl_map *map, + enum isl_dim_type type, unsigned pos, int fixed) +{ + int test; + + test = isl_map_plain_is_fixed(map, type, pos, NULL); + isl_map_free(map); + if (test < 0) + return -1; + if (test == fixed) + return 0; + if (fixed) + isl_die(ctx, isl_error_unknown, + "map not detected as fixed", return -1); + else + isl_die(ctx, isl_error_unknown, + "map detected as fixed", return -1); +} + +int test_fixed(isl_ctx *ctx) +{ + const char *str; + isl_map *map; + + str = "{ [i] -> [i] }"; + map = isl_map_read_from_str(ctx, str); + if (test_plain_fixed(ctx, map, isl_dim_out, 0, 0)) + return -1; + str = "{ [i] -> [1] }"; + map = isl_map_read_from_str(ctx, str); + if (test_plain_fixed(ctx, map, isl_dim_out, 0, 1)) + return -1; + str = "{ S_1[p1] -> [o0] : o0 = -2 and p1 >= 1 and p1 <= 7 }"; + map = isl_map_read_from_str(ctx, str); + if (test_plain_fixed(ctx, map, isl_dim_out, 0, 1)) + return -1; + map = isl_map_read_from_str(ctx, str); + map = isl_map_neg(map); + if (test_plain_fixed(ctx, map, isl_dim_out, 0, 1)) + return -1; + + return 0; +} + +struct isl_vertices_test_data { + const char *set; + int n; + const char *vertex[2]; +} vertices_tests[] = { + { "{ A[t, i] : t = 12 and i >= 4 and i <= 12 }", + 2, { "{ A[12, 4] }", "{ A[12, 12] }" } }, + { "{ A[t, i] : t = 14 and i = 1 }", + 1, { "{ A[14, 1] }" } }, +}; + +/* Check that "vertex" corresponds to one of the vertices in data->vertex. + */ +static isl_stat find_vertex(__isl_take isl_vertex *vertex, void *user) +{ + struct isl_vertices_test_data *data = user; + isl_ctx *ctx; + isl_multi_aff *ma; + isl_basic_set *bset; + isl_pw_multi_aff *pma; + int i; + isl_bool equal; + + ctx = isl_vertex_get_ctx(vertex); + bset = isl_vertex_get_domain(vertex); + ma = isl_vertex_get_expr(vertex); + pma = isl_pw_multi_aff_alloc(isl_set_from_basic_set(bset), ma); + + for (i = 0; i < data->n; ++i) { + isl_pw_multi_aff *pma_i; + + pma_i = isl_pw_multi_aff_read_from_str(ctx, data->vertex[i]); + equal = isl_pw_multi_aff_plain_is_equal(pma, pma_i); + isl_pw_multi_aff_free(pma_i); + + if (equal < 0 || equal) + break; + } + + isl_pw_multi_aff_free(pma); + isl_vertex_free(vertex); + + if (equal < 0) + return isl_stat_error; + + return equal ? isl_stat_ok : isl_stat_error; +} + +int test_vertices(isl_ctx *ctx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vertices_tests); ++i) { + isl_basic_set *bset; + isl_vertices *vertices; + int ok = 1; + int n; + + bset = isl_basic_set_read_from_str(ctx, vertices_tests[i].set); + vertices = isl_basic_set_compute_vertices(bset); + n = isl_vertices_get_n_vertices(vertices); + if (vertices_tests[i].n != n) + ok = 0; + if (isl_vertices_foreach_vertex(vertices, &find_vertex, + &vertices_tests[i]) < 0) + ok = 0; + isl_vertices_free(vertices); + isl_basic_set_free(bset); + + if (!vertices) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, "unexpected vertices", + return -1); + } + + return 0; +} + +int test_union_pw(isl_ctx *ctx) +{ + int equal; + const char *str; + isl_union_set *uset; + isl_union_pw_qpolynomial *upwqp1, *upwqp2; + + str = "{ [x] -> x^2 }"; + upwqp1 = isl_union_pw_qpolynomial_read_from_str(ctx, str); + upwqp2 = isl_union_pw_qpolynomial_copy(upwqp1); + uset = isl_union_pw_qpolynomial_domain(upwqp1); + upwqp1 = isl_union_pw_qpolynomial_copy(upwqp2); + upwqp1 = isl_union_pw_qpolynomial_intersect_domain(upwqp1, uset); + equal = isl_union_pw_qpolynomial_plain_is_equal(upwqp1, upwqp2); + isl_union_pw_qpolynomial_free(upwqp1); + isl_union_pw_qpolynomial_free(upwqp2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + return 0; +} + +/* Test that isl_union_pw_qpolynomial_eval picks up the function + * defined over the correct domain space. + */ +static int test_eval(isl_ctx *ctx) +{ + const char *str; + isl_point *pnt; + isl_set *set; + isl_union_pw_qpolynomial *upwqp; + isl_val *v; + int cmp; + + str = "{ A[x] -> x^2; B[x] -> -x^2 }"; + upwqp = isl_union_pw_qpolynomial_read_from_str(ctx, str); + str = "{ A[6] }"; + set = isl_set_read_from_str(ctx, str); + pnt = isl_set_sample_point(set); + v = isl_union_pw_qpolynomial_eval(upwqp, pnt); + cmp = isl_val_cmp_si(v, 36); + isl_val_free(v); + + if (!v) + return -1; + if (cmp != 0) + isl_die(ctx, isl_error_unknown, "unexpected value", return -1); + + return 0; +} + +/* Descriptions of sets that are tested for reparsing after printing. + */ +const char *output_tests[] = { + "{ [1, y] : 0 <= y <= 1; [x, -x] : 0 <= x <= 1 }", +}; + +/* Check that printing a set and reparsing a set from the printed output + * results in the same set. + */ +static int test_output_set(isl_ctx *ctx) +{ + int i; + char *str; + isl_set *set1, *set2; + isl_bool equal; + + for (i = 0; i < ARRAY_SIZE(output_tests); ++i) { + set1 = isl_set_read_from_str(ctx, output_tests[i]); + str = isl_set_to_str(set1); + set2 = isl_set_read_from_str(ctx, str); + free(str); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "parsed output not the same", return -1); + } + + return 0; +} + +int test_output(isl_ctx *ctx) +{ + char *s; + const char *str; + isl_pw_aff *pa; + isl_printer *p; + int equal; + + if (test_output_set(ctx) < 0) + return -1; + + str = "[x] -> { [1] : x % 4 <= 2; [2] : x = 3 }"; + pa = isl_pw_aff_read_from_str(ctx, str); + + p = isl_printer_to_str(ctx); + p = isl_printer_set_output_format(p, ISL_FORMAT_C); + p = isl_printer_print_pw_aff(p, pa); + s = isl_printer_get_str(p); + isl_printer_free(p); + isl_pw_aff_free(pa); + if (!s) + equal = -1; + else + equal = !strcmp(s, "4 * floord(x, 4) + 2 >= x ? 1 : 2"); + free(s); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "unexpected result", return -1); + + return 0; +} + +int test_sample(isl_ctx *ctx) +{ + const char *str; + isl_basic_set *bset1, *bset2; + int empty, subset; + + str = "{ [a, b, c, d, e, f, g, h, i, j, k] : " + "3i >= 1073741823b - c - 1073741823e + f and c >= 0 and " + "3i >= -1 + 3221225466b + c + d - 3221225466e - f and " + "2e >= a - b and 3e <= 2a and 3k <= -a and f <= -1 + a and " + "3i <= 4 - a + 4b + 2c - e - 2f and 3k <= -a + c - f and " + "3h >= -2 + a and 3g >= -3 - a and 3k >= -2 - a and " + "3i >= -2 - a - 2c + 3e + 2f and 3h <= a + c - f and " + "3h >= a + 2147483646b + 2c - 2147483646e - 2f and " + "3g <= -1 - a and 3i <= 1 + c + d - f and a <= 1073741823 and " + "f >= 1 - a + 1073741822b + c + d - 1073741822e and " + "3i >= 1 + 2b - 2c + e + 2f + 3g and " + "1073741822f <= 1073741822 - a + 1073741821b + 1073741822c +" + "d - 1073741821e and " + "3j <= 3 - a + 3b and 3g <= -2 - 2b + c + d - e - f and " + "3j >= 1 - a + b + 2e and " + "3f >= -3 + a + 3221225462b + 3c + d - 3221225465e and " + "3i <= 4 - a + 4b - e and " + "f <= 1073741822 + 1073741822b - 1073741822e and 3h <= a and " + "f >= 0 and 2e <= 4 - a + 5b - d and 2e <= a - b + d and " + "c <= -1 + a and 3i >= -2 - a + 3e and " + "1073741822e <= 1073741823 - a + 1073741822b + c and " + "3g >= -4 + 3221225464b + 3c + d - 3221225467e - 3f and " + "3i >= -1 + 3221225466b + 3c + d - 3221225466e - 3f and " + "1073741823e >= 1 + 1073741823b - d and " + "3i >= 1073741823b + c - 1073741823e - f and " + "3i >= 1 + 2b + e + 3g }"; + bset1 = isl_basic_set_read_from_str(ctx, str); + bset2 = isl_basic_set_sample(isl_basic_set_copy(bset1)); + empty = isl_basic_set_is_empty(bset2); + subset = isl_basic_set_is_subset(bset2, bset1); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + if (empty < 0 || subset < 0) + return -1; + if (empty) + isl_die(ctx, isl_error_unknown, "point not found", return -1); + if (!subset) + isl_die(ctx, isl_error_unknown, "bad point found", return -1); + + return 0; +} + +int test_fixed_power(isl_ctx *ctx) +{ + const char *str; + isl_map *map; + isl_int exp; + int equal; + + isl_int_init(exp); + str = "{ [i] -> [i + 1] }"; + map = isl_map_read_from_str(ctx, str); + isl_int_set_si(exp, 23); + map = isl_map_fixed_power(map, exp); + equal = map_check_equal(map, "{ [i] -> [i + 23] }"); + isl_int_clear(exp); + isl_map_free(map); + if (equal < 0) + return -1; + + return 0; +} + +int test_slice(isl_ctx *ctx) +{ + const char *str; + isl_map *map; + int equal; + + str = "{ [i] -> [j] }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_equate(map, isl_dim_in, 0, isl_dim_out, 0); + equal = map_check_equal(map, "{ [i] -> [i] }"); + isl_map_free(map); + if (equal < 0) + return -1; + + str = "{ [i] -> [j] }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_equate(map, isl_dim_in, 0, isl_dim_in, 0); + equal = map_check_equal(map, "{ [i] -> [j] }"); + isl_map_free(map); + if (equal < 0) + return -1; + + str = "{ [i] -> [j] }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_oppose(map, isl_dim_in, 0, isl_dim_out, 0); + equal = map_check_equal(map, "{ [i] -> [-i] }"); + isl_map_free(map); + if (equal < 0) + return -1; + + str = "{ [i] -> [j] }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_oppose(map, isl_dim_in, 0, isl_dim_in, 0); + equal = map_check_equal(map, "{ [0] -> [j] }"); + isl_map_free(map); + if (equal < 0) + return -1; + + str = "{ [i] -> [j] }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_order_gt(map, isl_dim_in, 0, isl_dim_out, 0); + equal = map_check_equal(map, "{ [i] -> [j] : i > j }"); + isl_map_free(map); + if (equal < 0) + return -1; + + str = "{ [i] -> [j] }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_order_gt(map, isl_dim_in, 0, isl_dim_in, 0); + equal = map_check_equal(map, "{ [i] -> [j] : false }"); + isl_map_free(map); + if (equal < 0) + return -1; + + return 0; +} + +int test_eliminate(isl_ctx *ctx) +{ + const char *str; + isl_map *map; + int equal; + + str = "{ [i] -> [j] : i = 2j }"; + map = isl_map_read_from_str(ctx, str); + map = isl_map_eliminate(map, isl_dim_out, 0, 1); + equal = map_check_equal(map, "{ [i] -> [j] : exists a : i = 2a }"); + isl_map_free(map); + if (equal < 0) + return -1; + + return 0; +} + +/* Check that isl_set_dim_residue_class detects that the values of j + * in the set below are all odd and that it does not detect any spurious + * strides. + */ +static int test_residue_class(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_int m, r; + int res; + + str = "{ [i,j] : j = 4 i + 1 and 0 <= i <= 100; " + "[i,j] : j = 4 i + 3 and 500 <= i <= 600 }"; + set = isl_set_read_from_str(ctx, str); + isl_int_init(m); + isl_int_init(r); + res = isl_set_dim_residue_class(set, 1, &m, &r); + if (res >= 0 && + (isl_int_cmp_si(m, 2) != 0 || isl_int_cmp_si(r, 1) != 0)) + isl_die(ctx, isl_error_unknown, "incorrect residue class", + res = -1); + isl_int_clear(r); + isl_int_clear(m); + isl_set_free(set); + + return res; +} + +int test_align_parameters(isl_ctx *ctx) +{ + const char *str; + isl_space *space; + isl_multi_aff *ma1, *ma2; + int equal; + + str = "{ A[B[] -> C[]] -> D[E[] -> F[]] }"; + ma1 = isl_multi_aff_read_from_str(ctx, str); + + space = isl_space_params_alloc(ctx, 1); + space = isl_space_set_dim_name(space, isl_dim_param, 0, "N"); + ma1 = isl_multi_aff_align_params(ma1, space); + + str = "[N] -> { A[B[] -> C[]] -> D[E[] -> F[]] }"; + ma2 = isl_multi_aff_read_from_str(ctx, str); + + equal = isl_multi_aff_plain_is_equal(ma1, ma2); + + isl_multi_aff_free(ma1); + isl_multi_aff_free(ma2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "result not as expected", return -1); + + return 0; +} + +static int test_list(isl_ctx *ctx) +{ + isl_id *a, *b, *c, *d, *id; + isl_id_list *list; + int ok; + + a = isl_id_alloc(ctx, "a", NULL); + b = isl_id_alloc(ctx, "b", NULL); + c = isl_id_alloc(ctx, "c", NULL); + d = isl_id_alloc(ctx, "d", NULL); + + list = isl_id_list_alloc(ctx, 4); + list = isl_id_list_add(list, a); + list = isl_id_list_add(list, b); + list = isl_id_list_add(list, c); + list = isl_id_list_add(list, d); + list = isl_id_list_drop(list, 1, 1); + + if (isl_id_list_n_id(list) != 3) { + isl_id_list_free(list); + isl_die(ctx, isl_error_unknown, + "unexpected number of elements in list", return -1); + } + + id = isl_id_list_get_id(list, 0); + ok = id == a; + isl_id_free(id); + id = isl_id_list_get_id(list, 1); + ok = ok && id == c; + isl_id_free(id); + id = isl_id_list_get_id(list, 2); + ok = ok && id == d; + isl_id_free(id); + + isl_id_list_free(list); + + if (!ok) + isl_die(ctx, isl_error_unknown, + "unexpected elements in list", return -1); + + return 0; +} + +const char *set_conversion_tests[] = { + "[N] -> { [i] : N - 1 <= 2 i <= N }", + "[N] -> { [i] : exists a : i = 4 a and N - 1 <= i <= N }", + "[N] -> { [i,j] : exists a : i = 4 a and N - 1 <= i, 2j <= N }", + "[N] -> { [[i]->[j]] : exists a : i = 4 a and N - 1 <= i, 2j <= N }", + "[N] -> { [3*floor(N/2) + 5*floor(N/3)] }", + "[a, b] -> { [c, d] : (4*floor((-a + c)/4) = -a + c and " + "32*floor((-b + d)/32) = -b + d and 5 <= c <= 8 and " + "-3 + c <= d <= 28 + c) }", +}; + +/* Check that converting from isl_set to isl_pw_multi_aff and back + * to isl_set produces the original isl_set. + */ +static int test_set_conversion(isl_ctx *ctx) +{ + int i; + const char *str; + isl_set *set1, *set2; + isl_pw_multi_aff *pma; + int equal; + + for (i = 0; i < ARRAY_SIZE(set_conversion_tests); ++i) { + str = set_conversion_tests[i]; + set1 = isl_set_read_from_str(ctx, str); + pma = isl_pw_multi_aff_from_set(isl_set_copy(set1)); + set2 = isl_set_from_pw_multi_aff(pma); + equal = isl_set_is_equal(set1, set2); + isl_set_free(set1); + isl_set_free(set2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "bad conversion", + return -1); + } + + return 0; +} + +const char *conversion_tests[] = { + "{ [a, b, c, d] -> s0[a, b, e, f] : " + "exists (e0 = [(a - 2c)/3], e1 = [(-4 + b - 5d)/9], " + "e2 = [(-d + f)/9]: 3e0 = a - 2c and 9e1 = -4 + b - 5d and " + "9e2 = -d + f and f >= 0 and f <= 8 and 9e >= -5 - 2a and " + "9e <= -2 - 2a) }", + "{ [a, b] -> [c] : exists (e0 = floor((-a - b + c)/5): " + "5e0 = -a - b + c and c >= -a and c <= 4 - a) }", + "{ [a, b] -> [c] : exists d : 18 * d = -3 - a + 2c and 1 <= c <= 3 }", +}; + +/* Check that converting from isl_map to isl_pw_multi_aff and back + * to isl_map produces the original isl_map. + */ +static int test_map_conversion(isl_ctx *ctx) +{ + int i; + isl_map *map1, *map2; + isl_pw_multi_aff *pma; + int equal; + + for (i = 0; i < ARRAY_SIZE(conversion_tests); ++i) { + map1 = isl_map_read_from_str(ctx, conversion_tests[i]); + pma = isl_pw_multi_aff_from_map(isl_map_copy(map1)); + map2 = isl_map_from_pw_multi_aff(pma); + equal = isl_map_is_equal(map1, map2); + isl_map_free(map1); + isl_map_free(map2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "bad conversion", + return -1); + } + + return 0; +} + +static int test_conversion(isl_ctx *ctx) +{ + if (test_set_conversion(ctx) < 0) + return -1; + if (test_map_conversion(ctx) < 0) + return -1; + return 0; +} + +/* Check that isl_basic_map_curry does not modify input. + */ +static int test_curry(isl_ctx *ctx) +{ + const char *str; + isl_basic_map *bmap1, *bmap2; + int equal; + + str = "{ [A[] -> B[]] -> C[] }"; + bmap1 = isl_basic_map_read_from_str(ctx, str); + bmap2 = isl_basic_map_curry(isl_basic_map_copy(bmap1)); + equal = isl_basic_map_is_equal(bmap1, bmap2); + isl_basic_map_free(bmap1); + isl_basic_map_free(bmap2); + + if (equal < 0) + return -1; + if (equal) + isl_die(ctx, isl_error_unknown, + "curried map should not be equal to original", + return -1); + + return 0; +} + +struct { + const char *set; + const char *ma; + const char *res; +} preimage_tests[] = { + { "{ B[i,j] : 0 <= i < 10 and 0 <= j < 100 }", + "{ A[j,i] -> B[i,j] }", + "{ A[j,i] : 0 <= i < 10 and 0 <= j < 100 }" }, + { "{ rat: B[i,j] : 0 <= i, j and 3 i + 5 j <= 100 }", + "{ A[a,b] -> B[a/2,b/6] }", + "{ rat: A[a,b] : 0 <= a, b and 9 a + 5 b <= 600 }" }, + { "{ B[i,j] : 0 <= i, j and 3 i + 5 j <= 100 }", + "{ A[a,b] -> B[a/2,b/6] }", + "{ A[a,b] : 0 <= a, b and 9 a + 5 b <= 600 and " + "exists i,j : a = 2 i and b = 6 j }" }, + { "[n] -> { S[i] : 0 <= i <= 100 }", "[n] -> { S[n] }", + "[n] -> { : 0 <= n <= 100 }" }, + { "{ B[i] : 0 <= i < 100 and exists a : i = 4 a }", + "{ A[a] -> B[2a] }", + "{ A[a] : 0 <= a < 50 and exists b : a = 2 b }" }, + { "{ B[i] : 0 <= i < 100 and exists a : i = 4 a }", + "{ A[a] -> B[([a/2])] }", + "{ A[a] : 0 <= a < 200 and exists b : [a/2] = 4 b }" }, + { "{ B[i,j,k] : 0 <= i,j,k <= 100 }", + "{ A[a] -> B[a,a,a/3] }", + "{ A[a] : 0 <= a <= 100 and exists b : a = 3 b }" }, + { "{ B[i,j] : j = [(i)/2] } ", "{ A[i,j] -> B[i/3,j] }", + "{ A[i,j] : j = [(i)/6] and exists a : i = 3 a }" }, +}; + +static int test_preimage_basic_set(isl_ctx *ctx) +{ + int i; + isl_basic_set *bset1, *bset2; + isl_multi_aff *ma; + int equal; + + for (i = 0; i < ARRAY_SIZE(preimage_tests); ++i) { + bset1 = isl_basic_set_read_from_str(ctx, preimage_tests[i].set); + ma = isl_multi_aff_read_from_str(ctx, preimage_tests[i].ma); + bset2 = isl_basic_set_read_from_str(ctx, preimage_tests[i].res); + bset1 = isl_basic_set_preimage_multi_aff(bset1, ma); + equal = isl_basic_set_is_equal(bset1, bset2); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "bad preimage", + return -1); + } + + return 0; +} + +struct { + const char *map; + const char *ma; + const char *res; +} preimage_domain_tests[] = { + { "{ B[i,j] -> C[2i + 3j] : 0 <= i < 10 and 0 <= j < 100 }", + "{ A[j,i] -> B[i,j] }", + "{ A[j,i] -> C[2i + 3j] : 0 <= i < 10 and 0 <= j < 100 }" }, + { "{ B[i] -> C[i]; D[i] -> E[i] }", + "{ A[i] -> B[i + 1] }", + "{ A[i] -> C[i + 1] }" }, + { "{ B[i] -> C[i]; B[i] -> E[i] }", + "{ A[i] -> B[i + 1] }", + "{ A[i] -> C[i + 1]; A[i] -> E[i + 1] }" }, + { "{ B[i] -> C[([i/2])] }", + "{ A[i] -> B[2i] }", + "{ A[i] -> C[i] }" }, + { "{ B[i,j] -> C[([i/2]), ([(i+j)/3])] }", + "{ A[i] -> B[([i/5]), ([i/7])] }", + "{ A[i] -> C[([([i/5])/2]), ([(([i/5])+([i/7]))/3])] }" }, + { "[N] -> { B[i] -> C[([N/2]), i, ([N/3])] }", + "[N] -> { A[] -> B[([N/5])] }", + "[N] -> { A[] -> C[([N/2]), ([N/5]), ([N/3])] }" }, + { "{ B[i] -> C[i] : exists a : i = 5 a }", + "{ A[i] -> B[2i] }", + "{ A[i] -> C[2i] : exists a : 2i = 5 a }" }, + { "{ B[i] -> C[i] : exists a : i = 2 a; " + "B[i] -> D[i] : exists a : i = 2 a + 1 }", + "{ A[i] -> B[2i] }", + "{ A[i] -> C[2i] }" }, + { "{ A[i] -> B[i] }", "{ C[i] -> A[(i + floor(i/3))/2] }", + "{ C[i] -> B[j] : 2j = i + floor(i/3) }" }, +}; + +static int test_preimage_union_map(isl_ctx *ctx) +{ + int i; + isl_union_map *umap1, *umap2; + isl_multi_aff *ma; + int equal; + + for (i = 0; i < ARRAY_SIZE(preimage_domain_tests); ++i) { + umap1 = isl_union_map_read_from_str(ctx, + preimage_domain_tests[i].map); + ma = isl_multi_aff_read_from_str(ctx, + preimage_domain_tests[i].ma); + umap2 = isl_union_map_read_from_str(ctx, + preimage_domain_tests[i].res); + umap1 = isl_union_map_preimage_domain_multi_aff(umap1, ma); + equal = isl_union_map_is_equal(umap1, umap2); + isl_union_map_free(umap1); + isl_union_map_free(umap2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "bad preimage", + return -1); + } + + return 0; +} + +static int test_preimage(isl_ctx *ctx) +{ + if (test_preimage_basic_set(ctx) < 0) + return -1; + if (test_preimage_union_map(ctx) < 0) + return -1; + + return 0; +} + +struct { + const char *ma1; + const char *ma; + const char *res; +} pullback_tests[] = { + { "{ B[i,j] -> C[i + 2j] }" , "{ A[a,b] -> B[b,a] }", + "{ A[a,b] -> C[b + 2a] }" }, + { "{ B[i] -> C[2i] }", "{ A[a] -> B[(a)/2] }", "{ A[a] -> C[a] }" }, + { "{ B[i] -> C[(i)/2] }", "{ A[a] -> B[2a] }", "{ A[a] -> C[a] }" }, + { "{ B[i] -> C[(i)/2] }", "{ A[a] -> B[(a)/3] }", + "{ A[a] -> C[(a)/6] }" }, + { "{ B[i] -> C[2i] }", "{ A[a] -> B[5a] }", "{ A[a] -> C[10a] }" }, + { "{ B[i] -> C[2i] }", "{ A[a] -> B[(a)/3] }", + "{ A[a] -> C[(2a)/3] }" }, + { "{ B[i,j] -> C[i + j] }", "{ A[a] -> B[a,a] }", "{ A[a] -> C[2a] }"}, + { "{ B[a] -> C[a,a] }", "{ A[i,j] -> B[i + j] }", + "{ A[i,j] -> C[i + j, i + j] }"}, + { "{ B[i] -> C[([i/2])] }", "{ B[5] }", "{ C[2] }" }, + { "[n] -> { B[i,j] -> C[([i/2]) + 2j] }", + "[n] -> { B[n,[n/3]] }", "[n] -> { C[([n/2]) + 2*[n/3]] }", }, + { "{ [i, j] -> [floor((i)/4) + floor((2*i+j)/5)] }", + "{ [i, j] -> [floor((i)/3), j] }", + "{ [i, j] -> [(floor((i)/12) + floor((j + 2*floor((i)/3))/5))] }" }, +}; + +static int test_pullback(isl_ctx *ctx) +{ + int i; + isl_multi_aff *ma1, *ma2; + isl_multi_aff *ma; + int equal; + + for (i = 0; i < ARRAY_SIZE(pullback_tests); ++i) { + ma1 = isl_multi_aff_read_from_str(ctx, pullback_tests[i].ma1); + ma = isl_multi_aff_read_from_str(ctx, pullback_tests[i].ma); + ma2 = isl_multi_aff_read_from_str(ctx, pullback_tests[i].res); + ma1 = isl_multi_aff_pullback_multi_aff(ma1, ma); + equal = isl_multi_aff_plain_is_equal(ma1, ma2); + isl_multi_aff_free(ma1); + isl_multi_aff_free(ma2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, "bad pullback", + return -1); + } + + return 0; +} + +/* Check that negation is printed correctly and that equal expressions + * are correctly identified. + */ +static int test_ast(isl_ctx *ctx) +{ + isl_ast_expr *expr, *expr1, *expr2, *expr3; + char *str; + int ok, equal; + + expr1 = isl_ast_expr_from_id(isl_id_alloc(ctx, "A", NULL)); + expr2 = isl_ast_expr_from_id(isl_id_alloc(ctx, "B", NULL)); + expr = isl_ast_expr_add(expr1, expr2); + expr2 = isl_ast_expr_copy(expr); + expr = isl_ast_expr_neg(expr); + expr2 = isl_ast_expr_neg(expr2); + equal = isl_ast_expr_is_equal(expr, expr2); + str = isl_ast_expr_to_str(expr); + ok = str ? !strcmp(str, "-(A + B)") : -1; + free(str); + isl_ast_expr_free(expr); + isl_ast_expr_free(expr2); + + if (ok < 0 || equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "equal expressions not considered equal", return -1); + if (!ok) + isl_die(ctx, isl_error_unknown, + "isl_ast_expr printed incorrectly", return -1); + + expr1 = isl_ast_expr_from_id(isl_id_alloc(ctx, "A", NULL)); + expr2 = isl_ast_expr_from_id(isl_id_alloc(ctx, "B", NULL)); + expr = isl_ast_expr_add(expr1, expr2); + expr3 = isl_ast_expr_from_id(isl_id_alloc(ctx, "C", NULL)); + expr = isl_ast_expr_sub(expr3, expr); + str = isl_ast_expr_to_str(expr); + ok = str ? !strcmp(str, "C - (A + B)") : -1; + free(str); + isl_ast_expr_free(expr); + + if (ok < 0) + return -1; + if (!ok) + isl_die(ctx, isl_error_unknown, + "isl_ast_expr printed incorrectly", return -1); + + return 0; +} + +/* Check that isl_ast_build_expr_from_set returns a valid expression + * for an empty set. Note that isl_ast_build_expr_from_set getting + * called on an empty set probably indicates a bug in the caller. + */ +static int test_ast_build(isl_ctx *ctx) +{ + isl_set *set; + isl_ast_build *build; + isl_ast_expr *expr; + + set = isl_set_universe(isl_space_params_alloc(ctx, 0)); + build = isl_ast_build_from_context(set); + + set = isl_set_empty(isl_space_params_alloc(ctx, 0)); + expr = isl_ast_build_expr_from_set(build, set); + + isl_ast_expr_free(expr); + isl_ast_build_free(build); + + if (!expr) + return -1; + + return 0; +} + +/* Internal data structure for before_for and after_for callbacks. + * + * depth is the current depth + * before is the number of times before_for has been called + * after is the number of times after_for has been called + */ +struct isl_test_codegen_data { + int depth; + int before; + int after; +}; + +/* This function is called before each for loop in the AST generated + * from test_ast_gen1. + * + * Increment the number of calls and the depth. + * Check that the space returned by isl_ast_build_get_schedule_space + * matches the target space of the schedule returned by + * isl_ast_build_get_schedule. + * Return an isl_id that is checked by the corresponding call + * to after_for. + */ +static __isl_give isl_id *before_for(__isl_keep isl_ast_build *build, + void *user) +{ + struct isl_test_codegen_data *data = user; + isl_ctx *ctx; + isl_space *space; + isl_union_map *schedule; + isl_union_set *uset; + isl_set *set; + int empty; + char name[] = "d0"; + + ctx = isl_ast_build_get_ctx(build); + + if (data->before >= 3) + isl_die(ctx, isl_error_unknown, + "unexpected number of for nodes", return NULL); + if (data->depth >= 2) + isl_die(ctx, isl_error_unknown, + "unexpected depth", return NULL); + + snprintf(name, sizeof(name), "d%d", data->depth); + data->before++; + data->depth++; + + schedule = isl_ast_build_get_schedule(build); + uset = isl_union_map_range(schedule); + if (!uset) + return NULL; + if (isl_union_set_n_set(uset) != 1) { + isl_union_set_free(uset); + isl_die(ctx, isl_error_unknown, + "expecting single range space", return NULL); + } + + space = isl_ast_build_get_schedule_space(build); + set = isl_union_set_extract_set(uset, space); + isl_union_set_free(uset); + empty = isl_set_is_empty(set); + isl_set_free(set); + + if (empty < 0) + return NULL; + if (empty) + isl_die(ctx, isl_error_unknown, + "spaces don't match", return NULL); + + return isl_id_alloc(ctx, name, NULL); +} + +/* This function is called after each for loop in the AST generated + * from test_ast_gen1. + * + * Increment the number of calls and decrement the depth. + * Check that the annotation attached to the node matches + * the isl_id returned by the corresponding call to before_for. + */ +static __isl_give isl_ast_node *after_for(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user) +{ + struct isl_test_codegen_data *data = user; + isl_id *id; + const char *name; + int valid; + + data->after++; + data->depth--; + + if (data->after > data->before) + isl_die(isl_ast_node_get_ctx(node), isl_error_unknown, + "mismatch in number of for nodes", + return isl_ast_node_free(node)); + + id = isl_ast_node_get_annotation(node); + if (!id) + isl_die(isl_ast_node_get_ctx(node), isl_error_unknown, + "missing annotation", return isl_ast_node_free(node)); + + name = isl_id_get_name(id); + valid = name && atoi(name + 1) == data->depth; + isl_id_free(id); + + if (!valid) + isl_die(isl_ast_node_get_ctx(node), isl_error_unknown, + "wrong annotation", return isl_ast_node_free(node)); + + return node; +} + +/* Check that the before_each_for and after_each_for callbacks + * are called for each for loop in the generated code, + * that they are called in the right order and that the isl_id + * returned from the before_each_for callback is attached to + * the isl_ast_node passed to the corresponding after_each_for call. + */ +static int test_ast_gen1(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_union_map *schedule; + isl_ast_build *build; + isl_ast_node *tree; + struct isl_test_codegen_data data; + + str = "[N] -> { : N >= 10 }"; + set = isl_set_read_from_str(ctx, str); + str = "[N] -> { A[i,j] -> S[8,i,3,j] : 0 <= i,j <= N; " + "B[i,j] -> S[8,j,9,i] : 0 <= i,j <= N }"; + schedule = isl_union_map_read_from_str(ctx, str); + + data.before = 0; + data.after = 0; + data.depth = 0; + build = isl_ast_build_from_context(set); + build = isl_ast_build_set_before_each_for(build, + &before_for, &data); + build = isl_ast_build_set_after_each_for(build, + &after_for, &data); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + if (!tree) + return -1; + + isl_ast_node_free(tree); + + if (data.before != 3 || data.after != 3) + isl_die(ctx, isl_error_unknown, + "unexpected number of for nodes", return -1); + + return 0; +} + +/* Check that the AST generator handles domains that are integrally disjoint + * but not rationally disjoint. + */ +static int test_ast_gen2(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_union_map *schedule; + isl_union_map *options; + isl_ast_build *build; + isl_ast_node *tree; + + str = "{ A[i,j] -> [i,j] : 0 <= i,j <= 1 }"; + schedule = isl_union_map_read_from_str(ctx, str); + set = isl_set_universe(isl_space_params_alloc(ctx, 0)); + build = isl_ast_build_from_context(set); + + str = "{ [i,j] -> atomic[1] : i + j = 1; [i,j] -> unroll[1] : i = j }"; + options = isl_union_map_read_from_str(ctx, str); + build = isl_ast_build_set_options(build, options); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + if (!tree) + return -1; + isl_ast_node_free(tree); + + return 0; +} + +/* Increment *user on each call. + */ +static __isl_give isl_ast_node *count_domains(__isl_take isl_ast_node *node, + __isl_keep isl_ast_build *build, void *user) +{ + int *n = user; + + (*n)++; + + return node; +} + +/* Test that unrolling tries to minimize the number of instances. + * In particular, for the schedule given below, make sure it generates + * 3 nodes (rather than 101). + */ +static int test_ast_gen3(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_union_map *schedule; + isl_union_map *options; + isl_ast_build *build; + isl_ast_node *tree; + int n_domain = 0; + + str = "[n] -> { A[i] -> [i] : 0 <= i <= 100 and n <= i <= n + 2 }"; + schedule = isl_union_map_read_from_str(ctx, str); + set = isl_set_universe(isl_space_params_alloc(ctx, 0)); + + str = "{ [i] -> unroll[0] }"; + options = isl_union_map_read_from_str(ctx, str); + + build = isl_ast_build_from_context(set); + build = isl_ast_build_set_options(build, options); + build = isl_ast_build_set_at_each_domain(build, + &count_domains, &n_domain); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + if (!tree) + return -1; + + isl_ast_node_free(tree); + + if (n_domain != 3) + isl_die(ctx, isl_error_unknown, + "unexpected number of for nodes", return -1); + + return 0; +} + +/* Check that if the ast_build_exploit_nested_bounds options is set, + * we do not get an outer if node in the generated AST, + * while we do get such an outer if node if the options is not set. + */ +static int test_ast_gen4(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_union_map *schedule; + isl_ast_build *build; + isl_ast_node *tree; + enum isl_ast_node_type type; + int enb; + + enb = isl_options_get_ast_build_exploit_nested_bounds(ctx); + str = "[N,M] -> { A[i,j] -> [i,j] : 0 <= i <= N and 0 <= j <= M }"; + + isl_options_set_ast_build_exploit_nested_bounds(ctx, 1); + + schedule = isl_union_map_read_from_str(ctx, str); + set = isl_set_universe(isl_space_params_alloc(ctx, 0)); + build = isl_ast_build_from_context(set); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + if (!tree) + return -1; + + type = isl_ast_node_get_type(tree); + isl_ast_node_free(tree); + + if (type == isl_ast_node_if) + isl_die(ctx, isl_error_unknown, + "not expecting if node", return -1); + + isl_options_set_ast_build_exploit_nested_bounds(ctx, 0); + + schedule = isl_union_map_read_from_str(ctx, str); + set = isl_set_universe(isl_space_params_alloc(ctx, 0)); + build = isl_ast_build_from_context(set); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + if (!tree) + return -1; + + type = isl_ast_node_get_type(tree); + isl_ast_node_free(tree); + + if (type != isl_ast_node_if) + isl_die(ctx, isl_error_unknown, + "expecting if node", return -1); + + isl_options_set_ast_build_exploit_nested_bounds(ctx, enb); + + return 0; +} + +/* This function is called for each leaf in the AST generated + * from test_ast_gen5. + * + * We finalize the AST generation by extending the outer schedule + * with a zero-dimensional schedule. If this results in any for loops, + * then this means that we did not pass along enough information + * about the outer schedule to the inner AST generation. + */ +static __isl_give isl_ast_node *create_leaf(__isl_take isl_ast_build *build, + void *user) +{ + isl_union_map *schedule, *extra; + isl_ast_node *tree; + + schedule = isl_ast_build_get_schedule(build); + extra = isl_union_map_copy(schedule); + extra = isl_union_map_from_domain(isl_union_map_domain(extra)); + schedule = isl_union_map_range_product(schedule, extra); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + + if (!tree) + return NULL; + + if (isl_ast_node_get_type(tree) == isl_ast_node_for) + isl_die(isl_ast_node_get_ctx(tree), isl_error_unknown, + "code should not contain any for loop", + return isl_ast_node_free(tree)); + + return tree; +} + +/* Check that we do not lose any information when going back and + * forth between internal and external schedule. + * + * In particular, we create an AST where we unroll the only + * non-constant dimension in the schedule. We therefore do + * not expect any for loops in the AST. However, older versions + * of isl would not pass along enough information about the outer + * schedule when performing an inner code generation from a create_leaf + * callback, resulting in the inner code generation producing a for loop. + */ +static int test_ast_gen5(isl_ctx *ctx) +{ + const char *str; + isl_set *set; + isl_union_map *schedule, *options; + isl_ast_build *build; + isl_ast_node *tree; + + str = "{ A[] -> [1, 1, 2]; B[i] -> [1, i, 0] : i >= 1 and i <= 2 }"; + schedule = isl_union_map_read_from_str(ctx, str); + + str = "{ [a, b, c] -> unroll[1] : exists (e0 = [(a)/4]: " + "4e0 >= -1 + a - b and 4e0 <= -2 + a + b) }"; + options = isl_union_map_read_from_str(ctx, str); + + set = isl_set_universe(isl_space_params_alloc(ctx, 0)); + build = isl_ast_build_from_context(set); + build = isl_ast_build_set_options(build, options); + build = isl_ast_build_set_create_leaf(build, &create_leaf, NULL); + tree = isl_ast_build_node_from_schedule_map(build, schedule); + isl_ast_build_free(build); + isl_ast_node_free(tree); + if (!tree) + return -1; + + return 0; +} + +static int test_ast_gen(isl_ctx *ctx) +{ + if (test_ast_gen1(ctx) < 0) + return -1; + if (test_ast_gen2(ctx) < 0) + return -1; + if (test_ast_gen3(ctx) < 0) + return -1; + if (test_ast_gen4(ctx) < 0) + return -1; + if (test_ast_gen5(ctx) < 0) + return -1; + return 0; +} + +/* Check if dropping output dimensions from an isl_pw_multi_aff + * works properly. + */ +static int test_pw_multi_aff(isl_ctx *ctx) +{ + const char *str; + isl_pw_multi_aff *pma1, *pma2; + int equal; + + str = "{ [i,j] -> [i+j, 4i-j] }"; + pma1 = isl_pw_multi_aff_read_from_str(ctx, str); + str = "{ [i,j] -> [4i-j] }"; + pma2 = isl_pw_multi_aff_read_from_str(ctx, str); + + pma1 = isl_pw_multi_aff_drop_dims(pma1, isl_dim_out, 0, 1); + + equal = isl_pw_multi_aff_plain_is_equal(pma1, pma2); + + isl_pw_multi_aff_free(pma1); + isl_pw_multi_aff_free(pma2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "expressions not equal", return -1); + + return 0; +} + +/* Check that we can properly parse multi piecewise affine expressions + * where the piecewise affine expressions have different domains. + */ +static int test_multi_pw_aff(isl_ctx *ctx) +{ + const char *str; + isl_set *dom, *dom2; + isl_multi_pw_aff *mpa1, *mpa2; + isl_pw_aff *pa; + int equal; + int equal_domain; + + mpa1 = isl_multi_pw_aff_read_from_str(ctx, "{ [i] -> [i] }"); + dom = isl_set_read_from_str(ctx, "{ [i] : i > 0 }"); + mpa1 = isl_multi_pw_aff_intersect_domain(mpa1, dom); + mpa2 = isl_multi_pw_aff_read_from_str(ctx, "{ [i] -> [2i] }"); + mpa2 = isl_multi_pw_aff_flat_range_product(mpa1, mpa2); + str = "{ [i] -> [(i : i > 0), 2i] }"; + mpa1 = isl_multi_pw_aff_read_from_str(ctx, str); + + equal = isl_multi_pw_aff_plain_is_equal(mpa1, mpa2); + + pa = isl_multi_pw_aff_get_pw_aff(mpa1, 0); + dom = isl_pw_aff_domain(pa); + pa = isl_multi_pw_aff_get_pw_aff(mpa1, 1); + dom2 = isl_pw_aff_domain(pa); + equal_domain = isl_set_is_equal(dom, dom2); + + isl_set_free(dom); + isl_set_free(dom2); + isl_multi_pw_aff_free(mpa1); + isl_multi_pw_aff_free(mpa2); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "expressions not equal", return -1); + + if (equal_domain < 0) + return -1; + if (equal_domain) + isl_die(ctx, isl_error_unknown, + "domains unexpectedly equal", return -1); + + return 0; +} + +/* This is a regression test for a bug where isl_basic_map_simplify + * would end up in an infinite loop. In particular, we construct + * an empty basic set that is not obviously empty. + * isl_basic_set_is_empty marks the basic set as empty. + * After projecting out i3, the variable can be dropped completely, + * but isl_basic_map_simplify refrains from doing so if the basic set + * is empty and would end up in an infinite loop if it didn't test + * explicitly for empty basic maps in the outer loop. + */ +static int test_simplify_1(isl_ctx *ctx) +{ + const char *str; + isl_basic_set *bset; + int empty; + + str = "{ [i0, i1, i2, i3] : i0 >= -2 and 6i2 <= 4 + i0 + 5i1 and " + "i2 <= 22 and 75i2 <= 111 + 13i0 + 60i1 and " + "25i2 >= 38 + 6i0 + 20i1 and i0 <= -1 and i2 >= 20 and " + "i3 >= i2 }"; + bset = isl_basic_set_read_from_str(ctx, str); + empty = isl_basic_set_is_empty(bset); + bset = isl_basic_set_project_out(bset, isl_dim_set, 3, 1); + isl_basic_set_free(bset); + if (!bset) + return -1; + if (!empty) + isl_die(ctx, isl_error_unknown, + "basic set should be empty", return -1); + + return 0; +} + +/* Check that the equality in the set description below + * is simplified away. + */ +static int test_simplify_2(isl_ctx *ctx) +{ + const char *str; + isl_basic_set *bset; + isl_bool universe; + + str = "{ [a] : exists e0, e1: 32e1 = 31 + 31a + 31e0 }"; + bset = isl_basic_set_read_from_str(ctx, str); + universe = isl_basic_set_plain_is_universe(bset); + isl_basic_set_free(bset); + + if (universe < 0) + return -1; + if (!universe) + isl_die(ctx, isl_error_unknown, + "equality not simplified away", return -1); + return 0; +} + +/* Some simplification tests. + */ +static int test_simplify(isl_ctx *ctx) +{ + if (test_simplify_1(ctx) < 0) + return -1; + if (test_simplify_2(ctx) < 0) + return -1; + return 0; +} + +/* This is a regression test for a bug where isl_tab_basic_map_partial_lexopt + * with gbr context would fail to disable the use of the shifted tableau + * when transferring equalities for the input to the context, resulting + * in invalid sample values. + */ +static int test_partial_lexmin(isl_ctx *ctx) +{ + const char *str; + isl_basic_set *bset; + isl_basic_map *bmap; + isl_map *map; + + str = "{ [1, b, c, 1 - c] -> [e] : 2e <= -c and 2e >= -3 + c }"; + bmap = isl_basic_map_read_from_str(ctx, str); + str = "{ [a, b, c, d] : c <= 1 and 2d >= 6 - 4b - c }"; + bset = isl_basic_set_read_from_str(ctx, str); + map = isl_basic_map_partial_lexmin(bmap, bset, NULL); + isl_map_free(map); + + if (!map) + return -1; + + return 0; +} + +/* Check that the variable compression performed on the existentially + * quantified variables inside isl_basic_set_compute_divs is not confused + * by the implicit equalities among the parameters. + */ +static int test_compute_divs(isl_ctx *ctx) +{ + const char *str; + isl_basic_set *bset; + isl_set *set; + + str = "[a, b, c, d, e] -> { [] : exists (e0: 2d = b and a <= 124 and " + "b <= 2046 and b >= 0 and b <= 60 + 64a and 2e >= b + 2c and " + "2e >= b and 2e <= 1 + b and 2e <= 1 + b + 2c and " + "32768e0 >= -124 + a and 2097152e0 <= 60 + 64a - b) }"; + bset = isl_basic_set_read_from_str(ctx, str); + set = isl_basic_set_compute_divs(bset); + isl_set_free(set); + if (!set) + return -1; + + return 0; +} + +/* Check that the reaching domain elements and the prefix schedule + * at a leaf node are the same before and after grouping. + */ +static int test_schedule_tree_group_1(isl_ctx *ctx) +{ + int equal; + const char *str; + isl_id *id; + isl_union_set *uset; + isl_multi_union_pw_aff *mupa; + isl_union_pw_multi_aff *upma1, *upma2; + isl_union_set *domain1, *domain2; + isl_union_map *umap1, *umap2; + isl_schedule_node *node; + + str = "{ S1[i,j] : 0 <= i,j < 10; S2[i,j] : 0 <= i,j < 10 }"; + uset = isl_union_set_read_from_str(ctx, str); + node = isl_schedule_node_from_domain(uset); + node = isl_schedule_node_child(node, 0); + str = "[{ S1[i,j] -> [i]; S2[i,j] -> [9 - i] }]"; + mupa = isl_multi_union_pw_aff_read_from_str(ctx, str); + node = isl_schedule_node_insert_partial_schedule(node, mupa); + node = isl_schedule_node_child(node, 0); + str = "[{ S1[i,j] -> [j]; S2[i,j] -> [j] }]"; + mupa = isl_multi_union_pw_aff_read_from_str(ctx, str); + node = isl_schedule_node_insert_partial_schedule(node, mupa); + node = isl_schedule_node_child(node, 0); + umap1 = isl_schedule_node_get_prefix_schedule_union_map(node); + upma1 = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node); + domain1 = isl_schedule_node_get_domain(node); + id = isl_id_alloc(ctx, "group", NULL); + node = isl_schedule_node_parent(node); + node = isl_schedule_node_group(node, id); + node = isl_schedule_node_child(node, 0); + umap2 = isl_schedule_node_get_prefix_schedule_union_map(node); + upma2 = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node); + domain2 = isl_schedule_node_get_domain(node); + equal = isl_union_pw_multi_aff_plain_is_equal(upma1, upma2); + if (equal >= 0 && equal) + equal = isl_union_set_is_equal(domain1, domain2); + if (equal >= 0 && equal) + equal = isl_union_map_is_equal(umap1, umap2); + isl_union_map_free(umap1); + isl_union_map_free(umap2); + isl_union_set_free(domain1); + isl_union_set_free(domain2); + isl_union_pw_multi_aff_free(upma1); + isl_union_pw_multi_aff_free(upma2); + isl_schedule_node_free(node); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "expressions not equal", return -1); + + return 0; +} + +/* Check that we can have nested groupings and that the union map + * schedule representation is the same before and after the grouping. + * Note that after the grouping, the union map representation contains + * the domain constraints from the ranges of the expansion nodes, + * while they are missing from the union map representation of + * the tree without expansion nodes. + * + * Also check that the global expansion is as expected. + */ +static int test_schedule_tree_group_2(isl_ctx *ctx) +{ + int equal, equal_expansion; + const char *str; + isl_id *id; + isl_union_set *uset; + isl_union_map *umap1, *umap2; + isl_union_map *expansion1, *expansion2; + isl_union_set_list *filters; + isl_multi_union_pw_aff *mupa; + isl_schedule *schedule; + isl_schedule_node *node; + + str = "{ S1[i,j] : 0 <= i,j < 10; S2[i,j] : 0 <= i,j < 10; " + "S3[i,j] : 0 <= i,j < 10 }"; + uset = isl_union_set_read_from_str(ctx, str); + node = isl_schedule_node_from_domain(uset); + node = isl_schedule_node_child(node, 0); + str = "[{ S1[i,j] -> [i]; S2[i,j] -> [i]; S3[i,j] -> [i] }]"; + mupa = isl_multi_union_pw_aff_read_from_str(ctx, str); + node = isl_schedule_node_insert_partial_schedule(node, mupa); + node = isl_schedule_node_child(node, 0); + str = "{ S1[i,j] }"; + uset = isl_union_set_read_from_str(ctx, str); + filters = isl_union_set_list_from_union_set(uset); + str = "{ S2[i,j]; S3[i,j] }"; + uset = isl_union_set_read_from_str(ctx, str); + filters = isl_union_set_list_add(filters, uset); + node = isl_schedule_node_insert_sequence(node, filters); + node = isl_schedule_node_child(node, 1); + node = isl_schedule_node_child(node, 0); + str = "{ S2[i,j] }"; + uset = isl_union_set_read_from_str(ctx, str); + filters = isl_union_set_list_from_union_set(uset); + str = "{ S3[i,j] }"; + uset = isl_union_set_read_from_str(ctx, str); + filters = isl_union_set_list_add(filters, uset); + node = isl_schedule_node_insert_sequence(node, filters); + + schedule = isl_schedule_node_get_schedule(node); + umap1 = isl_schedule_get_map(schedule); + uset = isl_schedule_get_domain(schedule); + umap1 = isl_union_map_intersect_domain(umap1, uset); + isl_schedule_free(schedule); + + node = isl_schedule_node_parent(node); + node = isl_schedule_node_parent(node); + id = isl_id_alloc(ctx, "group1", NULL); + node = isl_schedule_node_group(node, id); + node = isl_schedule_node_child(node, 1); + node = isl_schedule_node_child(node, 0); + id = isl_id_alloc(ctx, "group2", NULL); + node = isl_schedule_node_group(node, id); + + schedule = isl_schedule_node_get_schedule(node); + umap2 = isl_schedule_get_map(schedule); + isl_schedule_free(schedule); + + node = isl_schedule_node_root(node); + node = isl_schedule_node_child(node, 0); + expansion1 = isl_schedule_node_get_subtree_expansion(node); + isl_schedule_node_free(node); + + str = "{ group1[i] -> S1[i,j] : 0 <= i,j < 10; " + "group1[i] -> S2[i,j] : 0 <= i,j < 10; " + "group1[i] -> S3[i,j] : 0 <= i,j < 10 }"; + + expansion2 = isl_union_map_read_from_str(ctx, str); + + equal = isl_union_map_is_equal(umap1, umap2); + equal_expansion = isl_union_map_is_equal(expansion1, expansion2); + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + isl_union_map_free(expansion1); + isl_union_map_free(expansion2); + + if (equal < 0 || equal_expansion < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "expressions not equal", return -1); + if (!equal_expansion) + isl_die(ctx, isl_error_unknown, + "unexpected expansion", return -1); + + return 0; +} + +/* Some tests for the isl_schedule_node_group function. + */ +static int test_schedule_tree_group(isl_ctx *ctx) +{ + if (test_schedule_tree_group_1(ctx) < 0) + return -1; + if (test_schedule_tree_group_2(ctx) < 0) + return -1; + return 0; +} + +struct { + const char *set; + const char *dual; +} coef_tests[] = { + { "{ rat: [i] : 0 <= i <= 10 }", + "{ rat: coefficients[[cst] -> [a]] : cst >= 0 and 10a + cst >= 0 }" }, + { "{ rat: [i] : FALSE }", + "{ rat: coefficients[[cst] -> [a]] }" }, + { "{ rat: [i] : }", + "{ rat: coefficients[[cst] -> [0]] : cst >= 0 }" }, +}; + +struct { + const char *set; + const char *dual; +} sol_tests[] = { + { "{ rat: coefficients[[cst] -> [a]] : cst >= 0 and 10a + cst >= 0 }", + "{ rat: [i] : 0 <= i <= 10 }" }, + { "{ rat: coefficients[[cst] -> [a]] : FALSE }", + "{ rat: [i] }" }, + { "{ rat: coefficients[[cst] -> [a]] }", + "{ rat: [i] : FALSE }" }, +}; + +/* Test the basic functionality of isl_basic_set_coefficients and + * isl_basic_set_solutions. + */ +static int test_dual(isl_ctx *ctx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coef_tests); ++i) { + int equal; + isl_basic_set *bset1, *bset2; + + bset1 = isl_basic_set_read_from_str(ctx, coef_tests[i].set); + bset2 = isl_basic_set_read_from_str(ctx, coef_tests[i].dual); + bset1 = isl_basic_set_coefficients(bset1); + equal = isl_basic_set_is_equal(bset1, bset2); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "incorrect dual", return -1); + } + + for (i = 0; i < ARRAY_SIZE(sol_tests); ++i) { + int equal; + isl_basic_set *bset1, *bset2; + + bset1 = isl_basic_set_read_from_str(ctx, sol_tests[i].set); + bset2 = isl_basic_set_read_from_str(ctx, sol_tests[i].dual); + bset1 = isl_basic_set_solutions(bset1); + equal = isl_basic_set_is_equal(bset1, bset2); + isl_basic_set_free(bset1); + isl_basic_set_free(bset2); + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "incorrect dual", return -1); + } + + return 0; +} + +struct { + int scale_tile; + int shift_point; + const char *domain; + const char *schedule; + const char *sizes; + const char *tile; + const char *point; +} tile_tests[] = { + { 0, 0, "[n] -> { S[i,j] : 0 <= i,j < n }", + "[{ S[i,j] -> [i] }, { S[i,j] -> [j] }]", + "{ [32,32] }", + "[{ S[i,j] -> [floor(i/32)] }, { S[i,j] -> [floor(j/32)] }]", + "[{ S[i,j] -> [i] }, { S[i,j] -> [j] }]", + }, + { 1, 0, "[n] -> { S[i,j] : 0 <= i,j < n }", + "[{ S[i,j] -> [i] }, { S[i,j] -> [j] }]", + "{ [32,32] }", + "[{ S[i,j] -> [32*floor(i/32)] }, { S[i,j] -> [32*floor(j/32)] }]", + "[{ S[i,j] -> [i] }, { S[i,j] -> [j] }]", + }, + { 0, 1, "[n] -> { S[i,j] : 0 <= i,j < n }", + "[{ S[i,j] -> [i] }, { S[i,j] -> [j] }]", + "{ [32,32] }", + "[{ S[i,j] -> [floor(i/32)] }, { S[i,j] -> [floor(j/32)] }]", + "[{ S[i,j] -> [i%32] }, { S[i,j] -> [j%32] }]", + }, + { 1, 1, "[n] -> { S[i,j] : 0 <= i,j < n }", + "[{ S[i,j] -> [i] }, { S[i,j] -> [j] }]", + "{ [32,32] }", + "[{ S[i,j] -> [32*floor(i/32)] }, { S[i,j] -> [32*floor(j/32)] }]", + "[{ S[i,j] -> [i%32] }, { S[i,j] -> [j%32] }]", + }, +}; + +/* Basic tiling tests. Create a schedule tree with a domain and a band node, + * tile the band and then check if the tile and point bands have the + * expected partial schedule. + */ +static int test_tile(isl_ctx *ctx) +{ + int i; + int scale; + int shift; + + scale = isl_options_get_tile_scale_tile_loops(ctx); + shift = isl_options_get_tile_shift_point_loops(ctx); + + for (i = 0; i < ARRAY_SIZE(tile_tests); ++i) { + int opt; + int equal; + const char *str; + isl_union_set *domain; + isl_multi_union_pw_aff *mupa, *mupa2; + isl_schedule_node *node; + isl_multi_val *sizes; + + opt = tile_tests[i].scale_tile; + isl_options_set_tile_scale_tile_loops(ctx, opt); + opt = tile_tests[i].shift_point; + isl_options_set_tile_shift_point_loops(ctx, opt); + + str = tile_tests[i].domain; + domain = isl_union_set_read_from_str(ctx, str); + node = isl_schedule_node_from_domain(domain); + node = isl_schedule_node_child(node, 0); + str = tile_tests[i].schedule; + mupa = isl_multi_union_pw_aff_read_from_str(ctx, str); + node = isl_schedule_node_insert_partial_schedule(node, mupa); + str = tile_tests[i].sizes; + sizes = isl_multi_val_read_from_str(ctx, str); + node = isl_schedule_node_band_tile(node, sizes); + + str = tile_tests[i].tile; + mupa = isl_multi_union_pw_aff_read_from_str(ctx, str); + mupa2 = isl_schedule_node_band_get_partial_schedule(node); + equal = isl_multi_union_pw_aff_plain_is_equal(mupa, mupa2); + isl_multi_union_pw_aff_free(mupa); + isl_multi_union_pw_aff_free(mupa2); + + node = isl_schedule_node_child(node, 0); + + str = tile_tests[i].point; + mupa = isl_multi_union_pw_aff_read_from_str(ctx, str); + mupa2 = isl_schedule_node_band_get_partial_schedule(node); + if (equal >= 0 && equal) + equal = isl_multi_union_pw_aff_plain_is_equal(mupa, + mupa2); + isl_multi_union_pw_aff_free(mupa); + isl_multi_union_pw_aff_free(mupa2); + + isl_schedule_node_free(node); + + if (equal < 0) + return -1; + if (!equal) + isl_die(ctx, isl_error_unknown, + "unexpected result", return -1); + } + + isl_options_set_tile_scale_tile_loops(ctx, scale); + isl_options_set_tile_shift_point_loops(ctx, shift); + + return 0; +} + +/* Check that the domain hash of a space is equal to the hash + * of the domain of the space. + */ +static int test_domain_hash(isl_ctx *ctx) +{ + isl_map *map; + isl_space *space; + uint32_t hash1, hash2; + + map = isl_map_read_from_str(ctx, "[n] -> { A[B[x] -> C[]] -> D[] }"); + space = isl_map_get_space(map); + isl_map_free(map); + hash1 = isl_space_get_domain_hash(space); + space = isl_space_domain(space); + hash2 = isl_space_get_hash(space); + isl_space_free(space); + + if (!space) + return -1; + if (hash1 != hash2) + isl_die(ctx, isl_error_unknown, + "domain hash not equal to hash of domain", return -1); + + return 0; +} + +/* Check that a universe basic set that is not obviously equal to the universe + * is still recognized as being equal to the universe. + */ +static int test_universe(isl_ctx *ctx) +{ + const char *s; + isl_basic_set *bset; + isl_bool is_univ; + + s = "{ [] : exists x, y : 3y <= 2x and y >= -3 + 2x and 2y >= 2 - x }"; + bset = isl_basic_set_read_from_str(ctx, s); + is_univ = isl_basic_set_is_universe(bset); + isl_basic_set_free(bset); + + if (is_univ < 0) + return -1; + if (!is_univ) + isl_die(ctx, isl_error_unknown, + "not recognized as universe set", return -1); + + return 0; +} + +struct { + const char *name; + int (*fn)(isl_ctx *ctx); +} tests [] = { + { "universe", &test_universe }, + { "domain hash", &test_domain_hash }, + { "dual", &test_dual }, + { "dependence analysis", &test_flow }, + { "val", &test_val }, + { "compute divs", &test_compute_divs }, + { "partial lexmin", &test_partial_lexmin }, + { "simplify", &test_simplify }, + { "curry", &test_curry }, + { "piecewise multi affine expressions", &test_pw_multi_aff }, + { "multi piecewise affine expressions", &test_multi_pw_aff }, + { "conversion", &test_conversion }, + { "list", &test_list }, + { "align parameters", &test_align_parameters }, + { "preimage", &test_preimage }, + { "pullback", &test_pullback }, + { "AST", &test_ast }, + { "AST build", &test_ast_build }, + { "AST generation", &test_ast_gen }, + { "eliminate", &test_eliminate }, + { "residue class", &test_residue_class }, + { "div", &test_div }, + { "slice", &test_slice }, + { "fixed power", &test_fixed_power }, + { "sample", &test_sample }, + { "output", &test_output }, + { "vertices", &test_vertices }, + { "fixed", &test_fixed }, + { "equal", &test_equal }, + { "disjoint", &test_disjoint }, + { "product", &test_product }, + { "dim_max", &test_dim_max }, + { "affine", &test_aff }, + { "injective", &test_injective }, + { "schedule (whole component)", &test_schedule_whole }, + { "schedule (incremental)", &test_schedule_incremental }, + { "schedule tree grouping", &test_schedule_tree_group }, + { "tile", &test_tile }, + { "union_pw", &test_union_pw }, + { "eval", &test_eval }, + { "parse", &test_parse }, + { "single-valued", &test_sv }, + { "affine hull", &test_affine_hull }, + { "simple_hull", &test_simple_hull }, + { "coalesce", &test_coalesce }, + { "factorize", &test_factorize }, + { "subset", &test_subset }, + { "subtract", &test_subtract }, + { "intersect", &test_intersect }, + { "lexmin", &test_lexmin }, + { "min", &test_min }, + { "gist", &test_gist }, + { "piecewise quasi-polynomials", &test_pwqp }, + { "lift", &test_lift }, + { "bound", &test_bound }, + { "union", &test_union }, + { "split periods", &test_split_periods }, + { "lexicographic order", &test_lex }, + { "bijectivity", &test_bijective }, + { "dataflow analysis", &test_dep }, + { "reading", &test_read }, + { "bounded", &test_bounded }, + { "construction", &test_construction }, + { "dimension manipulation", &test_dim }, + { "map application", &test_application }, + { "convex hull", &test_convex_hull }, + { "transitive closure", &test_closure }, +}; + +int main(int argc, char **argv) +{ + int i; + struct isl_ctx *ctx; + struct isl_options *options; + + srcdir = getenv("srcdir"); + assert(srcdir); + + options = isl_options_new_with_defaults(); + assert(options); + argc = isl_options_parse(options, argc, argv, ISL_ARG_ALL); + + ctx = isl_ctx_alloc_with_options(&isl_options_args, options); + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + printf("%s\n", tests[i].name); + if (tests[i].fn(ctx) < 0) + goto error; + } + isl_ctx_free(ctx); + return 0; +error: + isl_ctx_free(ctx); + return -1; +} Index: lib/Analysis/isl/isl_test_imath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_test_imath.c @@ -0,0 +1,79 @@ +/* + * Copyright 2015 INRIA Paris-Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Michael Kruse, INRIA Paris-Rocquencourt, + * Domaine de Voluceau, Rocquenqourt, B.P. 105, + * 78153 Le Chesnay Cedex France + */ + +#include +#include +#include + +/* This constant is not defined in limits.h, but IMath uses it */ +#define ULONG_MIN 0ul + +/* Test the IMath internals assumed by the imath implementation of isl_int. + * + * In particular, we test the ranges of IMath-defined types. + * + * Also, isl uses the existence and function of imath's struct + * fields. The digits are stored with less significant digits at lower array + * indices. Where they are stored (on the heap or in the field 'single') does + * not matter. + */ +int test_imath_internals() +{ + mpz_t val; + mp_result retval; + + assert(sizeof(mp_small) == sizeof(long)); + assert(MP_SMALL_MIN == LONG_MIN); + assert(MP_SMALL_MAX == LONG_MAX); + + assert(sizeof(mp_usmall) == sizeof(unsigned long)); + assert(MP_USMALL_MIN == ULONG_MIN); + assert(MP_USMALL_MAX == ULONG_MAX); + + retval = mp_int_init_value(&val, 0); + assert(retval == MP_OK); + assert(val.alloc >= val.used); + assert(val.used == 1); + assert(val.sign == MP_ZPOS); + assert(val.digits[0] == 0); + + retval = mp_int_set_value(&val, -1); + assert(retval == MP_OK); + assert(val.alloc >= val.used); + assert(val.used == 1); + assert(val.sign == MP_NEG); + assert(val.digits[0] == 1); + + retval = mp_int_set_value(&val, 1); + assert(retval == MP_OK); + assert(val.alloc >= val.used); + assert(val.used == 1); + assert(val.sign == MP_ZPOS); + assert(val.digits[0] == 1); + + retval = mp_int_mul_pow2(&val, sizeof(mp_digit) * CHAR_BIT, &val); + assert(retval == MP_OK); + assert(val.alloc >= val.used); + assert(val.used == 2); + assert(val.sign == MP_ZPOS); + assert(val.digits[0] == 0); + assert(val.digits[1] == 1); + + mp_int_clear(&val); + return 0; +} + +int main() +{ + if (test_imath_internals() < 0) + return -1; + + return 0; +} Index: lib/Analysis/isl/isl_test_int.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_test_int.c @@ -0,0 +1,626 @@ +/* + * Copyright 2015 INRIA Paris-Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Michael Kruse, INRIA Paris-Rocquencourt, + * Domaine de Voluceau, Rocquenqourt, B.P. 105, + * 78153 Le Chesnay Cedex France + */ + +#include +#include +#include + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array)) + +#ifdef USE_SMALL_INT_OPT +/* Test whether small and big representation of the same number have the same + * hash. + */ +static void int_test_hash(isl_int val) +{ + uint32_t demotedhash, promotedhash; + isl_int demoted, promoted; + + isl_int_init(demoted); + isl_int_set(demoted, val); + + isl_int_init(promoted); + isl_int_set(promoted, val); + + isl_sioimath_try_demote(demoted); + isl_sioimath_promote(promoted); + + assert(isl_int_eq(demoted, promoted)); + + demotedhash = isl_int_hash(demoted, 0); + promotedhash = isl_int_hash(promoted, 0); + assert(demotedhash == promotedhash); + + isl_int_clear(demoted); + isl_int_clear(promoted); +} + +struct { + void (*fn)(isl_int); + char *val; +} int_single_value_tests[] = { + { &int_test_hash, "0" }, + { &int_test_hash, "1" }, + { &int_test_hash, "-1" }, + { &int_test_hash, "23" }, + { &int_test_hash, "-23" }, + { &int_test_hash, "107" }, + { &int_test_hash, "32768" }, + { &int_test_hash, "2147483647" }, + { &int_test_hash, "-2147483647" }, + { &int_test_hash, "2147483648" }, + { &int_test_hash, "-2147483648" }, +}; + +static void int_test_single_value() +{ + int i; + + for (i = 0; i < ARRAY_SIZE(int_single_value_tests); i += 1) { + isl_int val; + + isl_int_init(val); + isl_int_read(val, int_single_value_tests[i].val); + + (*int_single_value_tests[i].fn)(val); + + isl_int_clear(val); + } +} + +static void invoke_alternate_representations_2args(char *arg1, char *arg2, + void (*fn)(isl_int, isl_int)) +{ + int j; + isl_int int1, int2; + + isl_int_init(int1); + isl_int_init(int2); + + for (j = 0; j < 4; ++j) { + isl_int_read(int1, arg1); + isl_int_read(int2, arg2); + + if (j & 1) + isl_sioimath_promote(int1); + else + isl_sioimath_try_demote(int1); + + if (j & 2) + isl_sioimath_promote(int2); + else + isl_sioimath_try_demote(int2); + + (*fn)(int1, int2); + } + + isl_int_clear(int1); + isl_int_clear(int2); +} + +static void invoke_alternate_representations_3args(char *arg1, char *arg2, + char *arg3, void (*fn)(isl_int, isl_int, isl_int)) +{ + int j; + isl_int int1, int2, int3; + + isl_int_init(int1); + isl_int_init(int2); + isl_int_init(int3); + + for (j = 0; j < 8; ++j) { + isl_int_read(int1, arg1); + isl_int_read(int2, arg2); + isl_int_read(int3, arg3); + + if (j & 1) + isl_sioimath_promote(int1); + else + isl_sioimath_try_demote(int1); + + if (j & 2) + isl_sioimath_promote(int2); + else + isl_sioimath_try_demote(int2); + + if (j & 4) + isl_sioimath_promote(int3); + else + isl_sioimath_try_demote(int3); + + (*fn)(int1, int2, int3); + } + + isl_int_clear(int1); + isl_int_clear(int2); + isl_int_clear(int3); +} +#else /* USE_SMALL_INT_OPT */ + +static void int_test_single_value() +{ +} + +static void invoke_alternate_representations_2args(char *arg1, char *arg2, + void (*fn)(isl_int, isl_int)) +{ + isl_int int1, int2; + + isl_int_init(int1); + isl_int_init(int2); + + isl_int_read(int1, arg1); + isl_int_read(int2, arg2); + + (*fn)(int1, int2); + + isl_int_clear(int1); + isl_int_clear(int2); +} + +static void invoke_alternate_representations_3args(char *arg1, char *arg2, + char *arg3, void (*fn)(isl_int, isl_int, isl_int)) +{ + isl_int int1, int2, int3; + + isl_int_init(int1); + isl_int_init(int2); + isl_int_init(int3); + + isl_int_read(int1, arg1); + isl_int_read(int2, arg2); + isl_int_read(int3, arg3); + + (*fn)(int1, int2, int3); + + isl_int_clear(int1); + isl_int_clear(int2); + isl_int_clear(int3); +} +#endif /* USE_SMALL_INT_OPT */ + +static void int_test_neg(isl_int expected, isl_int arg) +{ + isl_int result; + isl_int_init(result); + + isl_int_neg(result, arg); + assert(isl_int_eq(result, expected)); + + isl_int_neg(result, expected); + assert(isl_int_eq(result, arg)); + + isl_int_clear(result); +} + +static void int_test_abs(isl_int expected, isl_int arg) +{ + isl_int result; + isl_int_init(result); + + isl_int_abs(result, arg); + assert(isl_int_eq(result, expected)); + + isl_int_clear(result); +} + +struct { + void (*fn)(isl_int, isl_int); + char *expected, *arg; +} int_unary_tests[] = { + { &int_test_neg, "0", "0" }, + { &int_test_neg, "-1", "1" }, + { &int_test_neg, "-2147483647", "2147483647" }, + { &int_test_neg, "-2147483648", "2147483648" }, + { &int_test_neg, "-9223372036854775807", "9223372036854775807" }, + { &int_test_neg, "-9223372036854775808", "9223372036854775808" }, + + { &int_test_abs, "0", "0" }, + { &int_test_abs, "1", "1" }, + { &int_test_abs, "1", "-1" }, + { &int_test_abs, "2147483647", "2147483647" }, + { &int_test_abs, "2147483648", "-2147483648" }, + { &int_test_abs, "9223372036854775807", "9223372036854775807" }, + { &int_test_abs, "9223372036854775808", "-9223372036854775808" }, +}; + +static void int_test_divexact(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + unsigned long rhsulong; + + if (isl_int_sgn(rhs) == 0) + return; + + isl_int_init(result); + + isl_int_divexact(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_tdiv_q(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_fdiv_q(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_cdiv_q(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + if (isl_int_fits_ulong(rhs)) { + rhsulong = isl_int_get_ui(rhs); + + isl_int_divexact_ui(result, lhs, rhsulong); + assert(isl_int_eq(expected, result)); + + isl_int_fdiv_q_ui(result, lhs, rhsulong); + assert(isl_int_eq(expected, result)); + } + + isl_int_clear(result); +} + +static void int_test_mul(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_mul(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + if (isl_int_fits_ulong(rhs)) { + unsigned long rhsulong = isl_int_get_ui(rhs); + + isl_int_mul_ui(result, lhs, rhsulong); + assert(isl_int_eq(expected, result)); + } + + if (isl_int_fits_slong(rhs)) { + unsigned long rhsslong = isl_int_get_si(rhs); + + isl_int_mul_si(result, lhs, rhsslong); + assert(isl_int_eq(expected, result)); + } + + isl_int_clear(result); +} + +/* Use a triple that satisfies 'product = factor1 * factor2' to check the + * operations mul, divexact, tdiv, fdiv and cdiv. + */ +static void int_test_product(isl_int product, isl_int factor1, isl_int factor2) +{ + int_test_divexact(factor1, product, factor2); + int_test_divexact(factor2, product, factor1); + + int_test_mul(product, factor1, factor2); + int_test_mul(product, factor2, factor1); +} + +static void int_test_add(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_add(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +static void int_test_sub(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_sub(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +/* Use a triple that satisfies 'sum = term1 + term2' to check the operations add + * and sub. + */ +static void int_test_sum(isl_int sum, isl_int term1, isl_int term2) +{ + int_test_sub(term1, sum, term2); + int_test_sub(term2, sum, term1); + + int_test_add(sum, term1, term2); + int_test_add(sum, term2, term1); +} + +static void int_test_fdiv(isl_int expected, isl_int lhs, isl_int rhs) +{ + unsigned long rhsulong; + isl_int result; + isl_int_init(result); + + isl_int_fdiv_q(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + if (isl_int_fits_ulong(rhs)) { + rhsulong = isl_int_get_ui(rhs); + + isl_int_fdiv_q_ui(result, lhs, rhsulong); + assert(isl_int_eq(expected, result)); + } + + isl_int_clear(result); +} + +static void int_test_cdiv(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_cdiv_q(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +static void int_test_tdiv(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_tdiv_q(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +static void int_test_fdiv_r(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_fdiv_r(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +static void int_test_gcd(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_gcd(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_gcd(result, rhs, lhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +static void int_test_lcm(isl_int expected, isl_int lhs, isl_int rhs) +{ + isl_int result; + isl_int_init(result); + + isl_int_lcm(result, lhs, rhs); + assert(isl_int_eq(expected, result)); + + isl_int_lcm(result, rhs, lhs); + assert(isl_int_eq(expected, result)); + + isl_int_clear(result); +} + +static int sgn(int val) +{ + if (val > 0) + return 1; + if (val < 0) + return -1; + return 0; +} + +static void int_test_cmp(int exp, isl_int lhs, isl_int rhs) +{ + long rhslong; + + assert(exp == sgn(isl_int_cmp(lhs, rhs))); + + if (isl_int_fits_slong(rhs)) { + rhslong = isl_int_get_si(rhs); + assert(exp == sgn(isl_int_cmp_si(lhs, rhslong))); + } +} + +/* Test the comparison relations over two numbers. + * expected is the sign (1, 0 or -1) of 'lhs - rhs'. + */ +static void int_test_cmps(isl_int expected, isl_int lhs, isl_int rhs) +{ + int exp; + isl_int diff; + + exp = isl_int_get_si(expected); + + isl_int_init(diff); + isl_int_sub(diff, lhs, rhs); + assert(exp == isl_int_sgn(diff)); + isl_int_clear(diff); + + int_test_cmp(exp, lhs, rhs); + int_test_cmp(-exp, rhs, lhs); +} + +static void int_test_abs_cmp(isl_int expected, isl_int lhs, isl_int rhs) +{ + int exp; + + exp = isl_int_get_si(expected); + assert(exp == sgn(isl_int_abs_cmp(lhs, rhs))); + assert(-exp == sgn(isl_int_abs_cmp(rhs, lhs))); +} + +struct { + void (*fn)(isl_int, isl_int, isl_int); + char *expected, *lhs, *rhs; +} int_binary_tests[] = { + { &int_test_sum, "0", "0", "0" }, + { &int_test_sum, "1", "1", "0" }, + { &int_test_sum, "2", "1", "1" }, + { &int_test_sum, "-1", "0", "-1" }, + { &int_test_sum, "-2", "-1", "-1" }, + + { &int_test_sum, "2147483647", "1073741823", "1073741824" }, + { &int_test_sum, "-2147483648", "-1073741824", "-1073741824" }, + + { &int_test_sum, "2147483648", "2147483647", "1" }, + { &int_test_sum, "-2147483648", "-2147483647", "-1" }, + + { &int_test_product, "0", "0", "0" }, + { &int_test_product, "0", "0", "1" }, + { &int_test_product, "1", "1", "1" }, + + { &int_test_product, "6", "2", "3" }, + { &int_test_product, "-6", "2", "-3" }, + { &int_test_product, "-6", "-2", "3" }, + { &int_test_product, "6", "-2", "-3" }, + + { &int_test_product, "2147483648", "65536", "32768" }, + { &int_test_product, "-2147483648", "65536", "-32768" }, + + { &int_test_product, + "4611686014132420609", "2147483647", "2147483647" }, + { &int_test_product, + "-4611686014132420609", "-2147483647", "2147483647" }, + + { &int_test_product, + "4611686016279904256", "2147483647", "2147483648" }, + { &int_test_product, + "-4611686016279904256", "-2147483647", "2147483648" }, + { &int_test_product, + "-4611686016279904256", "2147483647", "-2147483648" }, + { &int_test_product, + "4611686016279904256", "-2147483647", "-2147483648" }, + + { &int_test_product, "85070591730234615847396907784232501249", + "9223372036854775807", "9223372036854775807" }, + { &int_test_product, "-85070591730234615847396907784232501249", + "-9223372036854775807", "9223372036854775807" }, + + { &int_test_product, "85070591730234615856620279821087277056", + "9223372036854775807", "9223372036854775808" }, + { &int_test_product, "-85070591730234615856620279821087277056", + "-9223372036854775807", "9223372036854775808" }, + { &int_test_product, "-85070591730234615856620279821087277056", + "9223372036854775807", "-9223372036854775808" }, + { &int_test_product, "85070591730234615856620279821087277056", + "-9223372036854775807", "-9223372036854775808" }, + + { &int_test_product, "340282366920938463426481119284349108225", + "18446744073709551615", "18446744073709551615" }, + { &int_test_product, "-340282366920938463426481119284349108225", + "-18446744073709551615", "18446744073709551615" }, + + { &int_test_product, "340282366920938463444927863358058659840", + "18446744073709551615", "18446744073709551616" }, + { &int_test_product, "-340282366920938463444927863358058659840", + "-18446744073709551615", "18446744073709551616" }, + { &int_test_product, "-340282366920938463444927863358058659840", + "18446744073709551615", "-18446744073709551616" }, + { &int_test_product, "340282366920938463444927863358058659840", + "-18446744073709551615", "-18446744073709551616" }, + + { &int_test_fdiv, "0", "1", "2" }, + { &int_test_fdiv_r, "1", "1", "3" }, + { &int_test_fdiv, "-1", "-1", "2" }, + { &int_test_fdiv_r, "2", "-1", "3" }, + { &int_test_fdiv, "-1", "1", "-2" }, + { &int_test_fdiv_r, "-2", "1", "-3" }, + { &int_test_fdiv, "0", "-1", "-2" }, + { &int_test_fdiv_r, "-1", "-1", "-3" }, + + { &int_test_cdiv, "1", "1", "2" }, + { &int_test_cdiv, "0", "-1", "2" }, + { &int_test_cdiv, "0", "1", "-2" }, + { &int_test_cdiv, "1", "-1", "-2" }, + + { &int_test_tdiv, "0", "1", "2" }, + { &int_test_tdiv, "0", "-1", "2" }, + { &int_test_tdiv, "0", "1", "-2" }, + { &int_test_tdiv, "0", "-1", "-2" }, + + { &int_test_gcd, "0", "0", "0" }, + { &int_test_lcm, "0", "0", "0" }, + { &int_test_gcd, "7", "0", "7" }, + { &int_test_lcm, "0", "0", "7" }, + { &int_test_gcd, "1", "1", "1" }, + { &int_test_lcm, "1", "1", "1" }, + { &int_test_gcd, "1", "1", "-1" }, + { &int_test_lcm, "1", "1", "-1" }, + { &int_test_gcd, "1", "-1", "-1" }, + { &int_test_lcm, "1", "-1", "-1" }, + { &int_test_gcd, "3", "6", "9" }, + { &int_test_lcm, "18", "6", "9" }, + { &int_test_gcd, "1", "14", "2147483647" }, + { &int_test_lcm, "15032385529", "7", "2147483647" }, + { &int_test_gcd, "2", "6", "-2147483648" }, + { &int_test_lcm, "6442450944", "6", "-2147483648" }, + { &int_test_gcd, "1", "6", "9223372036854775807" }, + { &int_test_lcm, "55340232221128654842", "6", "9223372036854775807" }, + { &int_test_gcd, "2", "6", "-9223372036854775808" }, + { &int_test_lcm, "27670116110564327424", "6", "-9223372036854775808" }, + { &int_test_gcd, "1", "18446744073709551616", "18446744073709551615" }, + { &int_test_lcm, "340282366920938463444927863358058659840", + "18446744073709551616", "18446744073709551615" }, + + { &int_test_cmps, "0", "0", "0" }, + { &int_test_abs_cmp, "0", "0", "0" }, + { &int_test_cmps, "1", "1", "0" }, + { &int_test_abs_cmp, "1", "1", "0" }, + { &int_test_cmps, "-1", "-1", "0" }, + { &int_test_abs_cmp, "1", "-1", "0" }, + { &int_test_cmps, "-1", "-1", "1" }, + { &int_test_abs_cmp, "0", "-1", "1" }, + + { &int_test_cmps, "-1", "5", "2147483647" }, + { &int_test_abs_cmp, "-1", "5", "2147483647" }, + { &int_test_cmps, "1", "5", "-2147483648" }, + { &int_test_abs_cmp, "-1", "5", "-2147483648" }, + { &int_test_cmps, "-1", "5", "9223372036854775807" }, + { &int_test_abs_cmp, "-1", "5", "9223372036854775807" }, + { &int_test_cmps, "1", "5", "-9223372036854775809" }, + { &int_test_abs_cmp, "-1", "5", "-9223372036854775809" }, +}; + +/* Tests the isl_int_* function to give the expected results. Tests are + * grouped by the number of arguments they take. + * + * If small integer optimization is enabled, we also test whether the results + * are the same in small and big representation. + */ +int main() +{ + int i; + + int_test_single_value(); + + for (i = 0; i < ARRAY_SIZE(int_unary_tests); i += 1) { + invoke_alternate_representations_2args( + int_unary_tests[i].expected, int_unary_tests[i].arg, + int_unary_tests[i].fn); + } + + for (i = 0; i < ARRAY_SIZE(int_binary_tests); i += 1) { + invoke_alternate_representations_3args( + int_binary_tests[i].expected, int_binary_tests[i].lhs, + int_binary_tests[i].rhs, int_binary_tests[i].fn); + } + + return 0; +} Index: lib/Analysis/isl/isl_transitive_closure.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_transitive_closure.c @@ -0,0 +1,2994 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int isl_map_is_transitively_closed(__isl_keep isl_map *map) +{ + isl_map *map2; + int closed; + + map2 = isl_map_apply_range(isl_map_copy(map), isl_map_copy(map)); + closed = isl_map_is_subset(map2, map); + isl_map_free(map2); + + return closed; +} + +int isl_union_map_is_transitively_closed(__isl_keep isl_union_map *umap) +{ + isl_union_map *umap2; + int closed; + + umap2 = isl_union_map_apply_range(isl_union_map_copy(umap), + isl_union_map_copy(umap)); + closed = isl_union_map_is_subset(umap2, umap); + isl_union_map_free(umap2); + + return closed; +} + +/* Given a map that represents a path with the length of the path + * encoded as the difference between the last output coordindate + * and the last input coordinate, set this length to either + * exactly "length" (if "exactly" is set) or at least "length" + * (if "exactly" is not set). + */ +static __isl_give isl_map *set_path_length(__isl_take isl_map *map, + int exactly, int length) +{ + isl_space *dim; + struct isl_basic_map *bmap; + unsigned d; + unsigned nparam; + int k; + isl_int *c; + + if (!map) + return NULL; + + dim = isl_map_get_space(map); + d = isl_space_dim(dim, isl_dim_in); + nparam = isl_space_dim(dim, isl_dim_param); + bmap = isl_basic_map_alloc_space(dim, 0, 1, 1); + if (exactly) { + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + c = bmap->eq[k]; + } else { + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + c = bmap->ineq[k]; + } + isl_seq_clr(c, 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(c[0], -length); + isl_int_set_si(c[1 + nparam + d - 1], -1); + isl_int_set_si(c[1 + nparam + d + d - 1], 1); + + bmap = isl_basic_map_finalize(bmap); + map = isl_map_intersect(map, isl_map_from_basic_map(bmap)); + + return map; +error: + isl_basic_map_free(bmap); + isl_map_free(map); + return NULL; +} + +/* Check whether the overapproximation of the power of "map" is exactly + * the power of "map". Let R be "map" and A_k the overapproximation. + * The approximation is exact if + * + * A_1 = R + * A_k = A_{k-1} \circ R k >= 2 + * + * Since A_k is known to be an overapproximation, we only need to check + * + * A_1 \subset R + * A_k \subset A_{k-1} \circ R k >= 2 + * + * In practice, "app" has an extra input and output coordinate + * to encode the length of the path. So, we first need to add + * this coordinate to "map" and set the length of the path to + * one. + */ +static int check_power_exactness(__isl_take isl_map *map, + __isl_take isl_map *app) +{ + int exact; + isl_map *app_1; + isl_map *app_2; + + map = isl_map_add_dims(map, isl_dim_in, 1); + map = isl_map_add_dims(map, isl_dim_out, 1); + map = set_path_length(map, 1, 1); + + app_1 = set_path_length(isl_map_copy(app), 1, 1); + + exact = isl_map_is_subset(app_1, map); + isl_map_free(app_1); + + if (!exact || exact < 0) { + isl_map_free(app); + isl_map_free(map); + return exact; + } + + app_1 = set_path_length(isl_map_copy(app), 0, 1); + app_2 = set_path_length(app, 0, 2); + app_1 = isl_map_apply_range(map, app_1); + + exact = isl_map_is_subset(app_2, app_1); + + isl_map_free(app_1); + isl_map_free(app_2); + + return exact; +} + +/* Check whether the overapproximation of the power of "map" is exactly + * the power of "map", possibly after projecting out the power (if "project" + * is set). + * + * If "project" is set and if "steps" can only result in acyclic paths, + * then we check + * + * A = R \cup (A \circ R) + * + * where A is the overapproximation with the power projected out, i.e., + * an overapproximation of the transitive closure. + * More specifically, since A is known to be an overapproximation, we check + * + * A \subset R \cup (A \circ R) + * + * Otherwise, we check if the power is exact. + * + * Note that "app" has an extra input and output coordinate to encode + * the length of the part. If we are only interested in the transitive + * closure, then we can simply project out these coordinates first. + */ +static int check_exactness(__isl_take isl_map *map, __isl_take isl_map *app, + int project) +{ + isl_map *test; + int exact; + unsigned d; + + if (!project) + return check_power_exactness(map, app); + + d = isl_map_dim(map, isl_dim_in); + app = set_path_length(app, 0, 1); + app = isl_map_project_out(app, isl_dim_in, d, 1); + app = isl_map_project_out(app, isl_dim_out, d, 1); + + app = isl_map_reset_space(app, isl_map_get_space(map)); + + test = isl_map_apply_range(isl_map_copy(map), isl_map_copy(app)); + test = isl_map_union(test, isl_map_copy(map)); + + exact = isl_map_is_subset(app, test); + + isl_map_free(app); + isl_map_free(test); + + isl_map_free(map); + + return exact; +} + +/* + * The transitive closure implementation is based on the paper + * "Computing the Transitive Closure of a Union of Affine Integer + * Tuple Relations" by Anna Beletska, Denis Barthou, Wlodzimierz Bielecki and + * Albert Cohen. + */ + +/* Given a set of n offsets v_i (the rows of "steps"), construct a relation + * of the given dimension specification (Z^{n+1} -> Z^{n+1}) + * that maps an element x to any element that can be reached + * by taking a non-negative number of steps along any of + * the extended offsets v'_i = [v_i 1]. + * That is, construct + * + * { [x] -> [y] : exists k_i >= 0, y = x + \sum_i k_i v'_i } + * + * For any element in this relation, the number of steps taken + * is equal to the difference in the final coordinates. + */ +static __isl_give isl_map *path_along_steps(__isl_take isl_space *dim, + __isl_keep isl_mat *steps) +{ + int i, j, k; + struct isl_basic_map *path = NULL; + unsigned d; + unsigned n; + unsigned nparam; + + if (!dim || !steps) + goto error; + + d = isl_space_dim(dim, isl_dim_in); + n = steps->n_row; + nparam = isl_space_dim(dim, isl_dim_param); + + path = isl_basic_map_alloc_space(isl_space_copy(dim), n, d, n); + + for (i = 0; i < n; ++i) { + k = isl_basic_map_alloc_div(path); + if (k < 0) + goto error; + isl_assert(steps->ctx, i == k, goto error); + isl_int_set_si(path->div[k][0], 0); + } + + for (i = 0; i < d; ++i) { + k = isl_basic_map_alloc_equality(path); + if (k < 0) + goto error; + isl_seq_clr(path->eq[k], 1 + isl_basic_map_total_dim(path)); + isl_int_set_si(path->eq[k][1 + nparam + i], 1); + isl_int_set_si(path->eq[k][1 + nparam + d + i], -1); + if (i == d - 1) + for (j = 0; j < n; ++j) + isl_int_set_si(path->eq[k][1 + nparam + 2 * d + j], 1); + else + for (j = 0; j < n; ++j) + isl_int_set(path->eq[k][1 + nparam + 2 * d + j], + steps->row[j][i]); + } + + for (i = 0; i < n; ++i) { + k = isl_basic_map_alloc_inequality(path); + if (k < 0) + goto error; + isl_seq_clr(path->ineq[k], 1 + isl_basic_map_total_dim(path)); + isl_int_set_si(path->ineq[k][1 + nparam + 2 * d + i], 1); + } + + isl_space_free(dim); + + path = isl_basic_map_simplify(path); + path = isl_basic_map_finalize(path); + return isl_map_from_basic_map(path); +error: + isl_space_free(dim); + isl_basic_map_free(path); + return NULL; +} + +#define IMPURE 0 +#define PURE_PARAM 1 +#define PURE_VAR 2 +#define MIXED 3 + +/* Check whether the parametric constant term of constraint c is never + * positive in "bset". + */ +static int parametric_constant_never_positive(__isl_keep isl_basic_set *bset, + isl_int *c, int *div_purity) +{ + unsigned d; + unsigned n_div; + unsigned nparam; + int i; + int k; + int empty; + + n_div = isl_basic_set_dim(bset, isl_dim_div); + d = isl_basic_set_dim(bset, isl_dim_set); + nparam = isl_basic_set_dim(bset, isl_dim_param); + + bset = isl_basic_set_copy(bset); + bset = isl_basic_set_cow(bset); + bset = isl_basic_set_extend_constraints(bset, 0, 1); + k = isl_basic_set_alloc_inequality(bset); + if (k < 0) + goto error; + isl_seq_clr(bset->ineq[k], 1 + isl_basic_set_total_dim(bset)); + isl_seq_cpy(bset->ineq[k], c, 1 + nparam); + for (i = 0; i < n_div; ++i) { + if (div_purity[i] != PURE_PARAM) + continue; + isl_int_set(bset->ineq[k][1 + nparam + d + i], + c[1 + nparam + d + i]); + } + isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1); + empty = isl_basic_set_is_empty(bset); + isl_basic_set_free(bset); + + return empty; +error: + isl_basic_set_free(bset); + return -1; +} + +/* Return PURE_PARAM if only the coefficients of the parameters are non-zero. + * Return PURE_VAR if only the coefficients of the set variables are non-zero. + * Return MIXED if only the coefficients of the parameters and the set + * variables are non-zero and if moreover the parametric constant + * can never attain positive values. + * Return IMPURE otherwise. + */ +static int purity(__isl_keep isl_basic_set *bset, isl_int *c, int *div_purity, + int eq) +{ + unsigned d; + unsigned n_div; + unsigned nparam; + int empty; + int i; + int p = 0, v = 0; + + n_div = isl_basic_set_dim(bset, isl_dim_div); + d = isl_basic_set_dim(bset, isl_dim_set); + nparam = isl_basic_set_dim(bset, isl_dim_param); + + for (i = 0; i < n_div; ++i) { + if (isl_int_is_zero(c[1 + nparam + d + i])) + continue; + switch (div_purity[i]) { + case PURE_PARAM: p = 1; break; + case PURE_VAR: v = 1; break; + default: return IMPURE; + } + } + if (!p && isl_seq_first_non_zero(c + 1, nparam) == -1) + return PURE_VAR; + if (!v && isl_seq_first_non_zero(c + 1 + nparam, d) == -1) + return PURE_PARAM; + + empty = parametric_constant_never_positive(bset, c, div_purity); + if (eq && empty >= 0 && !empty) { + isl_seq_neg(c, c, 1 + nparam + d + n_div); + empty = parametric_constant_never_positive(bset, c, div_purity); + } + + return empty < 0 ? -1 : empty ? MIXED : IMPURE; +} + +/* Return an array of integers indicating the type of each div in bset. + * If the div is (recursively) defined in terms of only the parameters, + * then the type is PURE_PARAM. + * If the div is (recursively) defined in terms of only the set variables, + * then the type is PURE_VAR. + * Otherwise, the type is IMPURE. + */ +static __isl_give int *get_div_purity(__isl_keep isl_basic_set *bset) +{ + int i, j; + int *div_purity; + unsigned d; + unsigned n_div; + unsigned nparam; + + if (!bset) + return NULL; + + n_div = isl_basic_set_dim(bset, isl_dim_div); + d = isl_basic_set_dim(bset, isl_dim_set); + nparam = isl_basic_set_dim(bset, isl_dim_param); + + div_purity = isl_alloc_array(bset->ctx, int, n_div); + if (n_div && !div_purity) + return NULL; + + for (i = 0; i < bset->n_div; ++i) { + int p = 0, v = 0; + if (isl_int_is_zero(bset->div[i][0])) { + div_purity[i] = IMPURE; + continue; + } + if (isl_seq_first_non_zero(bset->div[i] + 2, nparam) != -1) + p = 1; + if (isl_seq_first_non_zero(bset->div[i] + 2 + nparam, d) != -1) + v = 1; + for (j = 0; j < i; ++j) { + if (isl_int_is_zero(bset->div[i][2 + nparam + d + j])) + continue; + switch (div_purity[j]) { + case PURE_PARAM: p = 1; break; + case PURE_VAR: v = 1; break; + default: p = v = 1; break; + } + } + div_purity[i] = v ? p ? IMPURE : PURE_VAR : PURE_PARAM; + } + + return div_purity; +} + +/* Given a path with the as yet unconstrained length at position "pos", + * check if setting the length to zero results in only the identity + * mapping. + */ +static int empty_path_is_identity(__isl_keep isl_basic_map *path, unsigned pos) +{ + isl_basic_map *test = NULL; + isl_basic_map *id = NULL; + int k; + int is_id; + + test = isl_basic_map_copy(path); + test = isl_basic_map_extend_constraints(test, 1, 0); + k = isl_basic_map_alloc_equality(test); + if (k < 0) + goto error; + isl_seq_clr(test->eq[k], 1 + isl_basic_map_total_dim(test)); + isl_int_set_si(test->eq[k][pos], 1); + test = isl_basic_map_gauss(test, NULL); + id = isl_basic_map_identity(isl_basic_map_get_space(path)); + is_id = isl_basic_map_is_equal(test, id); + isl_basic_map_free(test); + isl_basic_map_free(id); + return is_id; +error: + isl_basic_map_free(test); + return -1; +} + +/* If any of the constraints is found to be impure then this function + * sets *impurity to 1. + * + * If impurity is NULL then we are dealing with a non-parametric set + * and so the constraints are obviously PURE_VAR. + */ +static __isl_give isl_basic_map *add_delta_constraints( + __isl_take isl_basic_map *path, + __isl_keep isl_basic_set *delta, unsigned off, unsigned nparam, + unsigned d, int *div_purity, int eq, int *impurity) +{ + int i, k; + int n = eq ? delta->n_eq : delta->n_ineq; + isl_int **delta_c = eq ? delta->eq : delta->ineq; + unsigned n_div; + + n_div = isl_basic_set_dim(delta, isl_dim_div); + + for (i = 0; i < n; ++i) { + isl_int *path_c; + int p = PURE_VAR; + if (impurity) + p = purity(delta, delta_c[i], div_purity, eq); + if (p < 0) + goto error; + if (p != PURE_VAR && p != PURE_PARAM && !*impurity) + *impurity = 1; + if (p == IMPURE) + continue; + if (eq && p != MIXED) { + k = isl_basic_map_alloc_equality(path); + if (k < 0) + goto error; + path_c = path->eq[k]; + } else { + k = isl_basic_map_alloc_inequality(path); + if (k < 0) + goto error; + path_c = path->ineq[k]; + } + isl_seq_clr(path_c, 1 + isl_basic_map_total_dim(path)); + if (p == PURE_VAR) { + isl_seq_cpy(path_c + off, + delta_c[i] + 1 + nparam, d); + isl_int_set(path_c[off + d], delta_c[i][0]); + } else if (p == PURE_PARAM) { + isl_seq_cpy(path_c, delta_c[i], 1 + nparam); + } else { + isl_seq_cpy(path_c + off, + delta_c[i] + 1 + nparam, d); + isl_seq_cpy(path_c, delta_c[i], 1 + nparam); + } + isl_seq_cpy(path_c + off - n_div, + delta_c[i] + 1 + nparam + d, n_div); + } + + return path; +error: + isl_basic_map_free(path); + return NULL; +} + +/* Given a set of offsets "delta", construct a relation of the + * given dimension specification (Z^{n+1} -> Z^{n+1}) that + * is an overapproximation of the relations that + * maps an element x to any element that can be reached + * by taking a non-negative number of steps along any of + * the elements in "delta". + * That is, construct an approximation of + * + * { [x] -> [y] : exists f \in \delta, k \in Z : + * y = x + k [f, 1] and k >= 0 } + * + * For any element in this relation, the number of steps taken + * is equal to the difference in the final coordinates. + * + * In particular, let delta be defined as + * + * \delta = [p] -> { [x] : A x + a >= 0 and B p + b >= 0 and + * C x + C'p + c >= 0 and + * D x + D'p + d >= 0 } + * + * where the constraints C x + C'p + c >= 0 are such that the parametric + * constant term of each constraint j, "C_j x + C'_j p + c_j", + * can never attain positive values, then the relation is constructed as + * + * { [x] -> [y] : exists [f, k] \in Z^{n+1} : y = x + f and + * A f + k a >= 0 and B p + b >= 0 and + * C f + C'p + c >= 0 and k >= 1 } + * union { [x] -> [x] } + * + * If the zero-length paths happen to correspond exactly to the identity + * mapping, then we return + * + * { [x] -> [y] : exists [f, k] \in Z^{n+1} : y = x + f and + * A f + k a >= 0 and B p + b >= 0 and + * C f + C'p + c >= 0 and k >= 0 } + * + * instead. + * + * Existentially quantified variables in \delta are handled by + * classifying them as independent of the parameters, purely + * parameter dependent and others. Constraints containing + * any of the other existentially quantified variables are removed. + * This is safe, but leads to an additional overapproximation. + * + * If there are any impure constraints, then we also eliminate + * the parameters from \delta, resulting in a set + * + * \delta' = { [x] : E x + e >= 0 } + * + * and add the constraints + * + * E f + k e >= 0 + * + * to the constructed relation. + */ +static __isl_give isl_map *path_along_delta(__isl_take isl_space *dim, + __isl_take isl_basic_set *delta) +{ + isl_basic_map *path = NULL; + unsigned d; + unsigned n_div; + unsigned nparam; + unsigned off; + int i, k; + int is_id; + int *div_purity = NULL; + int impurity = 0; + + if (!delta) + goto error; + n_div = isl_basic_set_dim(delta, isl_dim_div); + d = isl_basic_set_dim(delta, isl_dim_set); + nparam = isl_basic_set_dim(delta, isl_dim_param); + path = isl_basic_map_alloc_space(isl_space_copy(dim), n_div + d + 1, + d + 1 + delta->n_eq, delta->n_eq + delta->n_ineq + 1); + off = 1 + nparam + 2 * (d + 1) + n_div; + + for (i = 0; i < n_div + d + 1; ++i) { + k = isl_basic_map_alloc_div(path); + if (k < 0) + goto error; + isl_int_set_si(path->div[k][0], 0); + } + + for (i = 0; i < d + 1; ++i) { + k = isl_basic_map_alloc_equality(path); + if (k < 0) + goto error; + isl_seq_clr(path->eq[k], 1 + isl_basic_map_total_dim(path)); + isl_int_set_si(path->eq[k][1 + nparam + i], 1); + isl_int_set_si(path->eq[k][1 + nparam + d + 1 + i], -1); + isl_int_set_si(path->eq[k][off + i], 1); + } + + div_purity = get_div_purity(delta); + if (n_div && !div_purity) + goto error; + + path = add_delta_constraints(path, delta, off, nparam, d, + div_purity, 1, &impurity); + path = add_delta_constraints(path, delta, off, nparam, d, + div_purity, 0, &impurity); + if (impurity) { + isl_space *dim = isl_basic_set_get_space(delta); + delta = isl_basic_set_project_out(delta, + isl_dim_param, 0, nparam); + delta = isl_basic_set_add_dims(delta, isl_dim_param, nparam); + delta = isl_basic_set_reset_space(delta, dim); + if (!delta) + goto error; + path = isl_basic_map_extend_constraints(path, delta->n_eq, + delta->n_ineq + 1); + path = add_delta_constraints(path, delta, off, nparam, d, + NULL, 1, NULL); + path = add_delta_constraints(path, delta, off, nparam, d, + NULL, 0, NULL); + path = isl_basic_map_gauss(path, NULL); + } + + is_id = empty_path_is_identity(path, off + d); + if (is_id < 0) + goto error; + + k = isl_basic_map_alloc_inequality(path); + if (k < 0) + goto error; + isl_seq_clr(path->ineq[k], 1 + isl_basic_map_total_dim(path)); + if (!is_id) + isl_int_set_si(path->ineq[k][0], -1); + isl_int_set_si(path->ineq[k][off + d], 1); + + free(div_purity); + isl_basic_set_free(delta); + path = isl_basic_map_finalize(path); + if (is_id) { + isl_space_free(dim); + return isl_map_from_basic_map(path); + } + return isl_basic_map_union(path, isl_basic_map_identity(dim)); +error: + free(div_purity); + isl_space_free(dim); + isl_basic_set_free(delta); + isl_basic_map_free(path); + return NULL; +} + +/* Given a dimension specification Z^{n+1} -> Z^{n+1} and a parameter "param", + * construct a map that equates the parameter to the difference + * in the final coordinates and imposes that this difference is positive. + * That is, construct + * + * { [x,x_s] -> [y,y_s] : k = y_s - x_s > 0 } + */ +static __isl_give isl_map *equate_parameter_to_length(__isl_take isl_space *dim, + unsigned param) +{ + struct isl_basic_map *bmap; + unsigned d; + unsigned nparam; + int k; + + d = isl_space_dim(dim, isl_dim_in); + nparam = isl_space_dim(dim, isl_dim_param); + bmap = isl_basic_map_alloc_space(dim, 0, 1, 1); + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->eq[k], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[k][1 + param], -1); + isl_int_set_si(bmap->eq[k][1 + nparam + d - 1], -1); + isl_int_set_si(bmap->eq[k][1 + nparam + d + d - 1], 1); + + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->ineq[k], 1 + isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->ineq[k][1 + param], 1); + isl_int_set_si(bmap->ineq[k][0], -1); + + bmap = isl_basic_map_finalize(bmap); + return isl_map_from_basic_map(bmap); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Check whether "path" is acyclic, where the last coordinates of domain + * and range of path encode the number of steps taken. + * That is, check whether + * + * { d | d = y - x and (x,y) in path } + * + * does not contain any element with positive last coordinate (positive length) + * and zero remaining coordinates (cycle). + */ +static int is_acyclic(__isl_take isl_map *path) +{ + int i; + int acyclic; + unsigned dim; + struct isl_set *delta; + + delta = isl_map_deltas(path); + dim = isl_set_dim(delta, isl_dim_set); + for (i = 0; i < dim; ++i) { + if (i == dim -1) + delta = isl_set_lower_bound_si(delta, isl_dim_set, i, 1); + else + delta = isl_set_fix_si(delta, isl_dim_set, i, 0); + } + + acyclic = isl_set_is_empty(delta); + isl_set_free(delta); + + return acyclic; +} + +/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D + * and a dimension specification (Z^{n+1} -> Z^{n+1}), + * construct a map that is an overapproximation of the map + * that takes an element from the space D \times Z to another + * element from the same space, such that the first n coordinates of the + * difference between them is a sum of differences between images + * and pre-images in one of the R_i and such that the last coordinate + * is equal to the number of steps taken. + * That is, let + * + * \Delta_i = { y - x | (x, y) in R_i } + * + * then the constructed map is an overapproximation of + * + * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i : + * d = (\sum_i k_i \delta_i, \sum_i k_i) } + * + * The elements of the singleton \Delta_i's are collected as the + * rows of the steps matrix. For all these \Delta_i's together, + * a single path is constructed. + * For each of the other \Delta_i's, we compute an overapproximation + * of the paths along elements of \Delta_i. + * Since each of these paths performs an addition, composition is + * symmetric and we can simply compose all resulting paths in any order. + */ +static __isl_give isl_map *construct_extended_path(__isl_take isl_space *dim, + __isl_keep isl_map *map, int *project) +{ + struct isl_mat *steps = NULL; + struct isl_map *path = NULL; + unsigned d; + int i, j, n; + + if (!map) + goto error; + + d = isl_map_dim(map, isl_dim_in); + + path = isl_map_identity(isl_space_copy(dim)); + + steps = isl_mat_alloc(map->ctx, map->n, d); + if (!steps) + goto error; + + n = 0; + for (i = 0; i < map->n; ++i) { + struct isl_basic_set *delta; + + delta = isl_basic_map_deltas(isl_basic_map_copy(map->p[i])); + + for (j = 0; j < d; ++j) { + int fixed; + + fixed = isl_basic_set_plain_dim_is_fixed(delta, j, + &steps->row[n][j]); + if (fixed < 0) { + isl_basic_set_free(delta); + goto error; + } + if (!fixed) + break; + } + + + if (j < d) { + path = isl_map_apply_range(path, + path_along_delta(isl_space_copy(dim), delta)); + path = isl_map_coalesce(path); + } else { + isl_basic_set_free(delta); + ++n; + } + } + + if (n > 0) { + steps->n_row = n; + path = isl_map_apply_range(path, + path_along_steps(isl_space_copy(dim), steps)); + } + + if (project && *project) { + *project = is_acyclic(isl_map_copy(path)); + if (*project < 0) + goto error; + } + + isl_space_free(dim); + isl_mat_free(steps); + return path; +error: + isl_space_free(dim); + isl_mat_free(steps); + isl_map_free(path); + return NULL; +} + +static int isl_set_overlaps(__isl_keep isl_set *set1, __isl_keep isl_set *set2) +{ + isl_set *i; + int no_overlap; + + if (!set1 || !set2) + return -1; + + if (!isl_space_tuple_is_equal(set1->dim, isl_dim_set, + set2->dim, isl_dim_set)) + return 0; + + i = isl_set_intersect(isl_set_copy(set1), isl_set_copy(set2)); + no_overlap = isl_set_is_empty(i); + isl_set_free(i); + + return no_overlap < 0 ? -1 : !no_overlap; +} + +/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D + * and a dimension specification (Z^{n+1} -> Z^{n+1}), + * construct a map that is an overapproximation of the map + * that takes an element from the dom R \times Z to an + * element from ran R \times Z, such that the first n coordinates of the + * difference between them is a sum of differences between images + * and pre-images in one of the R_i and such that the last coordinate + * is equal to the number of steps taken. + * That is, let + * + * \Delta_i = { y - x | (x, y) in R_i } + * + * then the constructed map is an overapproximation of + * + * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i : + * d = (\sum_i k_i \delta_i, \sum_i k_i) and + * x in dom R and x + d in ran R and + * \sum_i k_i >= 1 } + */ +static __isl_give isl_map *construct_component(__isl_take isl_space *dim, + __isl_keep isl_map *map, int *exact, int project) +{ + struct isl_set *domain = NULL; + struct isl_set *range = NULL; + struct isl_map *app = NULL; + struct isl_map *path = NULL; + int overlaps; + + domain = isl_map_domain(isl_map_copy(map)); + domain = isl_set_coalesce(domain); + range = isl_map_range(isl_map_copy(map)); + range = isl_set_coalesce(range); + overlaps = isl_set_overlaps(domain, range); + if (overlaps < 0 || !overlaps) { + isl_set_free(domain); + isl_set_free(range); + isl_space_free(dim); + + if (overlaps < 0) + map = NULL; + map = isl_map_copy(map); + map = isl_map_add_dims(map, isl_dim_in, 1); + map = isl_map_add_dims(map, isl_dim_out, 1); + map = set_path_length(map, 1, 1); + return map; + } + app = isl_map_from_domain_and_range(domain, range); + app = isl_map_add_dims(app, isl_dim_in, 1); + app = isl_map_add_dims(app, isl_dim_out, 1); + + path = construct_extended_path(isl_space_copy(dim), map, + exact && *exact ? &project : NULL); + app = isl_map_intersect(app, path); + + if (exact && *exact && + (*exact = check_exactness(isl_map_copy(map), isl_map_copy(app), + project)) < 0) + goto error; + + isl_space_free(dim); + app = set_path_length(app, 0, 1); + return app; +error: + isl_space_free(dim); + isl_map_free(app); + return NULL; +} + +/* Call construct_component and, if "project" is set, project out + * the final coordinates. + */ +static __isl_give isl_map *construct_projected_component( + __isl_take isl_space *dim, + __isl_keep isl_map *map, int *exact, int project) +{ + isl_map *app; + unsigned d; + + if (!dim) + return NULL; + d = isl_space_dim(dim, isl_dim_in); + + app = construct_component(dim, map, exact, project); + if (project) { + app = isl_map_project_out(app, isl_dim_in, d - 1, 1); + app = isl_map_project_out(app, isl_dim_out, d - 1, 1); + } + return app; +} + +/* Compute an extended version, i.e., with path lengths, of + * an overapproximation of the transitive closure of "bmap" + * with path lengths greater than or equal to zero and with + * domain and range equal to "dom". + */ +static __isl_give isl_map *q_closure(__isl_take isl_space *dim, + __isl_take isl_set *dom, __isl_keep isl_basic_map *bmap, int *exact) +{ + int project = 1; + isl_map *path; + isl_map *map; + isl_map *app; + + dom = isl_set_add_dims(dom, isl_dim_set, 1); + app = isl_map_from_domain_and_range(dom, isl_set_copy(dom)); + map = isl_map_from_basic_map(isl_basic_map_copy(bmap)); + path = construct_extended_path(dim, map, &project); + app = isl_map_intersect(app, path); + + if ((*exact = check_exactness(map, isl_map_copy(app), project)) < 0) + goto error; + + return app; +error: + isl_map_free(app); + return NULL; +} + +/* Check whether qc has any elements of length at least one + * with domain and/or range outside of dom and ran. + */ +static int has_spurious_elements(__isl_keep isl_map *qc, + __isl_keep isl_set *dom, __isl_keep isl_set *ran) +{ + isl_set *s; + int subset; + unsigned d; + + if (!qc || !dom || !ran) + return -1; + + d = isl_map_dim(qc, isl_dim_in); + + qc = isl_map_copy(qc); + qc = set_path_length(qc, 0, 1); + qc = isl_map_project_out(qc, isl_dim_in, d - 1, 1); + qc = isl_map_project_out(qc, isl_dim_out, d - 1, 1); + + s = isl_map_domain(isl_map_copy(qc)); + subset = isl_set_is_subset(s, dom); + isl_set_free(s); + if (subset < 0) + goto error; + if (!subset) { + isl_map_free(qc); + return 1; + } + + s = isl_map_range(qc); + subset = isl_set_is_subset(s, ran); + isl_set_free(s); + + return subset < 0 ? -1 : !subset; +error: + isl_map_free(qc); + return -1; +} + +#define LEFT 2 +#define RIGHT 1 + +/* For each basic map in "map", except i, check whether it combines + * with the transitive closure that is reflexive on C combines + * to the left and to the right. + * + * In particular, if + * + * dom map_j \subseteq C + * + * then right[j] is set to 1. Otherwise, if + * + * ran map_i \cap dom map_j = \emptyset + * + * then right[j] is set to 0. Otherwise, composing to the right + * is impossible. + * + * Similar, for composing to the left, we have if + * + * ran map_j \subseteq C + * + * then left[j] is set to 1. Otherwise, if + * + * dom map_i \cap ran map_j = \emptyset + * + * then left[j] is set to 0. Otherwise, composing to the left + * is impossible. + * + * The return value is or'd with LEFT if composing to the left + * is possible and with RIGHT if composing to the right is possible. + */ +static int composability(__isl_keep isl_set *C, int i, + isl_set **dom, isl_set **ran, int *left, int *right, + __isl_keep isl_map *map) +{ + int j; + int ok; + + ok = LEFT | RIGHT; + for (j = 0; j < map->n && ok; ++j) { + int overlaps, subset; + if (j == i) + continue; + + if (ok & RIGHT) { + if (!dom[j]) + dom[j] = isl_set_from_basic_set( + isl_basic_map_domain( + isl_basic_map_copy(map->p[j]))); + if (!dom[j]) + return -1; + overlaps = isl_set_overlaps(ran[i], dom[j]); + if (overlaps < 0) + return -1; + if (!overlaps) + right[j] = 0; + else { + subset = isl_set_is_subset(dom[j], C); + if (subset < 0) + return -1; + if (subset) + right[j] = 1; + else + ok &= ~RIGHT; + } + } + + if (ok & LEFT) { + if (!ran[j]) + ran[j] = isl_set_from_basic_set( + isl_basic_map_range( + isl_basic_map_copy(map->p[j]))); + if (!ran[j]) + return -1; + overlaps = isl_set_overlaps(dom[i], ran[j]); + if (overlaps < 0) + return -1; + if (!overlaps) + left[j] = 0; + else { + subset = isl_set_is_subset(ran[j], C); + if (subset < 0) + return -1; + if (subset) + left[j] = 1; + else + ok &= ~LEFT; + } + } + } + + return ok; +} + +static __isl_give isl_map *anonymize(__isl_take isl_map *map) +{ + map = isl_map_reset(map, isl_dim_in); + map = isl_map_reset(map, isl_dim_out); + return map; +} + +/* Return a map that is a union of the basic maps in "map", except i, + * composed to left and right with qc based on the entries of "left" + * and "right". + */ +static __isl_give isl_map *compose(__isl_keep isl_map *map, int i, + __isl_take isl_map *qc, int *left, int *right) +{ + int j; + isl_map *comp; + + comp = isl_map_empty(isl_map_get_space(map)); + for (j = 0; j < map->n; ++j) { + isl_map *map_j; + + if (j == i) + continue; + + map_j = isl_map_from_basic_map(isl_basic_map_copy(map->p[j])); + map_j = anonymize(map_j); + if (left && left[j]) + map_j = isl_map_apply_range(map_j, isl_map_copy(qc)); + if (right && right[j]) + map_j = isl_map_apply_range(isl_map_copy(qc), map_j); + comp = isl_map_union(comp, map_j); + } + + comp = isl_map_compute_divs(comp); + comp = isl_map_coalesce(comp); + + isl_map_free(qc); + + return comp; +} + +/* Compute the transitive closure of "map" incrementally by + * computing + * + * map_i^+ \cup qc^+ + * + * or + * + * map_i^+ \cup ((id \cup map_i^) \circ qc^+) + * + * or + * + * map_i^+ \cup (qc^+ \circ (id \cup map_i^)) + * + * depending on whether left or right are NULL. + */ +static __isl_give isl_map *compute_incremental( + __isl_take isl_space *dim, __isl_keep isl_map *map, + int i, __isl_take isl_map *qc, int *left, int *right, int *exact) +{ + isl_map *map_i; + isl_map *tc; + isl_map *rtc = NULL; + + if (!map) + goto error; + isl_assert(map->ctx, left || right, goto error); + + map_i = isl_map_from_basic_map(isl_basic_map_copy(map->p[i])); + tc = construct_projected_component(isl_space_copy(dim), map_i, + exact, 1); + isl_map_free(map_i); + + if (*exact) + qc = isl_map_transitive_closure(qc, exact); + + if (!*exact) { + isl_space_free(dim); + isl_map_free(tc); + isl_map_free(qc); + return isl_map_universe(isl_map_get_space(map)); + } + + if (!left || !right) + rtc = isl_map_union(isl_map_copy(tc), + isl_map_identity(isl_map_get_space(tc))); + if (!right) + qc = isl_map_apply_range(rtc, qc); + if (!left) + qc = isl_map_apply_range(qc, rtc); + qc = isl_map_union(tc, qc); + + isl_space_free(dim); + + return qc; +error: + isl_space_free(dim); + isl_map_free(qc); + return NULL; +} + +/* Given a map "map", try to find a basic map such that + * map^+ can be computed as + * + * map^+ = map_i^+ \cup + * \bigcup_j ((map_i^+ \cup Id_C)^+ \circ map_j \circ (map_i^+ \cup Id_C))^+ + * + * with C the simple hull of the domain and range of the input map. + * map_i^ \cup Id_C is computed by allowing the path lengths to be zero + * and by intersecting domain and range with C. + * Of course, we need to check that this is actually equal to map_i^ \cup Id_C. + * Also, we only use the incremental computation if all the transitive + * closures are exact and if the number of basic maps in the union, + * after computing the integer divisions, is smaller than the number + * of basic maps in the input map. + */ +static int incemental_on_entire_domain(__isl_keep isl_space *dim, + __isl_keep isl_map *map, + isl_set **dom, isl_set **ran, int *left, int *right, + __isl_give isl_map **res) +{ + int i; + isl_set *C; + unsigned d; + + *res = NULL; + + C = isl_set_union(isl_map_domain(isl_map_copy(map)), + isl_map_range(isl_map_copy(map))); + C = isl_set_from_basic_set(isl_set_simple_hull(C)); + if (!C) + return -1; + if (C->n != 1) { + isl_set_free(C); + return 0; + } + + d = isl_map_dim(map, isl_dim_in); + + for (i = 0; i < map->n; ++i) { + isl_map *qc; + int exact_i, spurious; + int j; + dom[i] = isl_set_from_basic_set(isl_basic_map_domain( + isl_basic_map_copy(map->p[i]))); + ran[i] = isl_set_from_basic_set(isl_basic_map_range( + isl_basic_map_copy(map->p[i]))); + qc = q_closure(isl_space_copy(dim), isl_set_copy(C), + map->p[i], &exact_i); + if (!qc) + goto error; + if (!exact_i) { + isl_map_free(qc); + continue; + } + spurious = has_spurious_elements(qc, dom[i], ran[i]); + if (spurious) { + isl_map_free(qc); + if (spurious < 0) + goto error; + continue; + } + qc = isl_map_project_out(qc, isl_dim_in, d, 1); + qc = isl_map_project_out(qc, isl_dim_out, d, 1); + qc = isl_map_compute_divs(qc); + for (j = 0; j < map->n; ++j) + left[j] = right[j] = 1; + qc = compose(map, i, qc, left, right); + if (!qc) + goto error; + if (qc->n >= map->n) { + isl_map_free(qc); + continue; + } + *res = compute_incremental(isl_space_copy(dim), map, i, qc, + left, right, &exact_i); + if (!*res) + goto error; + if (exact_i) + break; + isl_map_free(*res); + *res = NULL; + } + + isl_set_free(C); + + return *res != NULL; +error: + isl_set_free(C); + return -1; +} + +/* Try and compute the transitive closure of "map" as + * + * map^+ = map_i^+ \cup + * \bigcup_j ((map_i^+ \cup Id_C)^+ \circ map_j \circ (map_i^+ \cup Id_C))^+ + * + * with C either the simple hull of the domain and range of the entire + * map or the simple hull of domain and range of map_i. + */ +static __isl_give isl_map *incremental_closure(__isl_take isl_space *dim, + __isl_keep isl_map *map, int *exact, int project) +{ + int i; + isl_set **dom = NULL; + isl_set **ran = NULL; + int *left = NULL; + int *right = NULL; + isl_set *C; + unsigned d; + isl_map *res = NULL; + + if (!project) + return construct_projected_component(dim, map, exact, project); + + if (!map) + goto error; + if (map->n <= 1) + return construct_projected_component(dim, map, exact, project); + + d = isl_map_dim(map, isl_dim_in); + + dom = isl_calloc_array(map->ctx, isl_set *, map->n); + ran = isl_calloc_array(map->ctx, isl_set *, map->n); + left = isl_calloc_array(map->ctx, int, map->n); + right = isl_calloc_array(map->ctx, int, map->n); + if (!ran || !dom || !left || !right) + goto error; + + if (incemental_on_entire_domain(dim, map, dom, ran, left, right, &res) < 0) + goto error; + + for (i = 0; !res && i < map->n; ++i) { + isl_map *qc; + int exact_i, spurious, comp; + if (!dom[i]) + dom[i] = isl_set_from_basic_set( + isl_basic_map_domain( + isl_basic_map_copy(map->p[i]))); + if (!dom[i]) + goto error; + if (!ran[i]) + ran[i] = isl_set_from_basic_set( + isl_basic_map_range( + isl_basic_map_copy(map->p[i]))); + if (!ran[i]) + goto error; + C = isl_set_union(isl_set_copy(dom[i]), + isl_set_copy(ran[i])); + C = isl_set_from_basic_set(isl_set_simple_hull(C)); + if (!C) + goto error; + if (C->n != 1) { + isl_set_free(C); + continue; + } + comp = composability(C, i, dom, ran, left, right, map); + if (!comp || comp < 0) { + isl_set_free(C); + if (comp < 0) + goto error; + continue; + } + qc = q_closure(isl_space_copy(dim), C, map->p[i], &exact_i); + if (!qc) + goto error; + if (!exact_i) { + isl_map_free(qc); + continue; + } + spurious = has_spurious_elements(qc, dom[i], ran[i]); + if (spurious) { + isl_map_free(qc); + if (spurious < 0) + goto error; + continue; + } + qc = isl_map_project_out(qc, isl_dim_in, d, 1); + qc = isl_map_project_out(qc, isl_dim_out, d, 1); + qc = isl_map_compute_divs(qc); + qc = compose(map, i, qc, (comp & LEFT) ? left : NULL, + (comp & RIGHT) ? right : NULL); + if (!qc) + goto error; + if (qc->n >= map->n) { + isl_map_free(qc); + continue; + } + res = compute_incremental(isl_space_copy(dim), map, i, qc, + (comp & LEFT) ? left : NULL, + (comp & RIGHT) ? right : NULL, &exact_i); + if (!res) + goto error; + if (exact_i) + break; + isl_map_free(res); + res = NULL; + } + + for (i = 0; i < map->n; ++i) { + isl_set_free(dom[i]); + isl_set_free(ran[i]); + } + free(dom); + free(ran); + free(left); + free(right); + + if (res) { + isl_space_free(dim); + return res; + } + + return construct_projected_component(dim, map, exact, project); +error: + if (dom) + for (i = 0; i < map->n; ++i) + isl_set_free(dom[i]); + free(dom); + if (ran) + for (i = 0; i < map->n; ++i) + isl_set_free(ran[i]); + free(ran); + free(left); + free(right); + isl_space_free(dim); + return NULL; +} + +/* Given an array of sets "set", add "dom" at position "pos" + * and search for elements at earlier positions that overlap with "dom". + * If any can be found, then merge all of them, together with "dom", into + * a single set and assign the union to the first in the array, + * which becomes the new group leader for all groups involved in the merge. + * During the search, we only consider group leaders, i.e., those with + * group[i] = i, as the other sets have already been combined + * with one of the group leaders. + */ +static int merge(isl_set **set, int *group, __isl_take isl_set *dom, int pos) +{ + int i; + + group[pos] = pos; + set[pos] = isl_set_copy(dom); + + for (i = pos - 1; i >= 0; --i) { + int o; + + if (group[i] != i) + continue; + + o = isl_set_overlaps(set[i], dom); + if (o < 0) + goto error; + if (!o) + continue; + + set[i] = isl_set_union(set[i], set[group[pos]]); + set[group[pos]] = NULL; + if (!set[i]) + goto error; + group[group[pos]] = i; + group[pos] = i; + } + + isl_set_free(dom); + return 0; +error: + isl_set_free(dom); + return -1; +} + +/* Replace each entry in the n by n grid of maps by the cross product + * with the relation { [i] -> [i + 1] }. + */ +static int add_length(__isl_keep isl_map *map, isl_map ***grid, int n) +{ + int i, j, k; + isl_space *dim; + isl_basic_map *bstep; + isl_map *step; + unsigned nparam; + + if (!map) + return -1; + + dim = isl_map_get_space(map); + nparam = isl_space_dim(dim, isl_dim_param); + dim = isl_space_drop_dims(dim, isl_dim_in, 0, isl_space_dim(dim, isl_dim_in)); + dim = isl_space_drop_dims(dim, isl_dim_out, 0, isl_space_dim(dim, isl_dim_out)); + dim = isl_space_add_dims(dim, isl_dim_in, 1); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + bstep = isl_basic_map_alloc_space(dim, 0, 1, 0); + k = isl_basic_map_alloc_equality(bstep); + if (k < 0) { + isl_basic_map_free(bstep); + return -1; + } + isl_seq_clr(bstep->eq[k], 1 + isl_basic_map_total_dim(bstep)); + isl_int_set_si(bstep->eq[k][0], 1); + isl_int_set_si(bstep->eq[k][1 + nparam], 1); + isl_int_set_si(bstep->eq[k][1 + nparam + 1], -1); + bstep = isl_basic_map_finalize(bstep); + step = isl_map_from_basic_map(bstep); + + for (i = 0; i < n; ++i) + for (j = 0; j < n; ++j) + grid[i][j] = isl_map_product(grid[i][j], + isl_map_copy(step)); + + isl_map_free(step); + + return 0; +} + +/* The core of the Floyd-Warshall algorithm. + * Updates the given n x x matrix of relations in place. + * + * The algorithm iterates over all vertices. In each step, the whole + * matrix is updated to include all paths that go to the current vertex, + * possibly stay there a while (including passing through earlier vertices) + * and then come back. At the start of each iteration, the diagonal + * element corresponding to the current vertex is replaced by its + * transitive closure to account for all indirect paths that stay + * in the current vertex. + */ +static void floyd_warshall_iterate(isl_map ***grid, int n, int *exact) +{ + int r, p, q; + + for (r = 0; r < n; ++r) { + int r_exact; + grid[r][r] = isl_map_transitive_closure(grid[r][r], + (exact && *exact) ? &r_exact : NULL); + if (exact && *exact && !r_exact) + *exact = 0; + + for (p = 0; p < n; ++p) + for (q = 0; q < n; ++q) { + isl_map *loop; + if (p == r && q == r) + continue; + loop = isl_map_apply_range( + isl_map_copy(grid[p][r]), + isl_map_copy(grid[r][q])); + grid[p][q] = isl_map_union(grid[p][q], loop); + loop = isl_map_apply_range( + isl_map_copy(grid[p][r]), + isl_map_apply_range( + isl_map_copy(grid[r][r]), + isl_map_copy(grid[r][q]))); + grid[p][q] = isl_map_union(grid[p][q], loop); + grid[p][q] = isl_map_coalesce(grid[p][q]); + } + } +} + +/* Given a partition of the domains and ranges of the basic maps in "map", + * apply the Floyd-Warshall algorithm with the elements in the partition + * as vertices. + * + * In particular, there are "n" elements in the partition and "group" is + * an array of length 2 * map->n with entries in [0,n-1]. + * + * We first construct a matrix of relations based on the partition information, + * apply Floyd-Warshall on this matrix of relations and then take the + * union of all entries in the matrix as the final result. + * + * If we are actually computing the power instead of the transitive closure, + * i.e., when "project" is not set, then the result should have the + * path lengths encoded as the difference between an extra pair of + * coordinates. We therefore apply the nested transitive closures + * to relations that include these lengths. In particular, we replace + * the input relation by the cross product with the unit length relation + * { [i] -> [i + 1] }. + */ +static __isl_give isl_map *floyd_warshall_with_groups(__isl_take isl_space *dim, + __isl_keep isl_map *map, int *exact, int project, int *group, int n) +{ + int i, j, k; + isl_map ***grid = NULL; + isl_map *app; + + if (!map) + goto error; + + if (n == 1) { + free(group); + return incremental_closure(dim, map, exact, project); + } + + grid = isl_calloc_array(map->ctx, isl_map **, n); + if (!grid) + goto error; + for (i = 0; i < n; ++i) { + grid[i] = isl_calloc_array(map->ctx, isl_map *, n); + if (!grid[i]) + goto error; + for (j = 0; j < n; ++j) + grid[i][j] = isl_map_empty(isl_map_get_space(map)); + } + + for (k = 0; k < map->n; ++k) { + i = group[2 * k]; + j = group[2 * k + 1]; + grid[i][j] = isl_map_union(grid[i][j], + isl_map_from_basic_map( + isl_basic_map_copy(map->p[k]))); + } + + if (!project && add_length(map, grid, n) < 0) + goto error; + + floyd_warshall_iterate(grid, n, exact); + + app = isl_map_empty(isl_map_get_space(grid[0][0])); + + for (i = 0; i < n; ++i) { + for (j = 0; j < n; ++j) + app = isl_map_union(app, grid[i][j]); + free(grid[i]); + } + free(grid); + + free(group); + isl_space_free(dim); + + return app; +error: + if (grid) + for (i = 0; i < n; ++i) { + if (!grid[i]) + continue; + for (j = 0; j < n; ++j) + isl_map_free(grid[i][j]); + free(grid[i]); + } + free(grid); + free(group); + isl_space_free(dim); + return NULL; +} + +/* Partition the domains and ranges of the n basic relations in list + * into disjoint cells. + * + * To find the partition, we simply consider all of the domains + * and ranges in turn and combine those that overlap. + * "set" contains the partition elements and "group" indicates + * to which partition element a given domain or range belongs. + * The domain of basic map i corresponds to element 2 * i in these arrays, + * while the domain corresponds to element 2 * i + 1. + * During the construction group[k] is either equal to k, + * in which case set[k] contains the union of all the domains and + * ranges in the corresponding group, or is equal to some l < k, + * with l another domain or range in the same group. + */ +static int *setup_groups(isl_ctx *ctx, __isl_keep isl_basic_map **list, int n, + isl_set ***set, int *n_group) +{ + int i; + int *group = NULL; + int g; + + *set = isl_calloc_array(ctx, isl_set *, 2 * n); + group = isl_alloc_array(ctx, int, 2 * n); + + if (!*set || !group) + goto error; + + for (i = 0; i < n; ++i) { + isl_set *dom; + dom = isl_set_from_basic_set(isl_basic_map_domain( + isl_basic_map_copy(list[i]))); + if (merge(*set, group, dom, 2 * i) < 0) + goto error; + dom = isl_set_from_basic_set(isl_basic_map_range( + isl_basic_map_copy(list[i]))); + if (merge(*set, group, dom, 2 * i + 1) < 0) + goto error; + } + + g = 0; + for (i = 0; i < 2 * n; ++i) + if (group[i] == i) { + if (g != i) { + (*set)[g] = (*set)[i]; + (*set)[i] = NULL; + } + group[i] = g++; + } else + group[i] = group[group[i]]; + + *n_group = g; + + return group; +error: + if (*set) { + for (i = 0; i < 2 * n; ++i) + isl_set_free((*set)[i]); + free(*set); + *set = NULL; + } + free(group); + return NULL; +} + +/* Check if the domains and ranges of the basic maps in "map" can + * be partitioned, and if so, apply Floyd-Warshall on the elements + * of the partition. Note that we also apply this algorithm + * if we want to compute the power, i.e., when "project" is not set. + * However, the results are unlikely to be exact since the recursive + * calls inside the Floyd-Warshall algorithm typically result in + * non-linear path lengths quite quickly. + */ +static __isl_give isl_map *floyd_warshall(__isl_take isl_space *dim, + __isl_keep isl_map *map, int *exact, int project) +{ + int i; + isl_set **set = NULL; + int *group = NULL; + int n; + + if (!map) + goto error; + if (map->n <= 1) + return incremental_closure(dim, map, exact, project); + + group = setup_groups(map->ctx, map->p, map->n, &set, &n); + if (!group) + goto error; + + for (i = 0; i < 2 * map->n; ++i) + isl_set_free(set[i]); + + free(set); + + return floyd_warshall_with_groups(dim, map, exact, project, group, n); +error: + isl_space_free(dim); + return NULL; +} + +/* Structure for representing the nodes of the graph of which + * strongly connected components are being computed. + * + * list contains the actual nodes + * check_closed is set if we may have used the fact that + * a pair of basic maps can be interchanged + */ +struct isl_tc_follows_data { + isl_basic_map **list; + int check_closed; +}; + +/* Check whether in the computation of the transitive closure + * "list[i]" (R_1) should follow (or be part of the same component as) + * "list[j]" (R_2). + * + * That is check whether + * + * R_1 \circ R_2 + * + * is a subset of + * + * R_2 \circ R_1 + * + * If so, then there is no reason for R_1 to immediately follow R_2 + * in any path. + * + * *check_closed is set if the subset relation holds while + * R_1 \circ R_2 is not empty. + */ +static isl_bool basic_map_follows(int i, int j, void *user) +{ + struct isl_tc_follows_data *data = user; + struct isl_map *map12 = NULL; + struct isl_map *map21 = NULL; + isl_bool subset; + + if (!isl_space_tuple_is_equal(data->list[i]->dim, isl_dim_in, + data->list[j]->dim, isl_dim_out)) + return isl_bool_false; + + map21 = isl_map_from_basic_map( + isl_basic_map_apply_range( + isl_basic_map_copy(data->list[j]), + isl_basic_map_copy(data->list[i]))); + subset = isl_map_is_empty(map21); + if (subset < 0) + goto error; + if (subset) { + isl_map_free(map21); + return isl_bool_false; + } + + if (!isl_space_tuple_is_equal(data->list[i]->dim, isl_dim_in, + data->list[i]->dim, isl_dim_out) || + !isl_space_tuple_is_equal(data->list[j]->dim, isl_dim_in, + data->list[j]->dim, isl_dim_out)) { + isl_map_free(map21); + return isl_bool_true; + } + + map12 = isl_map_from_basic_map( + isl_basic_map_apply_range( + isl_basic_map_copy(data->list[i]), + isl_basic_map_copy(data->list[j]))); + + subset = isl_map_is_subset(map21, map12); + + isl_map_free(map12); + isl_map_free(map21); + + if (subset) + data->check_closed = 1; + + return subset < 0 ? isl_bool_error : !subset; +error: + isl_map_free(map21); + return isl_bool_error; +} + +/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D + * and a dimension specification (Z^{n+1} -> Z^{n+1}), + * construct a map that is an overapproximation of the map + * that takes an element from the dom R \times Z to an + * element from ran R \times Z, such that the first n coordinates of the + * difference between them is a sum of differences between images + * and pre-images in one of the R_i and such that the last coordinate + * is equal to the number of steps taken. + * If "project" is set, then these final coordinates are not included, + * i.e., a relation of type Z^n -> Z^n is returned. + * That is, let + * + * \Delta_i = { y - x | (x, y) in R_i } + * + * then the constructed map is an overapproximation of + * + * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i : + * d = (\sum_i k_i \delta_i, \sum_i k_i) and + * x in dom R and x + d in ran R } + * + * or + * + * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i : + * d = (\sum_i k_i \delta_i) and + * x in dom R and x + d in ran R } + * + * if "project" is set. + * + * We first split the map into strongly connected components, perform + * the above on each component and then join the results in the correct + * order, at each join also taking in the union of both arguments + * to allow for paths that do not go through one of the two arguments. + */ +static __isl_give isl_map *construct_power_components(__isl_take isl_space *dim, + __isl_keep isl_map *map, int *exact, int project) +{ + int i, n, c; + struct isl_map *path = NULL; + struct isl_tc_follows_data data; + struct isl_tarjan_graph *g = NULL; + int *orig_exact; + int local_exact; + + if (!map) + goto error; + if (map->n <= 1) + return floyd_warshall(dim, map, exact, project); + + data.list = map->p; + data.check_closed = 0; + g = isl_tarjan_graph_init(map->ctx, map->n, &basic_map_follows, &data); + if (!g) + goto error; + + orig_exact = exact; + if (data.check_closed && !exact) + exact = &local_exact; + + c = 0; + i = 0; + n = map->n; + if (project) + path = isl_map_empty(isl_map_get_space(map)); + else + path = isl_map_empty(isl_space_copy(dim)); + path = anonymize(path); + while (n) { + struct isl_map *comp; + isl_map *path_comp, *path_comb; + comp = isl_map_alloc_space(isl_map_get_space(map), n, 0); + while (g->order[i] != -1) { + comp = isl_map_add_basic_map(comp, + isl_basic_map_copy(map->p[g->order[i]])); + --n; + ++i; + } + path_comp = floyd_warshall(isl_space_copy(dim), + comp, exact, project); + path_comp = anonymize(path_comp); + path_comb = isl_map_apply_range(isl_map_copy(path), + isl_map_copy(path_comp)); + path = isl_map_union(path, path_comp); + path = isl_map_union(path, path_comb); + isl_map_free(comp); + ++i; + ++c; + } + + if (c > 1 && data.check_closed && !*exact) { + int closed; + + closed = isl_map_is_transitively_closed(path); + if (closed < 0) + goto error; + if (!closed) { + isl_tarjan_graph_free(g); + isl_map_free(path); + return floyd_warshall(dim, map, orig_exact, project); + } + } + + isl_tarjan_graph_free(g); + isl_space_free(dim); + + return path; +error: + isl_tarjan_graph_free(g); + isl_space_free(dim); + isl_map_free(path); + return NULL; +} + +/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D, + * construct a map that is an overapproximation of the map + * that takes an element from the space D to another + * element from the same space, such that the difference between + * them is a strictly positive sum of differences between images + * and pre-images in one of the R_i. + * The number of differences in the sum is equated to parameter "param". + * That is, let + * + * \Delta_i = { y - x | (x, y) in R_i } + * + * then the constructed map is an overapproximation of + * + * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i : + * d = \sum_i k_i \delta_i and k = \sum_i k_i > 0 } + * or + * + * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i : + * d = \sum_i k_i \delta_i and \sum_i k_i > 0 } + * + * if "project" is set. + * + * If "project" is not set, then + * we construct an extended mapping with an extra coordinate + * that indicates the number of steps taken. In particular, + * the difference in the last coordinate is equal to the number + * of steps taken to move from a domain element to the corresponding + * image element(s). + */ +static __isl_give isl_map *construct_power(__isl_keep isl_map *map, + int *exact, int project) +{ + struct isl_map *app = NULL; + isl_space *dim = NULL; + + if (!map) + return NULL; + + dim = isl_map_get_space(map); + + dim = isl_space_add_dims(dim, isl_dim_in, 1); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + + app = construct_power_components(isl_space_copy(dim), map, + exact, project); + + isl_space_free(dim); + + return app; +} + +/* Compute the positive powers of "map", or an overapproximation. + * If the result is exact, then *exact is set to 1. + * + * If project is set, then we are actually interested in the transitive + * closure, so we can use a more relaxed exactness check. + * The lengths of the paths are also projected out instead of being + * encoded as the difference between an extra pair of final coordinates. + */ +static __isl_give isl_map *map_power(__isl_take isl_map *map, + int *exact, int project) +{ + struct isl_map *app = NULL; + + if (exact) + *exact = 1; + + if (!map) + return NULL; + + isl_assert(map->ctx, + isl_map_dim(map, isl_dim_in) == isl_map_dim(map, isl_dim_out), + goto error); + + app = construct_power(map, exact, project); + + isl_map_free(map); + return app; +error: + isl_map_free(map); + isl_map_free(app); + return NULL; +} + +/* Compute the positive powers of "map", or an overapproximation. + * The result maps the exponent to a nested copy of the corresponding power. + * If the result is exact, then *exact is set to 1. + * map_power constructs an extended relation with the path lengths + * encoded as the difference between the final coordinates. + * In the final step, this difference is equated to an extra parameter + * and made positive. The extra coordinates are subsequently projected out + * and the parameter is turned into the domain of the result. + */ +__isl_give isl_map *isl_map_power(__isl_take isl_map *map, int *exact) +{ + isl_space *target_dim; + isl_space *dim; + isl_map *diff; + unsigned d; + unsigned param; + + if (!map) + return NULL; + + d = isl_map_dim(map, isl_dim_in); + param = isl_map_dim(map, isl_dim_param); + + map = isl_map_compute_divs(map); + map = isl_map_coalesce(map); + + if (isl_map_plain_is_empty(map)) { + map = isl_map_from_range(isl_map_wrap(map)); + map = isl_map_add_dims(map, isl_dim_in, 1); + map = isl_map_set_dim_name(map, isl_dim_in, 0, "k"); + return map; + } + + target_dim = isl_map_get_space(map); + target_dim = isl_space_from_range(isl_space_wrap(target_dim)); + target_dim = isl_space_add_dims(target_dim, isl_dim_in, 1); + target_dim = isl_space_set_dim_name(target_dim, isl_dim_in, 0, "k"); + + map = map_power(map, exact, 0); + + map = isl_map_add_dims(map, isl_dim_param, 1); + dim = isl_map_get_space(map); + diff = equate_parameter_to_length(dim, param); + map = isl_map_intersect(map, diff); + map = isl_map_project_out(map, isl_dim_in, d, 1); + map = isl_map_project_out(map, isl_dim_out, d, 1); + map = isl_map_from_range(isl_map_wrap(map)); + map = isl_map_move_dims(map, isl_dim_in, 0, isl_dim_param, param, 1); + + map = isl_map_reset_space(map, target_dim); + + return map; +} + +/* Compute a relation that maps each element in the range of the input + * relation to the lengths of all paths composed of edges in the input + * relation that end up in the given range element. + * The result may be an overapproximation, in which case *exact is set to 0. + * The resulting relation is very similar to the power relation. + * The difference are that the domain has been projected out, the + * range has become the domain and the exponent is the range instead + * of a parameter. + */ +__isl_give isl_map *isl_map_reaching_path_lengths(__isl_take isl_map *map, + int *exact) +{ + isl_space *dim; + isl_map *diff; + unsigned d; + unsigned param; + + if (!map) + return NULL; + + d = isl_map_dim(map, isl_dim_in); + param = isl_map_dim(map, isl_dim_param); + + map = isl_map_compute_divs(map); + map = isl_map_coalesce(map); + + if (isl_map_plain_is_empty(map)) { + if (exact) + *exact = 1; + map = isl_map_project_out(map, isl_dim_out, 0, d); + map = isl_map_add_dims(map, isl_dim_out, 1); + return map; + } + + map = map_power(map, exact, 0); + + map = isl_map_add_dims(map, isl_dim_param, 1); + dim = isl_map_get_space(map); + diff = equate_parameter_to_length(dim, param); + map = isl_map_intersect(map, diff); + map = isl_map_project_out(map, isl_dim_in, 0, d + 1); + map = isl_map_project_out(map, isl_dim_out, d, 1); + map = isl_map_reverse(map); + map = isl_map_move_dims(map, isl_dim_out, 0, isl_dim_param, param, 1); + + return map; +} + +/* Check whether equality i of bset is a pure stride constraint + * on a single dimensions, i.e., of the form + * + * v = k e + * + * with k a constant and e an existentially quantified variable. + */ +static int is_eq_stride(__isl_keep isl_basic_set *bset, int i) +{ + unsigned nparam; + unsigned d; + unsigned n_div; + int pos1; + int pos2; + + if (!bset) + return -1; + + if (!isl_int_is_zero(bset->eq[i][0])) + return 0; + + nparam = isl_basic_set_dim(bset, isl_dim_param); + d = isl_basic_set_dim(bset, isl_dim_set); + n_div = isl_basic_set_dim(bset, isl_dim_div); + + if (isl_seq_first_non_zero(bset->eq[i] + 1, nparam) != -1) + return 0; + pos1 = isl_seq_first_non_zero(bset->eq[i] + 1 + nparam, d); + if (pos1 == -1) + return 0; + if (isl_seq_first_non_zero(bset->eq[i] + 1 + nparam + pos1 + 1, + d - pos1 - 1) != -1) + return 0; + + pos2 = isl_seq_first_non_zero(bset->eq[i] + 1 + nparam + d, n_div); + if (pos2 == -1) + return 0; + if (isl_seq_first_non_zero(bset->eq[i] + 1 + nparam + d + pos2 + 1, + n_div - pos2 - 1) != -1) + return 0; + if (!isl_int_is_one(bset->eq[i][1 + nparam + pos1]) && + !isl_int_is_negone(bset->eq[i][1 + nparam + pos1])) + return 0; + + return 1; +} + +/* Given a map, compute the smallest superset of this map that is of the form + * + * { i -> j : L <= j - i <= U and exists a_p: j_p - i_p = M_p a_p } + * + * (where p ranges over the (non-parametric) dimensions), + * compute the transitive closure of this map, i.e., + * + * { i -> j : exists k > 0: + * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p } + * + * and intersect domain and range of this transitive closure with + * the given domain and range. + * + * If with_id is set, then try to include as much of the identity mapping + * as possible, by computing + * + * { i -> j : exists k >= 0: + * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p } + * + * instead (i.e., allow k = 0). + * + * In practice, we compute the difference set + * + * delta = { j - i | i -> j in map }, + * + * look for stride constraint on the individual dimensions and compute + * (constant) lower and upper bounds for each individual dimension, + * adding a constraint for each bound not equal to infinity. + */ +static __isl_give isl_map *box_closure_on_domain(__isl_take isl_map *map, + __isl_take isl_set *dom, __isl_take isl_set *ran, int with_id) +{ + int i; + int k; + unsigned d; + unsigned nparam; + unsigned total; + isl_space *dim; + isl_set *delta; + isl_map *app = NULL; + isl_basic_set *aff = NULL; + isl_basic_map *bmap = NULL; + isl_vec *obj = NULL; + isl_int opt; + + isl_int_init(opt); + + delta = isl_map_deltas(isl_map_copy(map)); + + aff = isl_set_affine_hull(isl_set_copy(delta)); + if (!aff) + goto error; + dim = isl_map_get_space(map); + d = isl_space_dim(dim, isl_dim_in); + nparam = isl_space_dim(dim, isl_dim_param); + total = isl_space_dim(dim, isl_dim_all); + bmap = isl_basic_map_alloc_space(dim, + aff->n_div + 1, aff->n_div, 2 * d + 1); + for (i = 0; i < aff->n_div + 1; ++i) { + k = isl_basic_map_alloc_div(bmap); + if (k < 0) + goto error; + isl_int_set_si(bmap->div[k][0], 0); + } + for (i = 0; i < aff->n_eq; ++i) { + if (!is_eq_stride(aff, i)) + continue; + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->eq[k], 1 + nparam); + isl_seq_cpy(bmap->eq[k] + 1 + nparam + d, + aff->eq[i] + 1 + nparam, d); + isl_seq_neg(bmap->eq[k] + 1 + nparam, + aff->eq[i] + 1 + nparam, d); + isl_seq_cpy(bmap->eq[k] + 1 + nparam + 2 * d, + aff->eq[i] + 1 + nparam + d, aff->n_div); + isl_int_set_si(bmap->eq[k][1 + total + aff->n_div], 0); + } + obj = isl_vec_alloc(map->ctx, 1 + nparam + d); + if (!obj) + goto error; + isl_seq_clr(obj->el, 1 + nparam + d); + for (i = 0; i < d; ++ i) { + enum isl_lp_result res; + + isl_int_set_si(obj->el[1 + nparam + i], 1); + + res = isl_set_solve_lp(delta, 0, obj->el, map->ctx->one, &opt, + NULL, NULL); + if (res == isl_lp_error) + goto error; + if (res == isl_lp_ok) { + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->ineq[k], + 1 + nparam + 2 * d + bmap->n_div); + isl_int_set_si(bmap->ineq[k][1 + nparam + i], -1); + isl_int_set_si(bmap->ineq[k][1 + nparam + d + i], 1); + isl_int_neg(bmap->ineq[k][1 + nparam + 2 * d + aff->n_div], opt); + } + + res = isl_set_solve_lp(delta, 1, obj->el, map->ctx->one, &opt, + NULL, NULL); + if (res == isl_lp_error) + goto error; + if (res == isl_lp_ok) { + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->ineq[k], + 1 + nparam + 2 * d + bmap->n_div); + isl_int_set_si(bmap->ineq[k][1 + nparam + i], 1); + isl_int_set_si(bmap->ineq[k][1 + nparam + d + i], -1); + isl_int_set(bmap->ineq[k][1 + nparam + 2 * d + aff->n_div], opt); + } + + isl_int_set_si(obj->el[1 + nparam + i], 0); + } + k = isl_basic_map_alloc_inequality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->ineq[k], + 1 + nparam + 2 * d + bmap->n_div); + if (!with_id) + isl_int_set_si(bmap->ineq[k][0], -1); + isl_int_set_si(bmap->ineq[k][1 + nparam + 2 * d + aff->n_div], 1); + + app = isl_map_from_domain_and_range(dom, ran); + + isl_vec_free(obj); + isl_basic_set_free(aff); + isl_map_free(map); + bmap = isl_basic_map_finalize(bmap); + isl_set_free(delta); + isl_int_clear(opt); + + map = isl_map_from_basic_map(bmap); + map = isl_map_intersect(map, app); + + return map; +error: + isl_vec_free(obj); + isl_basic_map_free(bmap); + isl_basic_set_free(aff); + isl_set_free(dom); + isl_set_free(ran); + isl_map_free(map); + isl_set_free(delta); + isl_int_clear(opt); + return NULL; +} + +/* Given a map, compute the smallest superset of this map that is of the form + * + * { i -> j : L <= j - i <= U and exists a_p: j_p - i_p = M_p a_p } + * + * (where p ranges over the (non-parametric) dimensions), + * compute the transitive closure of this map, i.e., + * + * { i -> j : exists k > 0: + * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p } + * + * and intersect domain and range of this transitive closure with + * domain and range of the original map. + */ +static __isl_give isl_map *box_closure(__isl_take isl_map *map) +{ + isl_set *domain; + isl_set *range; + + domain = isl_map_domain(isl_map_copy(map)); + domain = isl_set_coalesce(domain); + range = isl_map_range(isl_map_copy(map)); + range = isl_set_coalesce(range); + + return box_closure_on_domain(map, domain, range, 0); +} + +/* Given a map, compute the smallest superset of this map that is of the form + * + * { i -> j : L <= j - i <= U and exists a_p: j_p - i_p = M_p a_p } + * + * (where p ranges over the (non-parametric) dimensions), + * compute the transitive and partially reflexive closure of this map, i.e., + * + * { i -> j : exists k >= 0: + * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p } + * + * and intersect domain and range of this transitive closure with + * the given domain. + */ +static __isl_give isl_map *box_closure_with_identity(__isl_take isl_map *map, + __isl_take isl_set *dom) +{ + return box_closure_on_domain(map, dom, isl_set_copy(dom), 1); +} + +/* Check whether app is the transitive closure of map. + * In particular, check that app is acyclic and, if so, + * check that + * + * app \subset (map \cup (map \circ app)) + */ +static int check_exactness_omega(__isl_keep isl_map *map, + __isl_keep isl_map *app) +{ + isl_set *delta; + int i; + int is_empty, is_exact; + unsigned d; + isl_map *test; + + delta = isl_map_deltas(isl_map_copy(app)); + d = isl_set_dim(delta, isl_dim_set); + for (i = 0; i < d; ++i) + delta = isl_set_fix_si(delta, isl_dim_set, i, 0); + is_empty = isl_set_is_empty(delta); + isl_set_free(delta); + if (is_empty < 0) + return -1; + if (!is_empty) + return 0; + + test = isl_map_apply_range(isl_map_copy(app), isl_map_copy(map)); + test = isl_map_union(test, isl_map_copy(map)); + is_exact = isl_map_is_subset(app, test); + isl_map_free(test); + + return is_exact; +} + +/* Check if basic map M_i can be combined with all the other + * basic maps such that + * + * (\cup_j M_j)^+ + * + * can be computed as + * + * M_i \cup (\cup_{j \ne i} M_i^* \circ M_j \circ M_i^*)^+ + * + * In particular, check if we can compute a compact representation + * of + * + * M_i^* \circ M_j \circ M_i^* + * + * for each j != i. + * Let M_i^? be an extension of M_i^+ that allows paths + * of length zero, i.e., the result of box_closure(., 1). + * The criterion, as proposed by Kelly et al., is that + * id = M_i^? - M_i^+ can be represented as a basic map + * and that + * + * id \circ M_j \circ id = M_j + * + * for each j != i. + * + * If this function returns 1, then tc and qc are set to + * M_i^+ and M_i^?, respectively. + */ +static int can_be_split_off(__isl_keep isl_map *map, int i, + __isl_give isl_map **tc, __isl_give isl_map **qc) +{ + isl_map *map_i, *id = NULL; + int j = -1; + isl_set *C; + + *tc = NULL; + *qc = NULL; + + C = isl_set_union(isl_map_domain(isl_map_copy(map)), + isl_map_range(isl_map_copy(map))); + C = isl_set_from_basic_set(isl_set_simple_hull(C)); + if (!C) + goto error; + + map_i = isl_map_from_basic_map(isl_basic_map_copy(map->p[i])); + *tc = box_closure(isl_map_copy(map_i)); + *qc = box_closure_with_identity(map_i, C); + id = isl_map_subtract(isl_map_copy(*qc), isl_map_copy(*tc)); + + if (!id || !*qc) + goto error; + if (id->n != 1 || (*qc)->n != 1) + goto done; + + for (j = 0; j < map->n; ++j) { + isl_map *map_j, *test; + int is_ok; + + if (i == j) + continue; + map_j = isl_map_from_basic_map( + isl_basic_map_copy(map->p[j])); + test = isl_map_apply_range(isl_map_copy(id), + isl_map_copy(map_j)); + test = isl_map_apply_range(test, isl_map_copy(id)); + is_ok = isl_map_is_equal(test, map_j); + isl_map_free(map_j); + isl_map_free(test); + if (is_ok < 0) + goto error; + if (!is_ok) + break; + } + +done: + isl_map_free(id); + if (j == map->n) + return 1; + + isl_map_free(*qc); + isl_map_free(*tc); + *qc = NULL; + *tc = NULL; + + return 0; +error: + isl_map_free(id); + isl_map_free(*qc); + isl_map_free(*tc); + *qc = NULL; + *tc = NULL; + return -1; +} + +static __isl_give isl_map *box_closure_with_check(__isl_take isl_map *map, + int *exact) +{ + isl_map *app; + + app = box_closure(isl_map_copy(map)); + if (exact) + *exact = check_exactness_omega(map, app); + + isl_map_free(map); + return app; +} + +/* Compute an overapproximation of the transitive closure of "map" + * using a variation of the algorithm from + * "Transitive Closure of Infinite Graphs and its Applications" + * by Kelly et al. + * + * We first check whether we can can split of any basic map M_i and + * compute + * + * (\cup_j M_j)^+ + * + * as + * + * M_i \cup (\cup_{j \ne i} M_i^* \circ M_j \circ M_i^*)^+ + * + * using a recursive call on the remaining map. + * + * If not, we simply call box_closure on the whole map. + */ +static __isl_give isl_map *transitive_closure_omega(__isl_take isl_map *map, + int *exact) +{ + int i, j; + int exact_i; + isl_map *app; + + if (!map) + return NULL; + if (map->n == 1) + return box_closure_with_check(map, exact); + + for (i = 0; i < map->n; ++i) { + int ok; + isl_map *qc, *tc; + ok = can_be_split_off(map, i, &tc, &qc); + if (ok < 0) + goto error; + if (!ok) + continue; + + app = isl_map_alloc_space(isl_map_get_space(map), map->n - 1, 0); + + for (j = 0; j < map->n; ++j) { + if (j == i) + continue; + app = isl_map_add_basic_map(app, + isl_basic_map_copy(map->p[j])); + } + + app = isl_map_apply_range(isl_map_copy(qc), app); + app = isl_map_apply_range(app, qc); + + app = isl_map_union(tc, transitive_closure_omega(app, NULL)); + exact_i = check_exactness_omega(map, app); + if (exact_i == 1) { + if (exact) + *exact = exact_i; + isl_map_free(map); + return app; + } + isl_map_free(app); + if (exact_i < 0) + goto error; + } + + return box_closure_with_check(map, exact); +error: + isl_map_free(map); + return NULL; +} + +/* Compute the transitive closure of "map", or an overapproximation. + * If the result is exact, then *exact is set to 1. + * Simply use map_power to compute the powers of map, but tell + * it to project out the lengths of the paths instead of equating + * the length to a parameter. + */ +__isl_give isl_map *isl_map_transitive_closure(__isl_take isl_map *map, + int *exact) +{ + isl_space *target_dim; + int closed; + + if (!map) + goto error; + + if (map->ctx->opt->closure == ISL_CLOSURE_BOX) + return transitive_closure_omega(map, exact); + + map = isl_map_compute_divs(map); + map = isl_map_coalesce(map); + closed = isl_map_is_transitively_closed(map); + if (closed < 0) + goto error; + if (closed) { + if (exact) + *exact = 1; + return map; + } + + target_dim = isl_map_get_space(map); + map = map_power(map, exact, 1); + map = isl_map_reset_space(map, target_dim); + + return map; +error: + isl_map_free(map); + return NULL; +} + +static isl_stat inc_count(__isl_take isl_map *map, void *user) +{ + int *n = user; + + *n += map->n; + + isl_map_free(map); + + return isl_stat_ok; +} + +static isl_stat collect_basic_map(__isl_take isl_map *map, void *user) +{ + int i; + isl_basic_map ***next = user; + + for (i = 0; i < map->n; ++i) { + **next = isl_basic_map_copy(map->p[i]); + if (!**next) + goto error; + (*next)++; + } + + isl_map_free(map); + return isl_stat_ok; +error: + isl_map_free(map); + return isl_stat_error; +} + +/* Perform Floyd-Warshall on the given list of basic relations. + * The basic relations may live in different dimensions, + * but basic relations that get assigned to the diagonal of the + * grid have domains and ranges of the same dimension and so + * the standard algorithm can be used because the nested transitive + * closures are only applied to diagonal elements and because all + * compositions are peformed on relations with compatible domains and ranges. + */ +static __isl_give isl_union_map *union_floyd_warshall_on_list(isl_ctx *ctx, + __isl_keep isl_basic_map **list, int n, int *exact) +{ + int i, j, k; + int n_group; + int *group = NULL; + isl_set **set = NULL; + isl_map ***grid = NULL; + isl_union_map *app; + + group = setup_groups(ctx, list, n, &set, &n_group); + if (!group) + goto error; + + grid = isl_calloc_array(ctx, isl_map **, n_group); + if (!grid) + goto error; + for (i = 0; i < n_group; ++i) { + grid[i] = isl_calloc_array(ctx, isl_map *, n_group); + if (!grid[i]) + goto error; + for (j = 0; j < n_group; ++j) { + isl_space *dim1, *dim2, *dim; + dim1 = isl_space_reverse(isl_set_get_space(set[i])); + dim2 = isl_set_get_space(set[j]); + dim = isl_space_join(dim1, dim2); + grid[i][j] = isl_map_empty(dim); + } + } + + for (k = 0; k < n; ++k) { + i = group[2 * k]; + j = group[2 * k + 1]; + grid[i][j] = isl_map_union(grid[i][j], + isl_map_from_basic_map( + isl_basic_map_copy(list[k]))); + } + + floyd_warshall_iterate(grid, n_group, exact); + + app = isl_union_map_empty(isl_map_get_space(grid[0][0])); + + for (i = 0; i < n_group; ++i) { + for (j = 0; j < n_group; ++j) + app = isl_union_map_add_map(app, grid[i][j]); + free(grid[i]); + } + free(grid); + + for (i = 0; i < 2 * n; ++i) + isl_set_free(set[i]); + free(set); + + free(group); + return app; +error: + if (grid) + for (i = 0; i < n_group; ++i) { + if (!grid[i]) + continue; + for (j = 0; j < n_group; ++j) + isl_map_free(grid[i][j]); + free(grid[i]); + } + free(grid); + if (set) { + for (i = 0; i < 2 * n; ++i) + isl_set_free(set[i]); + free(set); + } + free(group); + return NULL; +} + +/* Perform Floyd-Warshall on the given union relation. + * The implementation is very similar to that for non-unions. + * The main difference is that it is applied unconditionally. + * We first extract a list of basic maps from the union map + * and then perform the algorithm on this list. + */ +static __isl_give isl_union_map *union_floyd_warshall( + __isl_take isl_union_map *umap, int *exact) +{ + int i, n; + isl_ctx *ctx; + isl_basic_map **list = NULL; + isl_basic_map **next; + isl_union_map *res; + + n = 0; + if (isl_union_map_foreach_map(umap, inc_count, &n) < 0) + goto error; + + ctx = isl_union_map_get_ctx(umap); + list = isl_calloc_array(ctx, isl_basic_map *, n); + if (!list) + goto error; + + next = list; + if (isl_union_map_foreach_map(umap, collect_basic_map, &next) < 0) + goto error; + + res = union_floyd_warshall_on_list(ctx, list, n, exact); + + if (list) { + for (i = 0; i < n; ++i) + isl_basic_map_free(list[i]); + free(list); + } + + isl_union_map_free(umap); + return res; +error: + if (list) { + for (i = 0; i < n; ++i) + isl_basic_map_free(list[i]); + free(list); + } + isl_union_map_free(umap); + return NULL; +} + +/* Decompose the give union relation into strongly connected components. + * The implementation is essentially the same as that of + * construct_power_components with the major difference that all + * operations are performed on union maps. + */ +static __isl_give isl_union_map *union_components( + __isl_take isl_union_map *umap, int *exact) +{ + int i; + int n; + isl_ctx *ctx; + isl_basic_map **list = NULL; + isl_basic_map **next; + isl_union_map *path = NULL; + struct isl_tc_follows_data data; + struct isl_tarjan_graph *g = NULL; + int c, l; + int recheck = 0; + + n = 0; + if (isl_union_map_foreach_map(umap, inc_count, &n) < 0) + goto error; + + if (n == 0) + return umap; + if (n <= 1) + return union_floyd_warshall(umap, exact); + + ctx = isl_union_map_get_ctx(umap); + list = isl_calloc_array(ctx, isl_basic_map *, n); + if (!list) + goto error; + + next = list; + if (isl_union_map_foreach_map(umap, collect_basic_map, &next) < 0) + goto error; + + data.list = list; + data.check_closed = 0; + g = isl_tarjan_graph_init(ctx, n, &basic_map_follows, &data); + if (!g) + goto error; + + c = 0; + i = 0; + l = n; + path = isl_union_map_empty(isl_union_map_get_space(umap)); + while (l) { + isl_union_map *comp; + isl_union_map *path_comp, *path_comb; + comp = isl_union_map_empty(isl_union_map_get_space(umap)); + while (g->order[i] != -1) { + comp = isl_union_map_add_map(comp, + isl_map_from_basic_map( + isl_basic_map_copy(list[g->order[i]]))); + --l; + ++i; + } + path_comp = union_floyd_warshall(comp, exact); + path_comb = isl_union_map_apply_range(isl_union_map_copy(path), + isl_union_map_copy(path_comp)); + path = isl_union_map_union(path, path_comp); + path = isl_union_map_union(path, path_comb); + ++i; + ++c; + } + + if (c > 1 && data.check_closed && !*exact) { + int closed; + + closed = isl_union_map_is_transitively_closed(path); + if (closed < 0) + goto error; + recheck = !closed; + } + + isl_tarjan_graph_free(g); + + for (i = 0; i < n; ++i) + isl_basic_map_free(list[i]); + free(list); + + if (recheck) { + isl_union_map_free(path); + return union_floyd_warshall(umap, exact); + } + + isl_union_map_free(umap); + + return path; +error: + isl_tarjan_graph_free(g); + if (list) { + for (i = 0; i < n; ++i) + isl_basic_map_free(list[i]); + free(list); + } + isl_union_map_free(umap); + isl_union_map_free(path); + return NULL; +} + +/* Compute the transitive closure of "umap", or an overapproximation. + * If the result is exact, then *exact is set to 1. + */ +__isl_give isl_union_map *isl_union_map_transitive_closure( + __isl_take isl_union_map *umap, int *exact) +{ + int closed; + + if (!umap) + return NULL; + + if (exact) + *exact = 1; + + umap = isl_union_map_compute_divs(umap); + umap = isl_union_map_coalesce(umap); + closed = isl_union_map_is_transitively_closed(umap); + if (closed < 0) + goto error; + if (closed) + return umap; + umap = union_components(umap, exact); + return umap; +error: + isl_union_map_free(umap); + return NULL; +} + +struct isl_union_power { + isl_union_map *pow; + int *exact; +}; + +static isl_stat power(__isl_take isl_map *map, void *user) +{ + struct isl_union_power *up = user; + + map = isl_map_power(map, up->exact); + up->pow = isl_union_map_from_map(map); + + return isl_stat_error; +} + +/* Construct a map [x] -> [x+1], with parameters prescribed by "dim". + */ +static __isl_give isl_union_map *increment(__isl_take isl_space *dim) +{ + int k; + isl_basic_map *bmap; + + dim = isl_space_add_dims(dim, isl_dim_in, 1); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + bmap = isl_basic_map_alloc_space(dim, 0, 1, 0); + k = isl_basic_map_alloc_equality(bmap); + if (k < 0) + goto error; + isl_seq_clr(bmap->eq[k], isl_basic_map_total_dim(bmap)); + isl_int_set_si(bmap->eq[k][0], 1); + isl_int_set_si(bmap->eq[k][isl_basic_map_offset(bmap, isl_dim_in)], 1); + isl_int_set_si(bmap->eq[k][isl_basic_map_offset(bmap, isl_dim_out)], -1); + return isl_union_map_from_map(isl_map_from_basic_map(bmap)); +error: + isl_basic_map_free(bmap); + return NULL; +} + +/* Construct a map [[x]->[y]] -> [y-x], with parameters prescribed by "dim". + */ +static __isl_give isl_union_map *deltas_map(__isl_take isl_space *dim) +{ + isl_basic_map *bmap; + + dim = isl_space_add_dims(dim, isl_dim_in, 1); + dim = isl_space_add_dims(dim, isl_dim_out, 1); + bmap = isl_basic_map_universe(dim); + bmap = isl_basic_map_deltas_map(bmap); + + return isl_union_map_from_map(isl_map_from_basic_map(bmap)); +} + +/* Compute the positive powers of "map", or an overapproximation. + * The result maps the exponent to a nested copy of the corresponding power. + * If the result is exact, then *exact is set to 1. + */ +__isl_give isl_union_map *isl_union_map_power(__isl_take isl_union_map *umap, + int *exact) +{ + int n; + isl_union_map *inc; + isl_union_map *dm; + + if (!umap) + return NULL; + n = isl_union_map_n_map(umap); + if (n == 0) + return umap; + if (n == 1) { + struct isl_union_power up = { NULL, exact }; + isl_union_map_foreach_map(umap, &power, &up); + isl_union_map_free(umap); + return up.pow; + } + inc = increment(isl_union_map_get_space(umap)); + umap = isl_union_map_product(inc, umap); + umap = isl_union_map_transitive_closure(umap, exact); + umap = isl_union_map_zip(umap); + dm = deltas_map(isl_union_map_get_space(umap)); + umap = isl_union_map_apply_domain(umap, dm); + + return umap; +} + +#undef TYPE +#define TYPE isl_map +#include "isl_power_templ.c" + +#undef TYPE +#define TYPE isl_union_map +#include "isl_power_templ.c" Index: lib/Analysis/isl/isl_union_eval.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_eval.c @@ -0,0 +1,58 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include + +/* Is the domain space of "entry" equal to "space"? + */ +static int FN(UNION,has_domain_space)(const void *entry, const void *val) +{ + PART *part = (PART *)entry; + isl_space *space = (isl_space *) val; + + if (isl_space_is_params(space)) + return isl_space_is_set(part->dim); + + return isl_space_tuple_is_equal(part->dim, isl_dim_in, + space, isl_dim_set); +} + +__isl_give isl_val *FN(UNION,eval)(__isl_take UNION *u, + __isl_take isl_point *pnt) +{ + uint32_t hash; + struct isl_hash_table_entry *entry; + isl_space *space; + isl_val *v; + + if (!u || !pnt) + goto error; + + space = isl_space_copy(pnt->dim); + if (!space) + goto error; + hash = isl_space_get_hash(space); + entry = isl_hash_table_find(u->space->ctx, &u->table, + hash, &FN(UNION,has_domain_space), + space, 0); + isl_space_free(space); + if (!entry) { + v = isl_val_zero(isl_point_get_ctx(pnt)); + isl_point_free(pnt); + } else { + v = FN(PART,eval)(FN(PART,copy)(entry->data), pnt); + } + FN(UNION,free)(u); + return v; +error: + FN(UNION,free)(u); + isl_point_free(pnt); + return NULL; +} Index: lib/Analysis/isl/isl_union_macro.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_macro.h @@ -0,0 +1,4 @@ +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) +#define xS(TYPE,NAME) struct TYPE ## _ ## NAME +#define S(TYPE,NAME) xS(TYPE,NAME) Index: lib/Analysis/isl/isl_union_map.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_map.c @@ -0,0 +1,3955 @@ +/* + * Copyright 2010-2011 INRIA Saclay + * Copyright 2013-2014 Ecole Normale Superieure + * Copyright 2014 INRIA Rocquencourt + * Copyright 2016 Sven Verdoolaege + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt, + * B.P. 105 - 78153 Le Chesnay, France + */ + +#define ISL_DIM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Return the number of parameters of "umap", where "type" + * is required to be set to isl_dim_param. + */ +unsigned isl_union_map_dim(__isl_keep isl_union_map *umap, + enum isl_dim_type type) +{ + if (!umap) + return 0; + + if (type != isl_dim_param) + isl_die(isl_union_map_get_ctx(umap), isl_error_invalid, + "can only reference parameters", return 0); + + return isl_space_dim(umap->dim, type); +} + +/* Return the number of parameters of "uset", where "type" + * is required to be set to isl_dim_param. + */ +unsigned isl_union_set_dim(__isl_keep isl_union_set *uset, + enum isl_dim_type type) +{ + return isl_union_map_dim(uset, type); +} + +/* Return the id of the specified dimension. + */ +__isl_give isl_id *isl_union_map_get_dim_id(__isl_keep isl_union_map *umap, + enum isl_dim_type type, unsigned pos) +{ + if (!umap) + return NULL; + + if (type != isl_dim_param) + isl_die(isl_union_map_get_ctx(umap), isl_error_invalid, + "can only reference parameters", return NULL); + + return isl_space_get_dim_id(umap->dim, type, pos); +} + +/* Is this union set a parameter domain? + */ +isl_bool isl_union_set_is_params(__isl_keep isl_union_set *uset) +{ + isl_set *set; + isl_bool params; + + if (!uset) + return isl_bool_error; + if (uset->table.n != 1) + return isl_bool_false; + + set = isl_set_from_union_set(isl_union_set_copy(uset)); + params = isl_set_is_params(set); + isl_set_free(set); + return params; +} + +static __isl_give isl_union_map *isl_union_map_alloc( + __isl_take isl_space *space, int size) +{ + isl_union_map *umap; + + space = isl_space_params(space); + if (!space) + return NULL; + + umap = isl_calloc_type(space->ctx, isl_union_map); + if (!umap) { + isl_space_free(space); + return NULL; + } + + umap->ref = 1; + umap->dim = space; + if (isl_hash_table_init(space->ctx, &umap->table, size) < 0) + return isl_union_map_free(umap); + + return umap; +} + +__isl_give isl_union_map *isl_union_map_empty(__isl_take isl_space *dim) +{ + return isl_union_map_alloc(dim, 16); +} + +__isl_give isl_union_set *isl_union_set_empty(__isl_take isl_space *dim) +{ + return isl_union_map_empty(dim); +} + +isl_ctx *isl_union_map_get_ctx(__isl_keep isl_union_map *umap) +{ + return umap ? umap->dim->ctx : NULL; +} + +isl_ctx *isl_union_set_get_ctx(__isl_keep isl_union_set *uset) +{ + return uset ? uset->dim->ctx : NULL; +} + +__isl_give isl_space *isl_union_map_get_space(__isl_keep isl_union_map *umap) +{ + if (!umap) + return NULL; + return isl_space_copy(umap->dim); +} + +/* Return the position of the parameter with the given name + * in "umap". + * Return -1 if no such dimension can be found. + */ +int isl_union_map_find_dim_by_name(__isl_keep isl_union_map *umap, + enum isl_dim_type type, const char *name) +{ + if (!umap) + return -1; + return isl_space_find_dim_by_name(umap->dim, type, name); +} + +__isl_give isl_space *isl_union_set_get_space(__isl_keep isl_union_set *uset) +{ + return isl_union_map_get_space(uset); +} + +static isl_stat free_umap_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_map_free(map); + return isl_stat_ok; +} + +static isl_stat add_map(__isl_take isl_map *map, void *user) +{ + isl_union_map **umap = (isl_union_map **)user; + + *umap = isl_union_map_add_map(*umap, map); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_dup(__isl_keep isl_union_map *umap) +{ + isl_union_map *dup; + + if (!umap) + return NULL; + + dup = isl_union_map_empty(isl_space_copy(umap->dim)); + if (isl_union_map_foreach_map(umap, &add_map, &dup) < 0) + goto error; + return dup; +error: + isl_union_map_free(dup); + return NULL; +} + +__isl_give isl_union_map *isl_union_map_cow(__isl_take isl_union_map *umap) +{ + if (!umap) + return NULL; + + if (umap->ref == 1) + return umap; + umap->ref--; + return isl_union_map_dup(umap); +} + +struct isl_union_align { + isl_reordering *exp; + isl_union_map *res; +}; + +static isl_stat align_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_reordering *exp; + struct isl_union_align *data = user; + + exp = isl_reordering_extend_space(isl_reordering_copy(data->exp), + isl_map_get_space(map)); + + data->res = isl_union_map_add_map(data->res, + isl_map_realign(isl_map_copy(map), exp)); + + return isl_stat_ok; +} + +/* Align the parameters of umap along those of model. + * The result has the parameters of model first, in the same order + * as they appear in model, followed by any remaining parameters of + * umap that do not appear in model. + */ +__isl_give isl_union_map *isl_union_map_align_params( + __isl_take isl_union_map *umap, __isl_take isl_space *model) +{ + struct isl_union_align data = { NULL, NULL }; + + if (!umap || !model) + goto error; + + if (isl_space_match(umap->dim, isl_dim_param, model, isl_dim_param)) { + isl_space_free(model); + return umap; + } + + model = isl_space_params(model); + data.exp = isl_parameter_alignment_reordering(umap->dim, model); + if (!data.exp) + goto error; + + data.res = isl_union_map_alloc(isl_space_copy(data.exp->dim), + umap->table.n); + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &align_entry, &data) < 0) + goto error; + + isl_reordering_free(data.exp); + isl_union_map_free(umap); + isl_space_free(model); + return data.res; +error: + isl_reordering_free(data.exp); + isl_union_map_free(umap); + isl_union_map_free(data.res); + isl_space_free(model); + return NULL; +} + +__isl_give isl_union_set *isl_union_set_align_params( + __isl_take isl_union_set *uset, __isl_take isl_space *model) +{ + return isl_union_map_align_params(uset, model); +} + +__isl_give isl_union_map *isl_union_map_union(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2) +{ + umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2)); + umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1)); + + umap1 = isl_union_map_cow(umap1); + + if (!umap1 || !umap2) + goto error; + + if (isl_union_map_foreach_map(umap2, &add_map, &umap1) < 0) + goto error; + + isl_union_map_free(umap2); + + return umap1; +error: + isl_union_map_free(umap1); + isl_union_map_free(umap2); + return NULL; +} + +__isl_give isl_union_set *isl_union_set_union(__isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2) +{ + return isl_union_map_union(uset1, uset2); +} + +__isl_give isl_union_map *isl_union_map_copy(__isl_keep isl_union_map *umap) +{ + if (!umap) + return NULL; + + umap->ref++; + return umap; +} + +__isl_give isl_union_set *isl_union_set_copy(__isl_keep isl_union_set *uset) +{ + return isl_union_map_copy(uset); +} + +__isl_null isl_union_map *isl_union_map_free(__isl_take isl_union_map *umap) +{ + if (!umap) + return NULL; + + if (--umap->ref > 0) + return NULL; + + isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &free_umap_entry, NULL); + isl_hash_table_clear(&umap->table); + isl_space_free(umap->dim); + free(umap); + return NULL; +} + +__isl_null isl_union_set *isl_union_set_free(__isl_take isl_union_set *uset) +{ + return isl_union_map_free(uset); +} + +static int has_dim(const void *entry, const void *val) +{ + isl_map *map = (isl_map *)entry; + isl_space *dim = (isl_space *)val; + + return isl_space_is_equal(map->dim, dim); +} + +__isl_give isl_union_map *isl_union_map_add_map(__isl_take isl_union_map *umap, + __isl_take isl_map *map) +{ + uint32_t hash; + struct isl_hash_table_entry *entry; + + if (!map || !umap) + goto error; + + if (isl_map_plain_is_empty(map)) { + isl_map_free(map); + return umap; + } + + if (!isl_space_match(map->dim, isl_dim_param, umap->dim, isl_dim_param)) { + umap = isl_union_map_align_params(umap, isl_map_get_space(map)); + map = isl_map_align_params(map, isl_union_map_get_space(umap)); + } + + umap = isl_union_map_cow(umap); + + if (!map || !umap) + goto error; + + hash = isl_space_get_hash(map->dim); + entry = isl_hash_table_find(umap->dim->ctx, &umap->table, hash, + &has_dim, map->dim, 1); + if (!entry) + goto error; + + if (!entry->data) + entry->data = map; + else { + entry->data = isl_map_union(entry->data, isl_map_copy(map)); + if (!entry->data) + goto error; + isl_map_free(map); + } + + return umap; +error: + isl_map_free(map); + isl_union_map_free(umap); + return NULL; +} + +__isl_give isl_union_set *isl_union_set_add_set(__isl_take isl_union_set *uset, + __isl_take isl_set *set) +{ + return isl_union_map_add_map(uset, (isl_map *)set); +} + +__isl_give isl_union_map *isl_union_map_from_map(__isl_take isl_map *map) +{ + isl_space *dim; + isl_union_map *umap; + + if (!map) + return NULL; + + dim = isl_map_get_space(map); + dim = isl_space_params(dim); + umap = isl_union_map_empty(dim); + umap = isl_union_map_add_map(umap, map); + + return umap; +} + +__isl_give isl_union_set *isl_union_set_from_set(__isl_take isl_set *set) +{ + return isl_union_map_from_map((isl_map *)set); +} + +__isl_give isl_union_map *isl_union_map_from_basic_map( + __isl_take isl_basic_map *bmap) +{ + return isl_union_map_from_map(isl_map_from_basic_map(bmap)); +} + +__isl_give isl_union_set *isl_union_set_from_basic_set( + __isl_take isl_basic_set *bset) +{ + return isl_union_map_from_basic_map(bset); +} + +struct isl_union_map_foreach_data +{ + isl_stat (*fn)(__isl_take isl_map *map, void *user); + void *user; +}; + +static isl_stat call_on_copy(void **entry, void *user) +{ + isl_map *map = *entry; + struct isl_union_map_foreach_data *data; + data = (struct isl_union_map_foreach_data *)user; + + return data->fn(isl_map_copy(map), data->user); +} + +int isl_union_map_n_map(__isl_keep isl_union_map *umap) +{ + return umap ? umap->table.n : 0; +} + +int isl_union_set_n_set(__isl_keep isl_union_set *uset) +{ + return uset ? uset->table.n : 0; +} + +isl_stat isl_union_map_foreach_map(__isl_keep isl_union_map *umap, + isl_stat (*fn)(__isl_take isl_map *map, void *user), void *user) +{ + struct isl_union_map_foreach_data data = { fn, user }; + + if (!umap) + return isl_stat_error; + + return isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &call_on_copy, &data); +} + +static isl_stat copy_map(void **entry, void *user) +{ + isl_map *map = *entry; + isl_map **map_p = user; + + *map_p = isl_map_copy(map); + + return isl_stat_error; +} + +__isl_give isl_map *isl_map_from_union_map(__isl_take isl_union_map *umap) +{ + isl_ctx *ctx; + isl_map *map = NULL; + + if (!umap) + return NULL; + ctx = isl_union_map_get_ctx(umap); + if (umap->table.n != 1) + isl_die(ctx, isl_error_invalid, + "union map needs to contain elements in exactly " + "one space", goto error); + + isl_hash_table_foreach(ctx, &umap->table, ©_map, &map); + + isl_union_map_free(umap); + + return map; +error: + isl_union_map_free(umap); + return NULL; +} + +__isl_give isl_set *isl_set_from_union_set(__isl_take isl_union_set *uset) +{ + return isl_map_from_union_map(uset); +} + +/* Extract the map in "umap" that lives in the given space (ignoring + * parameters). + */ +__isl_give isl_map *isl_union_map_extract_map(__isl_keep isl_union_map *umap, + __isl_take isl_space *space) +{ + uint32_t hash; + struct isl_hash_table_entry *entry; + + space = isl_space_drop_dims(space, isl_dim_param, + 0, isl_space_dim(space, isl_dim_param)); + space = isl_space_align_params(space, isl_union_map_get_space(umap)); + if (!umap || !space) + goto error; + + hash = isl_space_get_hash(space); + entry = isl_hash_table_find(umap->dim->ctx, &umap->table, hash, + &has_dim, space, 0); + if (!entry) + return isl_map_empty(space); + isl_space_free(space); + return isl_map_copy(entry->data); +error: + isl_space_free(space); + return NULL; +} + +__isl_give isl_set *isl_union_set_extract_set(__isl_keep isl_union_set *uset, + __isl_take isl_space *dim) +{ + return (isl_set *)isl_union_map_extract_map(uset, dim); +} + +/* Check if umap contains a map in the given space. + */ +__isl_give int isl_union_map_contains(__isl_keep isl_union_map *umap, + __isl_keep isl_space *dim) +{ + uint32_t hash; + struct isl_hash_table_entry *entry; + + if (!umap || !dim) + return -1; + + hash = isl_space_get_hash(dim); + entry = isl_hash_table_find(umap->dim->ctx, &umap->table, hash, + &has_dim, dim, 0); + return !!entry; +} + +__isl_give int isl_union_set_contains(__isl_keep isl_union_set *uset, + __isl_keep isl_space *dim) +{ + return isl_union_map_contains(uset, dim); +} + +isl_stat isl_union_set_foreach_set(__isl_keep isl_union_set *uset, + isl_stat (*fn)(__isl_take isl_set *set, void *user), void *user) +{ + return isl_union_map_foreach_map(uset, + (isl_stat(*)(__isl_take isl_map *, void*))fn, user); +} + +struct isl_union_set_foreach_point_data { + isl_stat (*fn)(__isl_take isl_point *pnt, void *user); + void *user; +}; + +static isl_stat foreach_point(__isl_take isl_set *set, void *user) +{ + struct isl_union_set_foreach_point_data *data = user; + isl_stat r; + + r = isl_set_foreach_point(set, data->fn, data->user); + isl_set_free(set); + + return r; +} + +isl_stat isl_union_set_foreach_point(__isl_keep isl_union_set *uset, + isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user) +{ + struct isl_union_set_foreach_point_data data = { fn, user }; + return isl_union_set_foreach_set(uset, &foreach_point, &data); +} + +struct isl_union_map_gen_bin_data { + isl_union_map *umap2; + isl_union_map *res; +}; + +static isl_stat subtract_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_map *map = *entry; + + hash = isl_space_get_hash(map->dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, map->dim, 0); + map = isl_map_copy(map); + if (entry2) { + int empty; + map = isl_map_subtract(map, isl_map_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + if (empty) { + isl_map_free(map); + return isl_stat_ok; + } + } + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +static __isl_give isl_union_map *gen_bin_op(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2, isl_stat (*fn)(void **, void *)) +{ + struct isl_union_map_gen_bin_data data = { NULL, NULL }; + + umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2)); + umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1)); + + if (!umap1 || !umap2) + goto error; + + data.umap2 = umap2; + data.res = isl_union_map_alloc(isl_space_copy(umap1->dim), + umap1->table.n); + if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table, + fn, &data) < 0) + goto error; + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + return data.res; +error: + isl_union_map_free(umap1); + isl_union_map_free(umap2); + isl_union_map_free(data.res); + return NULL; +} + +__isl_give isl_union_map *isl_union_map_subtract( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return gen_bin_op(umap1, umap2, &subtract_entry); +} + +__isl_give isl_union_set *isl_union_set_subtract( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2) +{ + return isl_union_map_subtract(uset1, uset2); +} + +struct isl_union_map_gen_bin_set_data { + isl_set *set; + isl_union_map *res; +}; + +static isl_stat intersect_params_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_set_data *data = user; + isl_map *map = *entry; + int empty; + + map = isl_map_copy(map); + map = isl_map_intersect_params(map, isl_set_copy(data->set)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +static __isl_give isl_union_map *gen_bin_set_op(__isl_take isl_union_map *umap, + __isl_take isl_set *set, isl_stat (*fn)(void **, void *)) +{ + struct isl_union_map_gen_bin_set_data data = { NULL, NULL }; + + umap = isl_union_map_align_params(umap, isl_set_get_space(set)); + set = isl_set_align_params(set, isl_union_map_get_space(umap)); + + if (!umap || !set) + goto error; + + data.set = set; + data.res = isl_union_map_alloc(isl_space_copy(umap->dim), + umap->table.n); + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, + fn, &data) < 0) + goto error; + + isl_union_map_free(umap); + isl_set_free(set); + return data.res; +error: + isl_union_map_free(umap); + isl_set_free(set); + isl_union_map_free(data.res); + return NULL; +} + +/* Intersect "umap" with the parameter domain "set". + * + * If "set" does not have any constraints, then we can return immediately. + */ +__isl_give isl_union_map *isl_union_map_intersect_params( + __isl_take isl_union_map *umap, __isl_take isl_set *set) +{ + int is_universe; + + is_universe = isl_set_plain_is_universe(set); + if (is_universe < 0) + goto error; + if (is_universe) { + isl_set_free(set); + return umap; + } + + return gen_bin_set_op(umap, set, &intersect_params_entry); +error: + isl_union_map_free(umap); + isl_set_free(set); + return NULL; +} + +__isl_give isl_union_set *isl_union_set_intersect_params( + __isl_take isl_union_set *uset, __isl_take isl_set *set) +{ + return isl_union_map_intersect_params(uset, set); +} + +static __isl_give isl_union_map *union_map_intersect_params( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset) +{ + return isl_union_map_intersect_params(umap, + isl_set_from_union_set(uset)); +} + +static __isl_give isl_union_map *union_map_gist_params( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset) +{ + return isl_union_map_gist_params(umap, isl_set_from_union_set(uset)); +} + +struct isl_union_map_match_bin_data { + isl_union_map *umap2; + isl_union_map *res; + __isl_give isl_map *(*fn)(__isl_take isl_map*, __isl_take isl_map*); +}; + +static isl_stat match_bin_entry(void **entry, void *user) +{ + struct isl_union_map_match_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_map *map = *entry; + int empty; + + hash = isl_space_get_hash(map->dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, map->dim, 0); + if (!entry2) + return isl_stat_ok; + + map = isl_map_copy(map); + map = data->fn(map, isl_map_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + if (empty) { + isl_map_free(map); + return isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +static __isl_give isl_union_map *match_bin_op(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2, + __isl_give isl_map *(*fn)(__isl_take isl_map*, __isl_take isl_map*)) +{ + struct isl_union_map_match_bin_data data = { NULL, NULL, fn }; + + umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2)); + umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1)); + + if (!umap1 || !umap2) + goto error; + + data.umap2 = umap2; + data.res = isl_union_map_alloc(isl_space_copy(umap1->dim), + umap1->table.n); + if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table, + &match_bin_entry, &data) < 0) + goto error; + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + return data.res; +error: + isl_union_map_free(umap1); + isl_union_map_free(umap2); + isl_union_map_free(data.res); + return NULL; +} + +__isl_give isl_union_map *isl_union_map_intersect( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return match_bin_op(umap1, umap2, &isl_map_intersect); +} + +/* Compute the intersection of the two union_sets. + * As a special case, if exactly one of the two union_sets + * is a parameter domain, then intersect the parameter domain + * of the other one with this set. + */ +__isl_give isl_union_set *isl_union_set_intersect( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2) +{ + int p1, p2; + + p1 = isl_union_set_is_params(uset1); + p2 = isl_union_set_is_params(uset2); + if (p1 < 0 || p2 < 0) + goto error; + if (!p1 && p2) + return union_map_intersect_params(uset1, uset2); + if (p1 && !p2) + return union_map_intersect_params(uset2, uset1); + return isl_union_map_intersect(uset1, uset2); +error: + isl_union_set_free(uset1); + isl_union_set_free(uset2); + return NULL; +} + +static isl_stat gist_params_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_set_data *data = user; + isl_map *map = *entry; + int empty; + + map = isl_map_copy(map); + map = isl_map_gist_params(map, isl_set_copy(data->set)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_gist_params( + __isl_take isl_union_map *umap, __isl_take isl_set *set) +{ + return gen_bin_set_op(umap, set, &gist_params_entry); +} + +__isl_give isl_union_set *isl_union_set_gist_params( + __isl_take isl_union_set *uset, __isl_take isl_set *set) +{ + return isl_union_map_gist_params(uset, set); +} + +__isl_give isl_union_map *isl_union_map_gist(__isl_take isl_union_map *umap, + __isl_take isl_union_map *context) +{ + return match_bin_op(umap, context, &isl_map_gist); +} + +__isl_give isl_union_set *isl_union_set_gist(__isl_take isl_union_set *uset, + __isl_take isl_union_set *context) +{ + if (isl_union_set_is_params(context)) + return union_map_gist_params(uset, context); + return isl_union_map_gist(uset, context); +} + +static __isl_give isl_map *lex_le_set(__isl_take isl_map *set1, + __isl_take isl_map *set2) +{ + return isl_set_lex_le_set((isl_set *)set1, (isl_set *)set2); +} + +static __isl_give isl_map *lex_lt_set(__isl_take isl_map *set1, + __isl_take isl_map *set2) +{ + return isl_set_lex_lt_set((isl_set *)set1, (isl_set *)set2); +} + +__isl_give isl_union_map *isl_union_set_lex_lt_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2) +{ + return match_bin_op(uset1, uset2, &lex_lt_set); +} + +__isl_give isl_union_map *isl_union_set_lex_le_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2) +{ + return match_bin_op(uset1, uset2, &lex_le_set); +} + +__isl_give isl_union_map *isl_union_set_lex_gt_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2) +{ + return isl_union_map_reverse(isl_union_set_lex_lt_union_set(uset2, uset1)); +} + +__isl_give isl_union_map *isl_union_set_lex_ge_union_set( + __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2) +{ + return isl_union_map_reverse(isl_union_set_lex_le_union_set(uset2, uset1)); +} + +__isl_give isl_union_map *isl_union_map_lex_gt_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return isl_union_map_reverse(isl_union_map_lex_lt_union_map(umap2, umap1)); +} + +__isl_give isl_union_map *isl_union_map_lex_ge_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return isl_union_map_reverse(isl_union_map_lex_le_union_map(umap2, umap1)); +} + +static isl_stat intersect_domain_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *dim; + isl_map *map = *entry; + isl_bool empty; + + dim = isl_map_get_space(map); + dim = isl_space_domain(dim); + hash = isl_space_get_hash(dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, dim, 0); + isl_space_free(dim); + if (!entry2) + return isl_stat_ok; + + map = isl_map_copy(map); + map = isl_map_intersect_domain(map, isl_set_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + if (empty) { + isl_map_free(map); + return isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Intersect the domain of "umap" with "uset". + * If "uset" is a parameters domain, then intersect the parameter + * domain of "umap" with this set. + */ +__isl_give isl_union_map *isl_union_map_intersect_domain( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset) +{ + if (isl_union_set_is_params(uset)) + return union_map_intersect_params(umap, uset); + return gen_bin_op(umap, uset, &intersect_domain_entry); +} + +/* Remove the elements of data->umap2 from the domain of *entry + * and add the result to data->res. + */ +static isl_stat subtract_domain_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *dim; + isl_map *map = *entry; + isl_bool empty; + + dim = isl_map_get_space(map); + dim = isl_space_domain(dim); + hash = isl_space_get_hash(dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, dim, 0); + isl_space_free(dim); + + map = isl_map_copy(map); + + if (!entry2) { + data->res = isl_union_map_add_map(data->res, map); + return isl_stat_ok; + } + + map = isl_map_subtract_domain(map, isl_set_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + if (empty) { + isl_map_free(map); + return isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Remove the elements of "uset" from the domain of "umap". + */ +__isl_give isl_union_map *isl_union_map_subtract_domain( + __isl_take isl_union_map *umap, __isl_take isl_union_set *dom) +{ + return gen_bin_op(umap, dom, &subtract_domain_entry); +} + +/* Remove the elements of data->umap2 from the range of *entry + * and add the result to data->res. + */ +static isl_stat subtract_range_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *space; + isl_map *map = *entry; + isl_bool empty; + + space = isl_map_get_space(map); + space = isl_space_range(space); + hash = isl_space_get_hash(space); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, space, 0); + isl_space_free(space); + + map = isl_map_copy(map); + + if (!entry2) { + data->res = isl_union_map_add_map(data->res, map); + return isl_stat_ok; + } + + map = isl_map_subtract_range(map, isl_set_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + if (empty) { + isl_map_free(map); + return isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Remove the elements of "uset" from the range of "umap". + */ +__isl_give isl_union_map *isl_union_map_subtract_range( + __isl_take isl_union_map *umap, __isl_take isl_union_set *dom) +{ + return gen_bin_op(umap, dom, &subtract_range_entry); +} + +static isl_stat gist_domain_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *dim; + isl_map *map = *entry; + isl_bool empty; + + dim = isl_map_get_space(map); + dim = isl_space_domain(dim); + hash = isl_space_get_hash(dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, dim, 0); + isl_space_free(dim); + if (!entry2) + return isl_stat_ok; + + map = isl_map_copy(map); + map = isl_map_gist_domain(map, isl_set_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Compute the gist of "umap" with respect to the domain "uset". + * If "uset" is a parameters domain, then compute the gist + * with respect to this parameter domain. + */ +__isl_give isl_union_map *isl_union_map_gist_domain( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset) +{ + if (isl_union_set_is_params(uset)) + return union_map_gist_params(umap, uset); + return gen_bin_op(umap, uset, &gist_domain_entry); +} + +static isl_stat gist_range_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *space; + isl_map *map = *entry; + isl_bool empty; + + space = isl_map_get_space(map); + space = isl_space_range(space); + hash = isl_space_get_hash(space); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, space, 0); + isl_space_free(space); + if (!entry2) + return isl_stat_ok; + + map = isl_map_copy(map); + map = isl_map_gist_range(map, isl_set_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Compute the gist of "umap" with respect to the range "uset". + */ +__isl_give isl_union_map *isl_union_map_gist_range( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset) +{ + return gen_bin_op(umap, uset, &gist_range_entry); +} + +static isl_stat intersect_range_entry(void **entry, void *user) +{ + struct isl_union_map_gen_bin_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *dim; + isl_map *map = *entry; + isl_bool empty; + + dim = isl_map_get_space(map); + dim = isl_space_range(dim); + hash = isl_space_get_hash(dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, dim, 0); + isl_space_free(dim); + if (!entry2) + return isl_stat_ok; + + map = isl_map_copy(map); + map = isl_map_intersect_range(map, isl_set_copy(entry2->data)); + + empty = isl_map_is_empty(map); + if (empty < 0) { + isl_map_free(map); + return isl_stat_error; + } + if (empty) { + isl_map_free(map); + return isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_intersect_range( + __isl_take isl_union_map *umap, __isl_take isl_union_set *uset) +{ + return gen_bin_op(umap, uset, &intersect_range_entry); +} + +struct isl_union_map_bin_data { + isl_union_map *umap2; + isl_union_map *res; + isl_map *map; + isl_stat (*fn)(void **entry, void *user); +}; + +static isl_stat apply_range_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + isl_bool empty; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_out, + map2->dim, isl_dim_in)) + return isl_stat_ok; + + map2 = isl_map_apply_range(isl_map_copy(data->map), isl_map_copy(map2)); + + empty = isl_map_is_empty(map2); + if (empty < 0) { + isl_map_free(map2); + return isl_stat_error; + } + if (empty) { + isl_map_free(map2); + return isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +static isl_stat bin_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map = *entry; + + data->map = map; + if (isl_hash_table_foreach(data->umap2->dim->ctx, &data->umap2->table, + data->fn, data) < 0) + return isl_stat_error; + + return isl_stat_ok; +} + +static __isl_give isl_union_map *bin_op(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2, + isl_stat (*fn)(void **entry, void *user)) +{ + struct isl_union_map_bin_data data = { NULL, NULL, NULL, fn }; + + umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2)); + umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1)); + + if (!umap1 || !umap2) + goto error; + + data.umap2 = umap2; + data.res = isl_union_map_alloc(isl_space_copy(umap1->dim), + umap1->table.n); + if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table, + &bin_entry, &data) < 0) + goto error; + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + return data.res; +error: + isl_union_map_free(umap1); + isl_union_map_free(umap2); + isl_union_map_free(data.res); + return NULL; +} + +__isl_give isl_union_map *isl_union_map_apply_range( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &apply_range_entry); +} + +__isl_give isl_union_map *isl_union_map_apply_domain( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + umap1 = isl_union_map_reverse(umap1); + umap1 = isl_union_map_apply_range(umap1, umap2); + return isl_union_map_reverse(umap1); +} + +__isl_give isl_union_set *isl_union_set_apply( + __isl_take isl_union_set *uset, __isl_take isl_union_map *umap) +{ + return isl_union_map_apply_range(uset, umap); +} + +static isl_stat map_lex_lt_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_out, + map2->dim, isl_dim_out)) + return isl_stat_ok; + + map2 = isl_map_lex_lt_map(isl_map_copy(data->map), isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_lex_lt_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &map_lex_lt_entry); +} + +static isl_stat map_lex_le_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_out, + map2->dim, isl_dim_out)) + return isl_stat_ok; + + map2 = isl_map_lex_le_map(isl_map_copy(data->map), isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_lex_le_union_map( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &map_lex_le_entry); +} + +static isl_stat product_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + map2 = isl_map_product(isl_map_copy(data->map), isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_product(__isl_take isl_union_map *umap1, + __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &product_entry); +} + +static isl_stat set_product_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_set *set2 = *entry; + + set2 = isl_set_product(isl_set_copy(data->map), isl_set_copy(set2)); + + data->res = isl_union_set_add_set(data->res, set2); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_set_product(__isl_take isl_union_set *uset1, + __isl_take isl_union_set *uset2) +{ + return bin_op(uset1, uset2, &set_product_entry); +} + +static isl_stat domain_product_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_out, + map2->dim, isl_dim_out)) + return isl_stat_ok; + + map2 = isl_map_domain_product(isl_map_copy(data->map), + isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +/* Given two maps A -> B and C -> D, construct a map [A -> C] -> (B * D) + */ +__isl_give isl_union_map *isl_union_map_domain_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &domain_product_entry); +} + +static isl_stat range_product_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_in, + map2->dim, isl_dim_in)) + return isl_stat_ok; + + map2 = isl_map_range_product(isl_map_copy(data->map), + isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_range_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &range_product_entry); +} + +/* If data->map A -> B and "map2" C -> D have the same range space, + * then add (A, C) -> (B * D) to data->res. + */ +static isl_stat flat_domain_product_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_out, + map2->dim, isl_dim_out)) + return isl_stat_ok; + + map2 = isl_map_flat_domain_product(isl_map_copy(data->map), + isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +/* Given two maps A -> B and C -> D, construct a map (A, C) -> (B * D). + */ +__isl_give isl_union_map *isl_union_map_flat_domain_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &flat_domain_product_entry); +} + +static isl_stat flat_range_product_entry(void **entry, void *user) +{ + struct isl_union_map_bin_data *data = user; + isl_map *map2 = *entry; + + if (!isl_space_tuple_is_equal(data->map->dim, isl_dim_in, + map2->dim, isl_dim_in)) + return isl_stat_ok; + + map2 = isl_map_flat_range_product(isl_map_copy(data->map), + isl_map_copy(map2)); + + data->res = isl_union_map_add_map(data->res, map2); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_flat_range_product( + __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2) +{ + return bin_op(umap1, umap2, &flat_range_product_entry); +} + +static __isl_give isl_union_set *cond_un_op(__isl_take isl_union_map *umap, + isl_stat (*fn)(void **, void *)) +{ + isl_union_set *res; + + if (!umap) + return NULL; + + res = isl_union_map_alloc(isl_space_copy(umap->dim), umap->table.n); + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, fn, &res) < 0) + goto error; + + isl_union_map_free(umap); + return res; +error: + isl_union_map_free(umap); + isl_union_set_free(res); + return NULL; +} + +static isl_stat from_range_entry(void **entry, void *user) +{ + isl_map *set = *entry; + isl_union_set **res = user; + + *res = isl_union_map_add_map(*res, + isl_map_from_range(isl_set_copy(set))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_from_range( + __isl_take isl_union_set *uset) +{ + return cond_un_op(uset, &from_range_entry); +} + +__isl_give isl_union_map *isl_union_map_from_domain( + __isl_take isl_union_set *uset) +{ + return isl_union_map_reverse(isl_union_map_from_range(uset)); +} + +__isl_give isl_union_map *isl_union_map_from_domain_and_range( + __isl_take isl_union_set *domain, __isl_take isl_union_set *range) +{ + return isl_union_map_apply_range(isl_union_map_from_domain(domain), + isl_union_map_from_range(range)); +} + +static __isl_give isl_union_map *un_op(__isl_take isl_union_map *umap, + isl_stat (*fn)(void **, void *)) +{ + umap = isl_union_map_cow(umap); + if (!umap) + return NULL; + + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, fn, NULL) < 0) + goto error; + + return umap; +error: + isl_union_map_free(umap); + return NULL; +} + +static isl_stat affine_entry(void **entry, void *user) +{ + isl_map **map = (isl_map **)entry; + + *map = isl_map_from_basic_map(isl_map_affine_hull(*map)); + + return *map ? isl_stat_ok : isl_stat_error; +} + +__isl_give isl_union_map *isl_union_map_affine_hull( + __isl_take isl_union_map *umap) +{ + return un_op(umap, &affine_entry); +} + +__isl_give isl_union_set *isl_union_set_affine_hull( + __isl_take isl_union_set *uset) +{ + return isl_union_map_affine_hull(uset); +} + +static isl_stat polyhedral_entry(void **entry, void *user) +{ + isl_map **map = (isl_map **)entry; + + *map = isl_map_from_basic_map(isl_map_polyhedral_hull(*map)); + + return *map ? isl_stat_ok : isl_stat_error; +} + +__isl_give isl_union_map *isl_union_map_polyhedral_hull( + __isl_take isl_union_map *umap) +{ + return un_op(umap, &polyhedral_entry); +} + +__isl_give isl_union_set *isl_union_set_polyhedral_hull( + __isl_take isl_union_set *uset) +{ + return isl_union_map_polyhedral_hull(uset); +} + +static isl_stat simple_entry(void **entry, void *user) +{ + isl_map **map = (isl_map **)entry; + + *map = isl_map_from_basic_map(isl_map_simple_hull(*map)); + + return *map ? isl_stat_ok : isl_stat_error; +} + +__isl_give isl_union_map *isl_union_map_simple_hull( + __isl_take isl_union_map *umap) +{ + return un_op(umap, &simple_entry); +} + +__isl_give isl_union_set *isl_union_set_simple_hull( + __isl_take isl_union_set *uset) +{ + return isl_union_map_simple_hull(uset); +} + +static isl_stat inplace_entry(void **entry, void *user) +{ + __isl_give isl_map *(*fn)(__isl_take isl_map *); + isl_map **map = (isl_map **)entry; + isl_map *copy; + + fn = *(__isl_give isl_map *(**)(__isl_take isl_map *)) user; + copy = fn(isl_map_copy(*map)); + if (!copy) + return isl_stat_error; + + isl_map_free(*map); + *map = copy; + + return isl_stat_ok; +} + +static __isl_give isl_union_map *inplace(__isl_take isl_union_map *umap, + __isl_give isl_map *(*fn)(__isl_take isl_map *)) +{ + if (!umap) + return NULL; + + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &inplace_entry, &fn) < 0) + goto error; + + return umap; +error: + isl_union_map_free(umap); + return NULL; +} + +/* Remove redundant constraints in each of the basic maps of "umap". + * Since removing redundant constraints does not change the meaning + * or the space, the operation can be performed in-place. + */ +__isl_give isl_union_map *isl_union_map_remove_redundancies( + __isl_take isl_union_map *umap) +{ + return inplace(umap, &isl_map_remove_redundancies); +} + +/* Remove redundant constraints in each of the basic sets of "uset". + */ +__isl_give isl_union_set *isl_union_set_remove_redundancies( + __isl_take isl_union_set *uset) +{ + return isl_union_map_remove_redundancies(uset); +} + +__isl_give isl_union_map *isl_union_map_coalesce( + __isl_take isl_union_map *umap) +{ + return inplace(umap, &isl_map_coalesce); +} + +__isl_give isl_union_set *isl_union_set_coalesce( + __isl_take isl_union_set *uset) +{ + return isl_union_map_coalesce(uset); +} + +__isl_give isl_union_map *isl_union_map_detect_equalities( + __isl_take isl_union_map *umap) +{ + return inplace(umap, &isl_map_detect_equalities); +} + +__isl_give isl_union_set *isl_union_set_detect_equalities( + __isl_take isl_union_set *uset) +{ + return isl_union_map_detect_equalities(uset); +} + +__isl_give isl_union_map *isl_union_map_compute_divs( + __isl_take isl_union_map *umap) +{ + return inplace(umap, &isl_map_compute_divs); +} + +__isl_give isl_union_set *isl_union_set_compute_divs( + __isl_take isl_union_set *uset) +{ + return isl_union_map_compute_divs(uset); +} + +static isl_stat lexmin_entry(void **entry, void *user) +{ + isl_map **map = (isl_map **)entry; + + *map = isl_map_lexmin(*map); + + return *map ? isl_stat_ok : isl_stat_error; +} + +__isl_give isl_union_map *isl_union_map_lexmin( + __isl_take isl_union_map *umap) +{ + return un_op(umap, &lexmin_entry); +} + +__isl_give isl_union_set *isl_union_set_lexmin( + __isl_take isl_union_set *uset) +{ + return isl_union_map_lexmin(uset); +} + +static isl_stat lexmax_entry(void **entry, void *user) +{ + isl_map **map = (isl_map **)entry; + + *map = isl_map_lexmax(*map); + + return *map ? isl_stat_ok : isl_stat_error; +} + +__isl_give isl_union_map *isl_union_map_lexmax( + __isl_take isl_union_map *umap) +{ + return un_op(umap, &lexmax_entry); +} + +__isl_give isl_union_set *isl_union_set_lexmax( + __isl_take isl_union_set *uset) +{ + return isl_union_map_lexmax(uset); +} + +static isl_stat universe_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + map = isl_map_universe(isl_map_get_space(map)); + *res = isl_union_map_add_map(*res, map); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_universe(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &universe_entry); +} + +__isl_give isl_union_set *isl_union_set_universe(__isl_take isl_union_set *uset) +{ + return isl_union_map_universe(uset); +} + +static isl_stat reverse_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + *res = isl_union_map_add_map(*res, isl_map_reverse(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_reverse(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &reverse_entry); +} + +static isl_stat params_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + *res = isl_union_set_add_set(*res, isl_map_params(isl_map_copy(map))); + + return isl_stat_ok; +} + +/* Compute the parameter domain of the given union map. + */ +__isl_give isl_set *isl_union_map_params(__isl_take isl_union_map *umap) +{ + int empty; + + empty = isl_union_map_is_empty(umap); + if (empty < 0) + goto error; + if (empty) { + isl_space *space; + space = isl_union_map_get_space(umap); + isl_union_map_free(umap); + return isl_set_empty(space); + } + return isl_set_from_union_set(cond_un_op(umap, ¶ms_entry)); +error: + isl_union_map_free(umap); + return NULL; +} + +/* Compute the parameter domain of the given union set. + */ +__isl_give isl_set *isl_union_set_params(__isl_take isl_union_set *uset) +{ + return isl_union_map_params(uset); +} + +static isl_stat domain_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + *res = isl_union_set_add_set(*res, isl_map_domain(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_map_domain(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &domain_entry); +} + +static isl_stat range_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + *res = isl_union_set_add_set(*res, isl_map_range(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_map_range(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &range_entry); +} + +static isl_stat domain_map_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + *res = isl_union_map_add_map(*res, + isl_map_domain_map(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_domain_map( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &domain_map_entry); +} + +/* Construct an isl_pw_multi_aff that maps "map" to its domain and + * add the result to "res". + */ +static isl_stat domain_map_upma(__isl_take isl_map *map, void *user) +{ + isl_union_pw_multi_aff **res = user; + isl_multi_aff *ma; + isl_pw_multi_aff *pma; + + ma = isl_multi_aff_domain_map(isl_map_get_space(map)); + pma = isl_pw_multi_aff_alloc(isl_map_wrap(map), ma); + *res = isl_union_pw_multi_aff_add_pw_multi_aff(*res, pma); + + return *res ? isl_stat_ok : isl_stat_error; + +} + +/* Return an isl_union_pw_multi_aff that maps a wrapped copy of "umap" + * to its domain. + */ +__isl_give isl_union_pw_multi_aff *isl_union_map_domain_map_union_pw_multi_aff( + __isl_take isl_union_map *umap) +{ + isl_union_pw_multi_aff *res; + + res = isl_union_pw_multi_aff_empty(isl_union_map_get_space(umap)); + if (isl_union_map_foreach_map(umap, &domain_map_upma, &res) < 0) + res = isl_union_pw_multi_aff_free(res); + + isl_union_map_free(umap); + return res; +} + +static isl_stat range_map_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + *res = isl_union_map_add_map(*res, + isl_map_range_map(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_range_map( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &range_map_entry); +} + +/* Check if "set" is of the form A[B -> C]. + * If so, add A[B -> C] -> B to "res". + */ +static isl_stat wrapped_domain_map_entry(void **entry, void *user) +{ + isl_set *set = *entry; + isl_union_set **res = user; + int wrapping; + + wrapping = isl_set_is_wrapping(set); + if (wrapping < 0) + return isl_stat_error; + if (!wrapping) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_set_wrapped_domain_map(isl_set_copy(set))); + + return isl_stat_ok; +} + +/* Given a collection of wrapped maps of the form A[B -> C], + * return the collection of maps A[B -> C] -> B. + */ +__isl_give isl_union_map *isl_union_set_wrapped_domain_map( + __isl_take isl_union_set *uset) +{ + return cond_un_op(uset, &wrapped_domain_map_entry); +} + +static isl_stat deltas_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + if (!isl_space_tuple_is_equal(map->dim, isl_dim_in, + map->dim, isl_dim_out)) + return isl_stat_ok; + + *res = isl_union_set_add_set(*res, isl_map_deltas(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_map_deltas(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &deltas_entry); +} + +static isl_stat deltas_map_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_space_tuple_is_equal(map->dim, isl_dim_in, + map->dim, isl_dim_out)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_deltas_map(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_deltas_map( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &deltas_map_entry); +} + +static isl_stat identity_entry(void **entry, void *user) +{ + isl_set *set = *entry; + isl_union_map **res = user; + + *res = isl_union_map_add_map(*res, isl_set_identity(isl_set_copy(set))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_set_identity(__isl_take isl_union_set *uset) +{ + return cond_un_op(uset, &identity_entry); +} + +/* Construct an identity isl_pw_multi_aff on "set" and add it to *res. + */ +static isl_stat identity_upma(__isl_take isl_set *set, void *user) +{ + isl_union_pw_multi_aff **res = user; + isl_space *space; + isl_pw_multi_aff *pma; + + space = isl_space_map_from_set(isl_set_get_space(set)); + pma = isl_pw_multi_aff_identity(space); + pma = isl_pw_multi_aff_intersect_domain(pma, set); + *res = isl_union_pw_multi_aff_add_pw_multi_aff(*res, pma); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* Return an identity function on "uset" in the form + * of an isl_union_pw_multi_aff. + */ +__isl_give isl_union_pw_multi_aff *isl_union_set_identity_union_pw_multi_aff( + __isl_take isl_union_set *uset) +{ + isl_union_pw_multi_aff *res; + + res = isl_union_pw_multi_aff_empty(isl_union_set_get_space(uset)); + if (isl_union_set_foreach_set(uset, &identity_upma, &res) < 0) + res = isl_union_pw_multi_aff_free(res); + + isl_union_set_free(uset); + return res; +} + +/* If "map" is of the form [A -> B] -> C, then add A -> C to "res". + */ +static isl_stat domain_factor_domain_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_domain_is_wrapping(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_domain_factor_domain(isl_map_copy(map))); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* For each map in "umap" of the form [A -> B] -> C, + * construct the map A -> C and collect the results. + */ +__isl_give isl_union_map *isl_union_map_domain_factor_domain( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &domain_factor_domain_entry); +} + +/* If "map" is of the form [A -> B] -> C, then add B -> C to "res". + */ +static isl_stat domain_factor_range_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_domain_is_wrapping(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_domain_factor_range(isl_map_copy(map))); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* For each map in "umap" of the form [A -> B] -> C, + * construct the map B -> C and collect the results. + */ +__isl_give isl_union_map *isl_union_map_domain_factor_range( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &domain_factor_range_entry); +} + +/* If "map" is of the form A -> [B -> C], then add A -> B to "res". + */ +static isl_stat range_factor_domain_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_range_is_wrapping(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_range_factor_domain(isl_map_copy(map))); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* For each map in "umap" of the form A -> [B -> C], + * construct the map A -> B and collect the results. + */ +__isl_give isl_union_map *isl_union_map_range_factor_domain( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &range_factor_domain_entry); +} + +/* If "map" is of the form A -> [B -> C], then add A -> C to "res". + */ +static isl_stat range_factor_range_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_range_is_wrapping(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_range_factor_range(isl_map_copy(map))); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* For each map in "umap" of the form A -> [B -> C], + * construct the map A -> C and collect the results. + */ +__isl_give isl_union_map *isl_union_map_range_factor_range( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &range_factor_range_entry); +} + +/* If "map" is of the form [A -> B] -> [C -> D], then add A -> C to "res". + */ +static isl_stat factor_domain_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_domain_is_wrapping(map) || !isl_map_range_is_wrapping(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_factor_domain(isl_map_copy(map))); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* For each map in "umap" of the form [A -> B] -> [C -> D], + * construct the map A -> C and collect the results. + */ +__isl_give isl_union_map *isl_union_map_factor_domain( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &factor_domain_entry); +} + +/* If "map" is of the form [A -> B] -> [C -> D], then add B -> D to "res". + */ +static isl_stat factor_range_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_domain_is_wrapping(map) || !isl_map_range_is_wrapping(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, + isl_map_factor_range(isl_map_copy(map))); + + return *res ? isl_stat_ok : isl_stat_error; +} + +/* For each map in "umap" of the form [A -> B] -> [C -> D], + * construct the map B -> D and collect the results. + */ +__isl_give isl_union_map *isl_union_map_factor_range( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &factor_range_entry); +} + +static isl_stat unwrap_entry(void **entry, void *user) +{ + isl_set *set = *entry; + isl_union_set **res = user; + + if (!isl_set_is_wrapping(set)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, isl_set_unwrap(isl_set_copy(set))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_set_unwrap(__isl_take isl_union_set *uset) +{ + return cond_un_op(uset, &unwrap_entry); +} + +static isl_stat wrap_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_set **res = user; + + *res = isl_union_set_add_set(*res, isl_map_wrap(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_map_wrap(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &wrap_entry); +} + +struct isl_union_map_is_subset_data { + isl_union_map *umap2; + isl_bool is_subset; +}; + +static isl_stat is_subset_entry(void **entry, void *user) +{ + struct isl_union_map_is_subset_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_map *map = *entry; + + hash = isl_space_get_hash(map->dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, map->dim, 0); + if (!entry2) { + int empty = isl_map_is_empty(map); + if (empty < 0) + return isl_stat_error; + if (empty) + return isl_stat_ok; + data->is_subset = 0; + return isl_stat_error; + } + + data->is_subset = isl_map_is_subset(map, entry2->data); + if (data->is_subset < 0 || !data->is_subset) + return isl_stat_error; + + return isl_stat_ok; +} + +isl_bool isl_union_map_is_subset(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2) +{ + struct isl_union_map_is_subset_data data = { NULL, isl_bool_true }; + + umap1 = isl_union_map_copy(umap1); + umap2 = isl_union_map_copy(umap2); + umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2)); + umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1)); + + if (!umap1 || !umap2) + goto error; + + data.umap2 = umap2; + if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table, + &is_subset_entry, &data) < 0 && + data.is_subset) + goto error; + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + + return data.is_subset; +error: + isl_union_map_free(umap1); + isl_union_map_free(umap2); + return isl_bool_error; +} + +isl_bool isl_union_set_is_subset(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2) +{ + return isl_union_map_is_subset(uset1, uset2); +} + +isl_bool isl_union_map_is_equal(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2) +{ + isl_bool is_subset; + + if (!umap1 || !umap2) + return isl_bool_error; + is_subset = isl_union_map_is_subset(umap1, umap2); + if (is_subset != isl_bool_true) + return is_subset; + is_subset = isl_union_map_is_subset(umap2, umap1); + return is_subset; +} + +isl_bool isl_union_set_is_equal(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2) +{ + return isl_union_map_is_equal(uset1, uset2); +} + +isl_bool isl_union_map_is_strict_subset(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2) +{ + isl_bool is_subset; + + if (!umap1 || !umap2) + return isl_bool_error; + is_subset = isl_union_map_is_subset(umap1, umap2); + if (is_subset != isl_bool_true) + return is_subset; + is_subset = isl_union_map_is_subset(umap2, umap1); + if (is_subset == isl_bool_error) + return is_subset; + return !is_subset; +} + +isl_bool isl_union_set_is_strict_subset(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2) +{ + return isl_union_map_is_strict_subset(uset1, uset2); +} + +/* Internal data structure for isl_union_map_is_disjoint. + * umap2 is the union map with which we are comparing. + * is_disjoint is initialized to 1 and is set to 0 as soon + * as the union maps turn out not to be disjoint. + */ +struct isl_union_map_is_disjoint_data { + isl_union_map *umap2; + isl_bool is_disjoint; +}; + +/* Check if "map" is disjoint from data->umap2 and abort + * the search if it is not. + */ +static isl_stat is_disjoint_entry(void **entry, void *user) +{ + struct isl_union_map_is_disjoint_data *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_map *map = *entry; + + hash = isl_space_get_hash(map->dim); + entry2 = isl_hash_table_find(data->umap2->dim->ctx, &data->umap2->table, + hash, &has_dim, map->dim, 0); + if (!entry2) + return isl_stat_ok; + + data->is_disjoint = isl_map_is_disjoint(map, entry2->data); + if (data->is_disjoint < 0 || !data->is_disjoint) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Are "umap1" and "umap2" disjoint? + */ +isl_bool isl_union_map_is_disjoint(__isl_keep isl_union_map *umap1, + __isl_keep isl_union_map *umap2) +{ + struct isl_union_map_is_disjoint_data data = { NULL, isl_bool_true }; + + umap1 = isl_union_map_copy(umap1); + umap2 = isl_union_map_copy(umap2); + umap1 = isl_union_map_align_params(umap1, + isl_union_map_get_space(umap2)); + umap2 = isl_union_map_align_params(umap2, + isl_union_map_get_space(umap1)); + + if (!umap1 || !umap2) + goto error; + + data.umap2 = umap2; + if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table, + &is_disjoint_entry, &data) < 0 && + data.is_disjoint) + goto error; + + isl_union_map_free(umap1); + isl_union_map_free(umap2); + + return data.is_disjoint; +error: + isl_union_map_free(umap1); + isl_union_map_free(umap2); + return isl_bool_error; +} + +/* Are "uset1" and "uset2" disjoint? + */ +isl_bool isl_union_set_is_disjoint(__isl_keep isl_union_set *uset1, + __isl_keep isl_union_set *uset2) +{ + return isl_union_map_is_disjoint(uset1, uset2); +} + +static isl_stat sample_entry(void **entry, void *user) +{ + isl_basic_map **sample = (isl_basic_map **)user; + isl_map *map = *entry; + + *sample = isl_map_sample(isl_map_copy(map)); + if (!*sample) + return isl_stat_error; + if (!isl_basic_map_plain_is_empty(*sample)) + return isl_stat_error; + return isl_stat_ok; +} + +__isl_give isl_basic_map *isl_union_map_sample(__isl_take isl_union_map *umap) +{ + isl_basic_map *sample = NULL; + + if (!umap) + return NULL; + + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &sample_entry, &sample) < 0 && + !sample) + goto error; + + if (!sample) + sample = isl_basic_map_empty(isl_union_map_get_space(umap)); + + isl_union_map_free(umap); + + return sample; +error: + isl_union_map_free(umap); + return NULL; +} + +__isl_give isl_basic_set *isl_union_set_sample(__isl_take isl_union_set *uset) +{ + return (isl_basic_set *)isl_union_map_sample(uset); +} + +/* Return an element in "uset" in the form of an isl_point. + * Return a void isl_point if "uset" is empty. + */ +__isl_give isl_point *isl_union_set_sample_point(__isl_take isl_union_set *uset) +{ + return isl_basic_set_sample_point(isl_union_set_sample(uset)); +} + +struct isl_forall_data { + isl_bool res; + isl_bool (*fn)(__isl_keep isl_map *map); +}; + +static isl_stat forall_entry(void **entry, void *user) +{ + struct isl_forall_data *data = user; + isl_map *map = *entry; + + data->res = data->fn(map); + if (data->res < 0) + return isl_stat_error; + + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +static isl_bool union_map_forall(__isl_keep isl_union_map *umap, + isl_bool (*fn)(__isl_keep isl_map *map)) +{ + struct isl_forall_data data = { isl_bool_true, fn }; + + if (!umap) + return isl_bool_error; + + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &forall_entry, &data) < 0 && data.res) + return isl_bool_error; + + return data.res; +} + +struct isl_forall_user_data { + isl_bool res; + isl_bool (*fn)(__isl_keep isl_map *map, void *user); + void *user; +}; + +static isl_stat forall_user_entry(void **entry, void *user) +{ + struct isl_forall_user_data *data = user; + isl_map *map = *entry; + + data->res = data->fn(map, data->user); + if (data->res < 0) + return isl_stat_error; + + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Check if fn(map, user) returns true for all maps "map" in umap. + */ +static isl_bool union_map_forall_user(__isl_keep isl_union_map *umap, + isl_bool (*fn)(__isl_keep isl_map *map, void *user), void *user) +{ + struct isl_forall_user_data data = { isl_bool_true, fn, user }; + + if (!umap) + return isl_bool_error; + + if (isl_hash_table_foreach(umap->dim->ctx, &umap->table, + &forall_user_entry, &data) < 0 && data.res) + return isl_bool_error; + + return data.res; +} + +isl_bool isl_union_map_is_empty(__isl_keep isl_union_map *umap) +{ + return union_map_forall(umap, &isl_map_is_empty); +} + +isl_bool isl_union_set_is_empty(__isl_keep isl_union_set *uset) +{ + return isl_union_map_is_empty(uset); +} + +static isl_bool is_subset_of_identity(__isl_keep isl_map *map) +{ + isl_bool is_subset; + isl_space *dim; + isl_map *id; + + if (!map) + return isl_bool_error; + + if (!isl_space_tuple_is_equal(map->dim, isl_dim_in, + map->dim, isl_dim_out)) + return isl_bool_false; + + dim = isl_map_get_space(map); + id = isl_map_identity(dim); + + is_subset = isl_map_is_subset(map, id); + + isl_map_free(id); + + return is_subset; +} + +/* Given an isl_union_map that consists of a single map, check + * if it is single-valued. + */ +static isl_bool single_map_is_single_valued(__isl_keep isl_union_map *umap) +{ + isl_map *map; + isl_bool sv; + + umap = isl_union_map_copy(umap); + map = isl_map_from_union_map(umap); + sv = isl_map_is_single_valued(map); + isl_map_free(map); + + return sv; +} + +/* Internal data structure for single_valued_on_domain. + * + * "umap" is the union map to be tested. + * "sv" is set to 1 as long as "umap" may still be single-valued. + */ +struct isl_union_map_is_sv_data { + isl_union_map *umap; + isl_bool sv; +}; + +/* Check if the data->umap is single-valued on "set". + * + * If data->umap consists of a single map on "set", then test it + * as an isl_map. + * + * Otherwise, compute + * + * M \circ M^-1 + * + * check if the result is a subset of the identity mapping and + * store the result in data->sv. + * + * Terminate as soon as data->umap has been determined not to + * be single-valued. + */ +static isl_stat single_valued_on_domain(__isl_take isl_set *set, void *user) +{ + struct isl_union_map_is_sv_data *data = user; + isl_union_map *umap, *test; + + umap = isl_union_map_copy(data->umap); + umap = isl_union_map_intersect_domain(umap, + isl_union_set_from_set(set)); + + if (isl_union_map_n_map(umap) == 1) { + data->sv = single_map_is_single_valued(umap); + isl_union_map_free(umap); + } else { + test = isl_union_map_reverse(isl_union_map_copy(umap)); + test = isl_union_map_apply_range(test, umap); + + data->sv = union_map_forall(test, &is_subset_of_identity); + + isl_union_map_free(test); + } + + if (data->sv < 0 || !data->sv) + return isl_stat_error; + return isl_stat_ok; +} + +/* Check if the given map is single-valued. + * + * If the union map consists of a single map, then test it as an isl_map. + * Otherwise, check if the union map is single-valued on each of its + * domain spaces. + */ +isl_bool isl_union_map_is_single_valued(__isl_keep isl_union_map *umap) +{ + isl_union_map *universe; + isl_union_set *domain; + struct isl_union_map_is_sv_data data; + + if (isl_union_map_n_map(umap) == 1) + return single_map_is_single_valued(umap); + + universe = isl_union_map_universe(isl_union_map_copy(umap)); + domain = isl_union_map_domain(universe); + + data.sv = isl_bool_true; + data.umap = umap; + if (isl_union_set_foreach_set(domain, + &single_valued_on_domain, &data) < 0 && data.sv) + data.sv = isl_bool_error; + isl_union_set_free(domain); + + return data.sv; +} + +isl_bool isl_union_map_is_injective(__isl_keep isl_union_map *umap) +{ + isl_bool in; + + umap = isl_union_map_copy(umap); + umap = isl_union_map_reverse(umap); + in = isl_union_map_is_single_valued(umap); + isl_union_map_free(umap); + + return in; +} + +/* Is "map" obviously not an identity relation because + * it maps elements from one space to another space? + * Update *non_identity accordingly. + * + * In particular, if the domain and range spaces are the same, + * then the map is not considered to obviously not be an identity relation. + * Otherwise, the map is considered to obviously not be an identity relation + * if it is is non-empty. + * + * If "map" is determined to obviously not be an identity relation, + * then the search is aborted. + */ +static isl_stat map_plain_is_not_identity(__isl_take isl_map *map, void *user) +{ + isl_bool *non_identity = user; + isl_bool equal; + isl_space *space; + + space = isl_map_get_space(map); + equal = isl_space_tuple_is_equal(space, isl_dim_in, space, isl_dim_out); + if (equal >= 0 && !equal) + *non_identity = isl_bool_not(isl_map_is_empty(map)); + else + *non_identity = isl_bool_not(equal); + isl_space_free(space); + isl_map_free(map); + + if (*non_identity < 0 || *non_identity) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Is "umap" obviously not an identity relation because + * it maps elements from one space to another space? + * + * As soon as a map has been found that maps elements to a different space, + * non_identity is changed and the search is aborted. + */ +static isl_bool isl_union_map_plain_is_not_identity( + __isl_keep isl_union_map *umap) +{ + isl_bool non_identity; + + non_identity = isl_bool_false; + if (isl_union_map_foreach_map(umap, &map_plain_is_not_identity, + &non_identity) < 0 && + non_identity == isl_bool_false) + return isl_bool_error; + + return non_identity; +} + +/* Does "map" only map elements to themselves? + * Update *identity accordingly. + * + * If "map" is determined not to be an identity relation, + * then the search is aborted. + */ +static isl_stat map_is_identity(__isl_take isl_map *map, void *user) +{ + isl_bool *identity = user; + + *identity = isl_map_is_identity(map); + isl_map_free(map); + + if (*identity < 0 || !*identity) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Does "umap" only map elements to themselves? + * + * First check if there are any maps that map elements to different spaces. + * If not, then check that all the maps (between identical spaces) + * are identity relations. + */ +isl_bool isl_union_map_is_identity(__isl_keep isl_union_map *umap) +{ + isl_bool non_identity; + isl_bool identity; + + non_identity = isl_union_map_plain_is_not_identity(umap); + if (non_identity < 0 || non_identity) + return isl_bool_not(non_identity); + + identity = isl_bool_true; + if (isl_union_map_foreach_map(umap, &map_is_identity, &identity) < 0 && + identity == isl_bool_true) + return isl_bool_error; + + return identity; +} + +/* Represents a map that has a fixed value (v) for one of its + * range dimensions. + * The map in this structure is not reference counted, so it + * is only valid while the isl_union_map from which it was + * obtained is still alive. + */ +struct isl_fixed_map { + isl_int v; + isl_map *map; +}; + +static struct isl_fixed_map *alloc_isl_fixed_map_array(isl_ctx *ctx, + int n) +{ + int i; + struct isl_fixed_map *v; + + v = isl_calloc_array(ctx, struct isl_fixed_map, n); + if (!v) + return NULL; + for (i = 0; i < n; ++i) + isl_int_init(v[i].v); + return v; +} + +static void free_isl_fixed_map_array(struct isl_fixed_map *v, int n) +{ + int i; + + if (!v) + return; + for (i = 0; i < n; ++i) + isl_int_clear(v[i].v); + free(v); +} + +/* Compare the "v" field of two isl_fixed_map structs. + */ +static int qsort_fixed_map_cmp(const void *p1, const void *p2) +{ + const struct isl_fixed_map *e1 = (const struct isl_fixed_map *) p1; + const struct isl_fixed_map *e2 = (const struct isl_fixed_map *) p2; + + return isl_int_cmp(e1->v, e2->v); +} + +/* Internal data structure used while checking whether all maps + * in a union_map have a fixed value for a given output dimension. + * v is the list of maps, with the fixed value for the dimension + * n is the number of maps considered so far + * pos is the output dimension under investigation + */ +struct isl_fixed_dim_data { + struct isl_fixed_map *v; + int n; + int pos; +}; + +static isl_bool fixed_at_pos(__isl_keep isl_map *map, void *user) +{ + struct isl_fixed_dim_data *data = user; + + data->v[data->n].map = map; + return isl_map_plain_is_fixed(map, isl_dim_out, data->pos, + &data->v[data->n++].v); +} + +static isl_bool plain_injective_on_range(__isl_take isl_union_map *umap, + int first, int n_range); + +/* Given a list of the maps, with their fixed values at output dimension "pos", + * check whether the ranges of the maps form an obvious partition. + * + * We first sort the maps according to their fixed values. + * If all maps have a different value, then we know the ranges form + * a partition. + * Otherwise, we collect the maps with the same fixed value and + * check whether each such collection is obviously injective + * based on later dimensions. + */ +static int separates(struct isl_fixed_map *v, int n, + __isl_take isl_space *dim, int pos, int n_range) +{ + int i; + + if (!v) + goto error; + + qsort(v, n, sizeof(*v), &qsort_fixed_map_cmp); + + for (i = 0; i + 1 < n; ++i) { + int j, k; + isl_union_map *part; + int injective; + + for (j = i + 1; j < n; ++j) + if (isl_int_ne(v[i].v, v[j].v)) + break; + + if (j == i + 1) + continue; + + part = isl_union_map_alloc(isl_space_copy(dim), j - i); + for (k = i; k < j; ++k) + part = isl_union_map_add_map(part, + isl_map_copy(v[k].map)); + + injective = plain_injective_on_range(part, pos + 1, n_range); + if (injective < 0) + goto error; + if (!injective) + break; + + i = j - 1; + } + + isl_space_free(dim); + free_isl_fixed_map_array(v, n); + return i + 1 >= n; +error: + isl_space_free(dim); + free_isl_fixed_map_array(v, n); + return -1; +} + +/* Check whether the maps in umap have obviously distinct ranges. + * In particular, check for an output dimension in the range + * [first,n_range) for which all maps have a fixed value + * and then check if these values, possibly along with fixed values + * at later dimensions, entail distinct ranges. + */ +static isl_bool plain_injective_on_range(__isl_take isl_union_map *umap, + int first, int n_range) +{ + isl_ctx *ctx; + int n; + struct isl_fixed_dim_data data = { NULL }; + + ctx = isl_union_map_get_ctx(umap); + + n = isl_union_map_n_map(umap); + if (!umap) + goto error; + + if (n <= 1) { + isl_union_map_free(umap); + return isl_bool_true; + } + + if (first >= n_range) { + isl_union_map_free(umap); + return isl_bool_false; + } + + data.v = alloc_isl_fixed_map_array(ctx, n); + if (!data.v) + goto error; + + for (data.pos = first; data.pos < n_range; ++data.pos) { + isl_bool fixed; + int injective; + isl_space *dim; + + data.n = 0; + fixed = union_map_forall_user(umap, &fixed_at_pos, &data); + if (fixed < 0) + goto error; + if (!fixed) + continue; + dim = isl_union_map_get_space(umap); + injective = separates(data.v, n, dim, data.pos, n_range); + isl_union_map_free(umap); + return injective; + } + + free_isl_fixed_map_array(data.v, n); + isl_union_map_free(umap); + + return isl_bool_false; +error: + free_isl_fixed_map_array(data.v, n); + isl_union_map_free(umap); + return isl_bool_error; +} + +/* Check whether the maps in umap that map to subsets of "ran" + * have obviously distinct ranges. + */ +static isl_bool plain_injective_on_range_wrap(__isl_keep isl_set *ran, + void *user) +{ + isl_union_map *umap = user; + + umap = isl_union_map_copy(umap); + umap = isl_union_map_intersect_range(umap, + isl_union_set_from_set(isl_set_copy(ran))); + return plain_injective_on_range(umap, 0, isl_set_dim(ran, isl_dim_set)); +} + +/* Check if the given union_map is obviously injective. + * + * In particular, we first check if all individual maps are obviously + * injective and then check if all the ranges of these maps are + * obviously disjoint. + */ +isl_bool isl_union_map_plain_is_injective(__isl_keep isl_union_map *umap) +{ + isl_bool in; + isl_union_map *univ; + isl_union_set *ran; + + in = union_map_forall(umap, &isl_map_plain_is_injective); + if (in < 0) + return isl_bool_error; + if (!in) + return isl_bool_false; + + univ = isl_union_map_universe(isl_union_map_copy(umap)); + ran = isl_union_map_range(univ); + + in = union_map_forall_user(ran, &plain_injective_on_range_wrap, umap); + + isl_union_set_free(ran); + + return in; +} + +isl_bool isl_union_map_is_bijective(__isl_keep isl_union_map *umap) +{ + isl_bool sv; + + sv = isl_union_map_is_single_valued(umap); + if (sv < 0 || !sv) + return sv; + + return isl_union_map_is_injective(umap); +} + +static isl_stat zip_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_can_zip(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, isl_map_zip(isl_map_copy(map))); + + return isl_stat_ok; +} + +__isl_give isl_union_map *isl_union_map_zip(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &zip_entry); +} + +static isl_stat uncurry_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_can_uncurry(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, isl_map_uncurry(isl_map_copy(map))); + + return isl_stat_ok; +} + +/* Given a union map, take the maps of the form A -> (B -> C) and + * return the union of the corresponding maps (A -> B) -> C. + */ +__isl_give isl_union_map *isl_union_map_uncurry(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &uncurry_entry); +} + +static isl_stat curry_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_can_curry(map)) + return isl_stat_ok; + + *res = isl_union_map_add_map(*res, isl_map_curry(isl_map_copy(map))); + + return isl_stat_ok; +} + +/* Given a union map, take the maps of the form (A -> B) -> C and + * return the union of the corresponding maps A -> (B -> C). + */ +__isl_give isl_union_map *isl_union_map_curry(__isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &curry_entry); +} + +/* If *entry is of the form A -> ((B -> C) -> D), then apply + * isl_map_range_curry to it and add the result to *res. + */ +static isl_stat range_curry_entry(void **entry, void *user) +{ + isl_map *map = *entry; + isl_union_map **res = user; + + if (!isl_map_can_range_curry(map)) + return isl_stat_ok; + + map = isl_map_range_curry(isl_map_copy(map)); + *res = isl_union_map_add_map(*res, map); + + return isl_stat_ok; +} + +/* Given a union map, take the maps of the form A -> ((B -> C) -> D) and + * return the union of the corresponding maps A -> (B -> (C -> D)). + */ +__isl_give isl_union_map *isl_union_map_range_curry( + __isl_take isl_union_map *umap) +{ + return cond_un_op(umap, &range_curry_entry); +} + +static isl_stat lift_entry(void **entry, void *user) +{ + isl_set *set = *entry; + isl_union_set **res = user; + + *res = isl_union_set_add_set(*res, isl_set_lift(isl_set_copy(set))); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_set_lift(__isl_take isl_union_set *uset) +{ + return cond_un_op(uset, &lift_entry); +} + +static isl_stat coefficients_entry(void **entry, void *user) +{ + isl_set *set = *entry; + isl_union_set **res = user; + + set = isl_set_copy(set); + set = isl_set_from_basic_set(isl_set_coefficients(set)); + *res = isl_union_set_add_set(*res, set); + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_set_coefficients( + __isl_take isl_union_set *uset) +{ + isl_ctx *ctx; + isl_space *dim; + isl_union_set *res; + + if (!uset) + return NULL; + + ctx = isl_union_set_get_ctx(uset); + dim = isl_space_set_alloc(ctx, 0, 0); + res = isl_union_map_alloc(dim, uset->table.n); + if (isl_hash_table_foreach(uset->dim->ctx, &uset->table, + &coefficients_entry, &res) < 0) + goto error; + + isl_union_set_free(uset); + return res; +error: + isl_union_set_free(uset); + isl_union_set_free(res); + return NULL; +} + +static isl_stat solutions_entry(void **entry, void *user) +{ + isl_set *set = *entry; + isl_union_set **res = user; + + set = isl_set_copy(set); + set = isl_set_from_basic_set(isl_set_solutions(set)); + if (!*res) + *res = isl_union_set_from_set(set); + else + *res = isl_union_set_add_set(*res, set); + + if (!*res) + return isl_stat_error; + + return isl_stat_ok; +} + +__isl_give isl_union_set *isl_union_set_solutions( + __isl_take isl_union_set *uset) +{ + isl_union_set *res = NULL; + + if (!uset) + return NULL; + + if (uset->table.n == 0) { + res = isl_union_set_empty(isl_union_set_get_space(uset)); + isl_union_set_free(uset); + return res; + } + + if (isl_hash_table_foreach(uset->dim->ctx, &uset->table, + &solutions_entry, &res) < 0) + goto error; + + isl_union_set_free(uset); + return res; +error: + isl_union_set_free(uset); + isl_union_set_free(res); + return NULL; +} + +/* Is the domain space of "map" equal to "space"? + */ +static int domain_match(__isl_keep isl_map *map, __isl_keep isl_space *space) +{ + return isl_space_tuple_is_equal(map->dim, isl_dim_in, + space, isl_dim_out); +} + +/* Is the range space of "map" equal to "space"? + */ +static int range_match(__isl_keep isl_map *map, __isl_keep isl_space *space) +{ + return isl_space_tuple_is_equal(map->dim, isl_dim_out, + space, isl_dim_out); +} + +/* Is the set space of "map" equal to "space"? + */ +static int set_match(__isl_keep isl_map *map, __isl_keep isl_space *space) +{ + return isl_space_tuple_is_equal(map->dim, isl_dim_set, + space, isl_dim_out); +} + +/* Internal data structure for preimage_pw_multi_aff. + * + * "pma" is the function under which the preimage should be taken. + * "space" is the space of "pma". + * "res" collects the results. + * "fn" computes the preimage for a given map. + * "match" returns true if "fn" can be called. + */ +struct isl_union_map_preimage_data { + isl_space *space; + isl_pw_multi_aff *pma; + isl_union_map *res; + int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space); + __isl_give isl_map *(*fn)(__isl_take isl_map *map, + __isl_take isl_pw_multi_aff *pma); +}; + +/* Call data->fn to compute the preimage of the domain or range of *entry + * under the function represented by data->pma, provided the domain/range + * space of *entry matches the target space of data->pma + * (as given by data->match), and add the result to data->res. + */ +static isl_stat preimage_entry(void **entry, void *user) +{ + int m; + isl_map *map = *entry; + struct isl_union_map_preimage_data *data = user; + isl_bool empty; + + m = data->match(map, data->space); + if (m < 0) + return isl_stat_error; + if (!m) + return isl_stat_ok; + + map = isl_map_copy(map); + map = data->fn(map, isl_pw_multi_aff_copy(data->pma)); + + empty = isl_map_is_empty(map); + if (empty < 0 || empty) { + isl_map_free(map); + return empty < 0 ? isl_stat_error : isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Compute the preimage of the domain or range of "umap" under the function + * represented by "pma". + * In other words, plug in "pma" in the domain or range of "umap". + * The function "fn" performs the actual preimage computation on a map, + * while "match" determines to which maps the function should be applied. + */ +static __isl_give isl_union_map *preimage_pw_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma, + int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space), + __isl_give isl_map *(*fn)(__isl_take isl_map *map, + __isl_take isl_pw_multi_aff *pma)) +{ + isl_ctx *ctx; + isl_space *space; + struct isl_union_map_preimage_data data; + + umap = isl_union_map_align_params(umap, + isl_pw_multi_aff_get_space(pma)); + pma = isl_pw_multi_aff_align_params(pma, isl_union_map_get_space(umap)); + + if (!umap || !pma) + goto error; + + ctx = isl_union_map_get_ctx(umap); + space = isl_union_map_get_space(umap); + data.space = isl_pw_multi_aff_get_space(pma); + data.pma = pma; + data.res = isl_union_map_alloc(space, umap->table.n); + data.match = match; + data.fn = fn; + if (isl_hash_table_foreach(ctx, &umap->table, &preimage_entry, + &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_space_free(data.space); + isl_union_map_free(umap); + isl_pw_multi_aff_free(pma); + return data.res; +error: + isl_union_map_free(umap); + isl_pw_multi_aff_free(pma); + return NULL; +} + +/* Compute the preimage of the domain of "umap" under the function + * represented by "pma". + * In other words, plug in "pma" in the domain of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with domain space equal to the target space of "pma", + * except that the domain has been replaced by the domain space of "pma". + */ +__isl_give isl_union_map *isl_union_map_preimage_domain_pw_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma) +{ + return preimage_pw_multi_aff(umap, pma, &domain_match, + &isl_map_preimage_domain_pw_multi_aff); +} + +/* Compute the preimage of the range of "umap" under the function + * represented by "pma". + * In other words, plug in "pma" in the range of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with range space equal to the target space of "pma", + * except that the range has been replaced by the domain space of "pma". + */ +__isl_give isl_union_map *isl_union_map_preimage_range_pw_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma) +{ + return preimage_pw_multi_aff(umap, pma, &range_match, + &isl_map_preimage_range_pw_multi_aff); +} + +/* Compute the preimage of "uset" under the function represented by "pma". + * In other words, plug in "pma" in "uset". + * The result contains sets that live in the same spaces as the sets of "uset" + * with space equal to the target space of "pma", + * except that the space has been replaced by the domain space of "pma". + */ +__isl_give isl_union_set *isl_union_set_preimage_pw_multi_aff( + __isl_take isl_union_set *uset, __isl_take isl_pw_multi_aff *pma) +{ + return preimage_pw_multi_aff(uset, pma, &set_match, + &isl_set_preimage_pw_multi_aff); +} + +/* Compute the preimage of the domain of "umap" under the function + * represented by "ma". + * In other words, plug in "ma" in the domain of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with domain space equal to the target space of "ma", + * except that the domain has been replaced by the domain space of "ma". + */ +__isl_give isl_union_map *isl_union_map_preimage_domain_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma) +{ + return isl_union_map_preimage_domain_pw_multi_aff(umap, + isl_pw_multi_aff_from_multi_aff(ma)); +} + +/* Compute the preimage of the range of "umap" under the function + * represented by "ma". + * In other words, plug in "ma" in the range of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with range space equal to the target space of "ma", + * except that the range has been replaced by the domain space of "ma". + */ +__isl_give isl_union_map *isl_union_map_preimage_range_multi_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma) +{ + return isl_union_map_preimage_range_pw_multi_aff(umap, + isl_pw_multi_aff_from_multi_aff(ma)); +} + +/* Compute the preimage of "uset" under the function represented by "ma". + * In other words, plug in "ma" in "uset". + * The result contains sets that live in the same spaces as the sets of "uset" + * with space equal to the target space of "ma", + * except that the space has been replaced by the domain space of "ma". + */ +__isl_give isl_union_map *isl_union_set_preimage_multi_aff( + __isl_take isl_union_set *uset, __isl_take isl_multi_aff *ma) +{ + return isl_union_set_preimage_pw_multi_aff(uset, + isl_pw_multi_aff_from_multi_aff(ma)); +} + +/* Internal data structure for preimage_multi_pw_aff. + * + * "mpa" is the function under which the preimage should be taken. + * "space" is the space of "mpa". + * "res" collects the results. + * "fn" computes the preimage for a given map. + * "match" returns true if "fn" can be called. + */ +struct isl_union_map_preimage_mpa_data { + isl_space *space; + isl_multi_pw_aff *mpa; + isl_union_map *res; + int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space); + __isl_give isl_map *(*fn)(__isl_take isl_map *map, + __isl_take isl_multi_pw_aff *mpa); +}; + +/* Call data->fn to compute the preimage of the domain or range of *entry + * under the function represented by data->mpa, provided the domain/range + * space of *entry matches the target space of data->mpa + * (as given by data->match), and add the result to data->res. + */ +static isl_stat preimage_mpa_entry(void **entry, void *user) +{ + int m; + isl_map *map = *entry; + struct isl_union_map_preimage_mpa_data *data = user; + isl_bool empty; + + m = data->match(map, data->space); + if (m < 0) + return isl_stat_error; + if (!m) + return isl_stat_ok; + + map = isl_map_copy(map); + map = data->fn(map, isl_multi_pw_aff_copy(data->mpa)); + + empty = isl_map_is_empty(map); + if (empty < 0 || empty) { + isl_map_free(map); + return empty < 0 ? isl_stat_error : isl_stat_ok; + } + + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Compute the preimage of the domain or range of "umap" under the function + * represented by "mpa". + * In other words, plug in "mpa" in the domain or range of "umap". + * The function "fn" performs the actual preimage computation on a map, + * while "match" determines to which maps the function should be applied. + */ +static __isl_give isl_union_map *preimage_multi_pw_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_pw_aff *mpa, + int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space), + __isl_give isl_map *(*fn)(__isl_take isl_map *map, + __isl_take isl_multi_pw_aff *mpa)) +{ + isl_ctx *ctx; + isl_space *space; + struct isl_union_map_preimage_mpa_data data; + + umap = isl_union_map_align_params(umap, + isl_multi_pw_aff_get_space(mpa)); + mpa = isl_multi_pw_aff_align_params(mpa, isl_union_map_get_space(umap)); + + if (!umap || !mpa) + goto error; + + ctx = isl_union_map_get_ctx(umap); + space = isl_union_map_get_space(umap); + data.space = isl_multi_pw_aff_get_space(mpa); + data.mpa = mpa; + data.res = isl_union_map_alloc(space, umap->table.n); + data.match = match; + data.fn = fn; + if (isl_hash_table_foreach(ctx, &umap->table, &preimage_mpa_entry, + &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_space_free(data.space); + isl_union_map_free(umap); + isl_multi_pw_aff_free(mpa); + return data.res; +error: + isl_union_map_free(umap); + isl_multi_pw_aff_free(mpa); + return NULL; +} + +/* Compute the preimage of the domain of "umap" under the function + * represented by "mpa". + * In other words, plug in "mpa" in the domain of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with domain space equal to the target space of "mpa", + * except that the domain has been replaced by the domain space of "mpa". + */ +__isl_give isl_union_map *isl_union_map_preimage_domain_multi_pw_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_pw_aff *mpa) +{ + return preimage_multi_pw_aff(umap, mpa, &domain_match, + &isl_map_preimage_domain_multi_pw_aff); +} + +/* Internal data structure for preimage_upma. + * + * "umap" is the map of which the preimage should be computed. + * "res" collects the results. + * "fn" computes the preimage for a given piecewise multi-affine function. + */ +struct isl_union_map_preimage_upma_data { + isl_union_map *umap; + isl_union_map *res; + __isl_give isl_union_map *(*fn)(__isl_take isl_union_map *umap, + __isl_take isl_pw_multi_aff *pma); +}; + +/* Call data->fn to compute the preimage of the domain or range of data->umap + * under the function represented by pma and add the result to data->res. + */ +static isl_stat preimage_upma(__isl_take isl_pw_multi_aff *pma, void *user) +{ + struct isl_union_map_preimage_upma_data *data = user; + isl_union_map *umap; + + umap = isl_union_map_copy(data->umap); + umap = data->fn(umap, pma); + data->res = isl_union_map_union(data->res, umap); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Compute the preimage of the domain or range of "umap" under the function + * represented by "upma". + * In other words, plug in "upma" in the domain or range of "umap". + * The function "fn" performs the actual preimage computation + * on a piecewise multi-affine function. + */ +static __isl_give isl_union_map *preimage_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma, + __isl_give isl_union_map *(*fn)(__isl_take isl_union_map *umap, + __isl_take isl_pw_multi_aff *pma)) +{ + struct isl_union_map_preimage_upma_data data; + + data.umap = umap; + data.res = isl_union_map_empty(isl_union_map_get_space(umap)); + data.fn = fn; + if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, + &preimage_upma, &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_union_map_free(umap); + isl_union_pw_multi_aff_free(upma); + + return data.res; +} + +/* Compute the preimage of the domain of "umap" under the function + * represented by "upma". + * In other words, plug in "upma" in the domain of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with domain space equal to one of the target spaces of "upma", + * except that the domain has been replaced by one of the the domain spaces that + * corresponds to that target space of "upma". + */ +__isl_give isl_union_map *isl_union_map_preimage_domain_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma) +{ + return preimage_union_pw_multi_aff(umap, upma, + &isl_union_map_preimage_domain_pw_multi_aff); +} + +/* Compute the preimage of the range of "umap" under the function + * represented by "upma". + * In other words, plug in "upma" in the range of "umap". + * The result contains maps that live in the same spaces as the maps of "umap" + * with range space equal to one of the target spaces of "upma", + * except that the range has been replaced by one of the the domain spaces that + * corresponds to that target space of "upma". + */ +__isl_give isl_union_map *isl_union_map_preimage_range_union_pw_multi_aff( + __isl_take isl_union_map *umap, + __isl_take isl_union_pw_multi_aff *upma) +{ + return preimage_union_pw_multi_aff(umap, upma, + &isl_union_map_preimage_range_pw_multi_aff); +} + +/* Compute the preimage of "uset" under the function represented by "upma". + * In other words, plug in "upma" in the range of "uset". + * The result contains sets that live in the same spaces as the sets of "uset" + * with space equal to one of the target spaces of "upma", + * except that the space has been replaced by one of the the domain spaces that + * corresponds to that target space of "upma". + */ +__isl_give isl_union_set *isl_union_set_preimage_union_pw_multi_aff( + __isl_take isl_union_set *uset, + __isl_take isl_union_pw_multi_aff *upma) +{ + return preimage_union_pw_multi_aff(uset, upma, + &isl_union_set_preimage_pw_multi_aff); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the space of *entry. + */ +static isl_stat reset_user(void **entry, void *user) +{ + isl_map **map = (isl_map **)entry; + + *map = isl_map_reset_user(*map); + + return *map ? isl_stat_ok : isl_stat_error; +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the spaces of "umap". + */ +__isl_give isl_union_map *isl_union_map_reset_user( + __isl_take isl_union_map *umap) +{ + umap = isl_union_map_cow(umap); + if (!umap) + return NULL; + umap->dim = isl_space_reset_user(umap->dim); + if (!umap->dim) + return isl_union_map_free(umap); + umap = un_op(umap, &reset_user); + + return umap; +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the spaces of "uset". + */ +__isl_give isl_union_set *isl_union_set_reset_user( + __isl_take isl_union_set *uset) +{ + return isl_union_map_reset_user(uset); +} + +/* Internal data structure for isl_union_map_project_out. + * "type", "first" and "n" are the arguments for the isl_map_project_out + * call. + * "res" collects the results. + */ +struct isl_union_map_project_out_data { + enum isl_dim_type type; + unsigned first; + unsigned n; + + isl_union_map *res; +}; + +/* Turn the data->n dimensions of type data->type, starting at data->first + * into existentially quantified variables and add the result to data->res. + */ +static isl_stat project_out(__isl_take isl_map *map, void *user) +{ + struct isl_union_map_project_out_data *data = user; + + map = isl_map_project_out(map, data->type, data->first, data->n); + data->res = isl_union_map_add_map(data->res, map); + + return isl_stat_ok; +} + +/* Turn the "n" dimensions of type "type", starting at "first" + * into existentially quantified variables. + * Since the space of an isl_union_map only contains parameters, + * type is required to be equal to isl_dim_param. + */ +__isl_give isl_union_map *isl_union_map_project_out( + __isl_take isl_union_map *umap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_space *space; + struct isl_union_map_project_out_data data = { type, first, n }; + + if (!umap) + return NULL; + + if (type != isl_dim_param) + isl_die(isl_union_map_get_ctx(umap), isl_error_invalid, + "can only project out parameters", + return isl_union_map_free(umap)); + + space = isl_union_map_get_space(umap); + space = isl_space_drop_dims(space, type, first, n); + data.res = isl_union_map_empty(space); + if (isl_union_map_foreach_map(umap, &project_out, &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_union_map_free(umap); + + return data.res; +} + +/* Turn the "n" dimensions of type "type", starting at "first" + * into existentially quantified variables. + * Since the space of an isl_union_set only contains parameters, + * "type" is required to be equal to isl_dim_param. + */ +__isl_give isl_union_set *isl_union_set_project_out( + __isl_take isl_union_set *uset, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return isl_union_map_project_out(uset, type, first, n); +} + +/* Internal data structure for isl_union_map_involves_dims. + * "first" and "n" are the arguments for the isl_map_involves_dims calls. + */ +struct isl_union_map_involves_dims_data { + unsigned first; + unsigned n; +}; + +/* Does "map" _not_ involve the data->n parameters starting at data->first? + */ +static isl_bool map_excludes(__isl_keep isl_map *map, void *user) +{ + struct isl_union_map_involves_dims_data *data = user; + isl_bool involves; + + involves = isl_map_involves_dims(map, + isl_dim_param, data->first, data->n); + if (involves < 0) + return isl_bool_error; + return !involves; +} + +/* Does "umap" involve any of the n parameters starting at first? + * "type" is required to be set to isl_dim_param. + * + * "umap" involves any of those parameters if any of its maps + * involve the parameters. In other words, "umap" does not + * involve any of the parameters if all its maps to not + * involve the parameters. + */ +isl_bool isl_union_map_involves_dims(__isl_keep isl_union_map *umap, + enum isl_dim_type type, unsigned first, unsigned n) +{ + struct isl_union_map_involves_dims_data data = { first, n }; + isl_bool excludes; + + if (type != isl_dim_param) + isl_die(isl_union_map_get_ctx(umap), isl_error_invalid, + "can only reference parameters", return isl_bool_error); + + excludes = union_map_forall_user(umap, &map_excludes, &data); + + if (excludes < 0) + return isl_bool_error; + + return !excludes; +} + +/* Internal data structure for isl_union_map_reset_range_space. + * "range" is the space from which to set the range space. + * "res" collects the results. + */ +struct isl_union_map_reset_range_space_data { + isl_space *range; + isl_union_map *res; +}; + +/* Replace the range space of "map" by the range space of data->range and + * add the result to data->res. + */ +static isl_stat reset_range_space(__isl_take isl_map *map, void *user) +{ + struct isl_union_map_reset_range_space_data *data = user; + isl_space *space; + + space = isl_map_get_space(map); + space = isl_space_domain(space); + space = isl_space_extend_domain_with_range(space, + isl_space_copy(data->range)); + map = isl_map_reset_space(map, space); + data->res = isl_union_map_add_map(data->res, map); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Replace the range space of all the maps in "umap" by + * the range space of "space". + * + * This assumes that all maps have the same output dimension. + * This function should therefore not be made publicly available. + * + * Since the spaces of the maps change, so do their hash value. + * We therefore need to create a new isl_union_map. + */ +__isl_give isl_union_map *isl_union_map_reset_range_space( + __isl_take isl_union_map *umap, __isl_take isl_space *space) +{ + struct isl_union_map_reset_range_space_data data = { space }; + + data.res = isl_union_map_empty(isl_union_map_get_space(umap)); + if (isl_union_map_foreach_map(umap, &reset_range_space, &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_space_free(space); + isl_union_map_free(umap); + return data.res; +} + +/* Internal data structure for isl_union_map_order_at_multi_union_pw_aff. + * "mupa" is the function from which the isl_multi_pw_affs are extracted. + * "order" is applied to the extracted isl_multi_pw_affs that correspond + * to the domain and the range of each map. + * "res" collects the results. + */ +struct isl_union_order_at_data { + isl_multi_union_pw_aff *mupa; + __isl_give isl_map *(*order)(__isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2); + isl_union_map *res; +}; + +/* Intersect "map" with the result of applying data->order to + * the functions in data->mupa that apply to the domain and the range + * of "map" and add the result to data->res. + */ +static isl_stat order_at(__isl_take isl_map *map, void *user) +{ + struct isl_union_order_at_data *data = user; + isl_space *space; + isl_multi_pw_aff *mpa1, *mpa2; + isl_map *order; + + space = isl_space_domain(isl_map_get_space(map)); + mpa1 = isl_multi_union_pw_aff_extract_multi_pw_aff(data->mupa, space); + space = isl_space_range(isl_map_get_space(map)); + mpa2 = isl_multi_union_pw_aff_extract_multi_pw_aff(data->mupa, space); + order = data->order(mpa1, mpa2); + map = isl_map_intersect(map, order); + data->res = isl_union_map_add_map(data->res, map); + + return data->res ? isl_stat_ok : isl_stat_error; +} + +/* Intersect each map in "umap" with the result of calling "order" + * on the functions is "mupa" that apply to the domain and the range + * of the map. + */ +static __isl_give isl_union_map *isl_union_map_order_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, __isl_take isl_multi_union_pw_aff *mupa, + __isl_give isl_map *(*order)(__isl_take isl_multi_pw_aff *mpa1, + __isl_take isl_multi_pw_aff *mpa2)) +{ + struct isl_union_order_at_data data; + + umap = isl_union_map_align_params(umap, + isl_multi_union_pw_aff_get_space(mupa)); + mupa = isl_multi_union_pw_aff_align_params(mupa, + isl_union_map_get_space(umap)); + data.mupa = mupa; + data.order = order; + data.res = isl_union_map_empty(isl_union_map_get_space(umap)); + if (isl_union_map_foreach_map(umap, &order_at, &data) < 0) + data.res = isl_union_map_free(data.res); + + isl_multi_union_pw_aff_free(mupa); + isl_union_map_free(umap); + return data.res; +} + +/* Return the subset of "umap" where the domain and the range + * have equal "mupa" values. + */ +__isl_give isl_union_map *isl_union_map_eq_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa) +{ + return isl_union_map_order_at_multi_union_pw_aff(umap, mupa, + &isl_multi_pw_aff_eq_map); +} + +/* Return the subset of "umap" where the domain has a lexicographically + * smaller "mupa" value than the range. + */ +__isl_give isl_union_map *isl_union_map_lex_lt_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa) +{ + return isl_union_map_order_at_multi_union_pw_aff(umap, mupa, + &isl_multi_pw_aff_lex_lt_map); +} + +/* Return the subset of "umap" where the domain has a lexicographically + * greater "mupa" value than the range. + */ +__isl_give isl_union_map *isl_union_map_lex_gt_at_multi_union_pw_aff( + __isl_take isl_union_map *umap, + __isl_take isl_multi_union_pw_aff *mupa) +{ + return isl_union_map_order_at_multi_union_pw_aff(umap, mupa, + &isl_multi_pw_aff_lex_gt_map); +} + +/* Return the union of the elements in the list "list". + */ +__isl_give isl_union_set *isl_union_set_list_union( + __isl_take isl_union_set_list *list) +{ + int i, n; + isl_ctx *ctx; + isl_space *space; + isl_union_set *res; + + if (!list) + return NULL; + + ctx = isl_union_set_list_get_ctx(list); + space = isl_space_params_alloc(ctx, 0); + res = isl_union_set_empty(space); + + n = isl_union_set_list_n_union_set(list); + for (i = 0; i < n; ++i) { + isl_union_set *uset_i; + + uset_i = isl_union_set_list_get_union_set(list, i); + res = isl_union_set_union(res, uset_i); + } + + isl_union_set_list_free(list); + return res; +} + +/* Update *hash with the hash value of "map". + */ +static isl_stat add_hash(__isl_take isl_map *map, void *user) +{ + uint32_t *hash = user; + uint32_t map_hash; + + map_hash = isl_map_get_hash(map); + isl_hash_hash(*hash, map_hash); + + isl_map_free(map); + return isl_stat_ok; +} + +/* Return a hash value that digests "umap". + */ +uint32_t isl_union_map_get_hash(__isl_keep isl_union_map *umap) +{ + uint32_t hash; + + if (!umap) + return 0; + + hash = isl_hash_init(); + if (isl_union_map_foreach_map(umap, &add_hash, &hash) < 0) + return 0; + + return hash; +} + +/* Return a hash value that digests "uset". + */ +uint32_t isl_union_set_get_hash(__isl_keep isl_union_set *uset) +{ + return isl_union_map_get_hash(uset); +} Index: lib/Analysis/isl/isl_union_map_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_map_private.h @@ -0,0 +1,14 @@ +#define isl_union_set_list isl_union_map_list +#define isl_union_set isl_union_map +#include +#include + +struct isl_union_map { + int ref; + isl_space *dim; + + struct isl_hash_table table; +}; + +__isl_give isl_union_map *isl_union_map_reset_range_space( + __isl_take isl_union_map *umap, __isl_take isl_space *space); Index: lib/Analysis/isl/isl_union_multi.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_multi.c @@ -0,0 +1,465 @@ +/* + * Copyright 2010 INRIA Saclay + * Copyright 2013 Ecole Normale Superieure + * Copyright 2015 INRIA Paris-Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + * and INRIA Paris-Rocquencourt, Domaine de Voluceau, Rocquenqourt, B.P. 105, + * 78153 Le Chesnay Cedex France + */ + +#include +#include + +/* A group of expressions defined over the same domain space "domain_space". + * The entries of "part_table" are the individual expressions, + * keyed on the entire space of the expression. + * + * Each UNION has its own groups, so there can only ever be a single + * reference to each group. + */ +S(UNION,group) { + isl_space *domain_space; + struct isl_hash_table part_table; +}; + +/* A union of expressions defined over different disjoint domains. + * "space" describes the parameters. + * The entries of "table" are keyed on the domain space of the entry and + * contain groups of expressions that are defined over the same domain space. + */ +struct UNION { + int ref; + isl_space *space; + + struct isl_hash_table table; +}; + +/* Internal data structure for isl_union_*_foreach_group. + * "fn" is the function that needs to be called on each group. + */ +S(UNION,foreach_group_data) +{ + isl_stat (*fn)(__isl_keep S(UNION,group) *group, void *user); + void *user; +}; + +/* Call data->fn on the group stored at *entry. + */ +static isl_stat FN(UNION,call_on_group)(void **entry, void *user) +{ + S(UNION,group) *group = *entry; + S(UNION,foreach_group_data) *data; + + data = (S(UNION,foreach_group_data) *) user; + return data->fn(group, data->user); +} + +/* Call "fn" on each group of expressions in "u". + */ +static isl_stat FN(UNION,foreach_group)(__isl_keep UNION *u, + isl_stat (*fn)(__isl_keep S(UNION,group) *group, void *user), + void *user) +{ + S(UNION,foreach_group_data) data = { fn, user }; + + if (!u) + return isl_stat_error; + + return isl_hash_table_foreach(u->space->ctx, &u->table, + &FN(UNION,call_on_group), &data); +} + +/* A isl_union_*_foreach_group callback for counting the total number + * of expressions in a UNION. Add the number of expressions in "group" + * to *n. + */ +static isl_stat FN(UNION,count_part)(__isl_keep S(UNION,group) *group, + void *user) +{ + int *n = user; + + if (!group) + return isl_stat_error; + + *n += group->part_table.n; + return isl_stat_ok; +} + +/* Return the number of base expressions in "u". + */ +int FN(FN(UNION,n),PARTS)(__isl_keep UNION *u) +{ + int n; + + n = 0; + if (FN(UNION,foreach_group)(u, &FN(UNION,count_part), &n) < 0) + n = -1; + return n; +} + +/* Free an entry in a group of expressions. + * Each entry in such a group is a single expression. + */ +static isl_stat FN(UNION,free_group_entry)(void **entry, void *user) +{ + PART *part = *entry; + + FN(PART,free)(part); + return isl_stat_ok; +} + +/* Free all memory allocated for "group" and return NULL. + */ +static __isl_null S(UNION,group) *FN(UNION,group_free)( + __isl_take S(UNION,group) *group) +{ + isl_ctx *ctx; + + if (!group) + return NULL; + + ctx = isl_space_get_ctx(group->domain_space); + isl_hash_table_foreach(ctx, &group->part_table, + &FN(UNION,free_group_entry), NULL); + isl_hash_table_clear(&group->part_table); + isl_space_free(group->domain_space); + free(group); + return NULL; +} + +/* Allocate a group of expressions defined over the same domain space + * with domain space "domain_space" and initial size "size". + */ +static __isl_give S(UNION,group) *FN(UNION,group_alloc)( + __isl_take isl_space *domain_space, int size) +{ + isl_ctx *ctx; + S(UNION,group) *group; + + if (!domain_space) + return NULL; + ctx = isl_space_get_ctx(domain_space); + group = isl_calloc_type(ctx, S(UNION,group)); + if (!group) + goto error; + group->domain_space = domain_space; + if (isl_hash_table_init(ctx, &group->part_table, size) < 0) + return FN(UNION,group_free)(group); + + return group; +error: + isl_space_free(domain_space); + return NULL; +} + +/* Is the space of "entry" equal to "space"? + */ +static int FN(UNION,has_space)(const void *entry, const void *val) +{ + PART *part = (PART *) entry; + isl_space *space = (isl_space *) val; + + return isl_space_is_equal(part->dim, space); +} + +/* Return a group equal to "group", but with a single reference. + * Since all groups have only a single reference, simply return "group". + */ +static __isl_give S(UNION,group) *FN(UNION,group_cow)( + __isl_take S(UNION,group) *group) +{ + return group; +} + +S(UNION,foreach_data) +{ + isl_stat (*fn)(__isl_take PART *part, void *user); + void *user; +}; + +static isl_stat FN(UNION,call_on_copy)(void **entry, void *user) +{ + PART *part = *entry; + S(UNION,foreach_data) *data = (S(UNION,foreach_data) *) user; + + part = FN(PART,copy)(part); + if (!part) + return isl_stat_error; + return data->fn(part, data->user); +} + +/* Call data->fn on a copy of each expression in "group". + */ +static isl_stat FN(UNION,group_call_on_copy)(__isl_keep S(UNION,group) *group, + void *user) +{ + isl_ctx *ctx; + + if (!group) + return isl_stat_error; + + ctx = isl_space_get_ctx(group->domain_space); + return isl_hash_table_foreach(ctx, &group->part_table, + &FN(UNION,call_on_copy), user); +} + +isl_stat FN(FN(UNION,foreach),PARTS)(__isl_keep UNION *u, + isl_stat (*fn)(__isl_take PART *part, void *user), void *user) +{ + S(UNION,foreach_data) data = { fn, user }; + + if (!u) + return isl_stat_error; + + return FN(UNION,foreach_group)(u, &FN(UNION,group_call_on_copy), &data); +} + +/* Is the domain space of the group of expressions at "entry" + * equal to "space"? + */ +static int FN(UNION,group_has_domain_space)(const void *entry, const void *val) +{ + S(UNION,group) *group = (S(UNION,group) *) entry; + isl_space *space = (isl_space *) val; + + return isl_space_is_domain_internal(group->domain_space, space); +} + +/* Return the entry, if any, in "u" that lives in "space". + * If "reserve" is set, then an entry is created if it does not exist yet. + * Return NULL on error and isl_hash_table_entry_none if no entry was found. + * Note that when "reserve" is set, the function will never return + * isl_hash_table_entry_none. + * + * First look for the group of expressions with the same domain space, + * creating one if needed. + * Then look for the expression living in the specified space in that group. + */ +static struct isl_hash_table_entry *FN(UNION,find_part_entry)( + __isl_keep UNION *u, __isl_keep isl_space *space, int reserve) +{ + isl_ctx *ctx; + uint32_t hash; + struct isl_hash_table_entry *group_entry, *part_entry; + S(UNION,group) *group; + + if (!u || !space) + return NULL; + + ctx = FN(UNION,get_ctx)(u); + hash = isl_space_get_domain_hash(space); + group_entry = isl_hash_table_find(ctx, &u->table, hash, + &FN(UNION,group_has_domain_space), space, reserve); + if (!group_entry) + return reserve ? NULL : isl_hash_table_entry_none; + if (reserve && !group_entry->data) { + isl_space *domain = isl_space_domain(isl_space_copy(space)); + group = FN(UNION,group_alloc)(domain, 1); + group_entry->data = group; + } else { + group = group_entry->data; + if (reserve) + group = FN(UNION,group_cow)(group); + } + if (!group) + return NULL; + hash = isl_space_get_hash(space); + part_entry = isl_hash_table_find(ctx, &group->part_table, hash, + &FN(UNION,has_space), space, reserve); + if (!reserve && !part_entry) + return isl_hash_table_entry_none; + return part_entry; +} + +/* Remove "part_entry" from the hash table of "u". + * + * First look the group_entry in "u" holding the group that + * contains "part_entry". Remove "part_entry" from that group. + * If the group becomes empty, then also remove the group_entry from "u". + */ +static __isl_give UNION *FN(UNION,remove_part_entry)(__isl_take UNION *u, + struct isl_hash_table_entry *part_entry) +{ + isl_ctx *ctx; + uint32_t hash; + PART *part; + struct isl_hash_table_entry *group_entry; + S(UNION,group) *group; + + if (!u || !part_entry) + return FN(UNION,free)(u); + + part = part_entry->data; + ctx = FN(UNION,get_ctx)(u); + hash = isl_space_get_domain_hash(part->dim); + group_entry = isl_hash_table_find(ctx, &u->table, hash, + &FN(UNION,group_has_domain_space), part->dim, 0); + if (!group_entry) + isl_die(ctx, isl_error_internal, "missing group", + return FN(UNION,free)(u)); + group = group_entry->data; + isl_hash_table_remove(ctx, &group->part_table, part_entry); + FN(PART,free)(part); + + if (group->part_table.n != 0) + return u; + + isl_hash_table_remove(ctx, &u->table, group_entry); + FN(UNION,group_free)(group); + + return u; +} + +/* Are the domains of "part1" and "part2" disjoint? + */ +static isl_bool FN(UNION,disjoint_domain)(__isl_keep PART *part1, + __isl_keep PART *part2) +{ + isl_set *dom1, *dom2; + isl_bool disjoint; + + if (!part1 || !part2) + return isl_bool_error; + dom1 = FN(PART,domain)(FN(PART,copy)(part1)); + dom2 = FN(PART,domain)(FN(PART,copy)(part2)); + disjoint = isl_set_is_disjoint(dom1, dom2); + isl_set_free(dom1); + isl_set_free(dom2); + + return disjoint; +} + +/* Check that the expression at *entry has a domain that is disjoint + * from that of "part", unless they also have the same target space. + */ +static isl_stat FN(UNION,check_disjoint_domain_entry)(void **entry, void *user) +{ + PART *part = user; + PART *other = *entry; + isl_bool equal; + isl_bool disjoint; + + equal = isl_space_is_equal(part->dim, other->dim); + if (equal < 0) + return isl_stat_error; + if (equal) + return isl_stat_ok; + + disjoint = FN(UNION,disjoint_domain)(part, other); + if (disjoint < 0) + return isl_stat_error; + if (!disjoint) + isl_die(FN(PART,get_ctx)(part), isl_error_invalid, + "overlapping domain with other part", + return isl_stat_error); + return isl_stat_ok; +} + +/* Check that the domain of "part" is disjoint from the domain of the entries + * in "u" that are defined on the same domain space, but have a different + * target space. + * If there is no group of expressions in "u" with the same domain space, + * then everything is fine. Otherwise, check the individual expressions + * in that group. + */ +static isl_stat FN(UNION,check_disjoint_domain_other)(__isl_keep UNION *u, + __isl_keep PART *part) +{ + isl_ctx *ctx; + uint32_t hash; + struct isl_hash_table_entry *group_entry; + S(UNION,group) *group; + + if (!u || !part) + return isl_stat_error; + ctx = FN(UNION,get_ctx)(u); + hash = isl_space_get_domain_hash(part->dim); + group_entry = isl_hash_table_find(ctx, &u->table, hash, + &FN(UNION,group_has_domain_space), part->dim, 0); + if (!group_entry) + return isl_stat_ok; + group = group_entry->data; + return isl_hash_table_foreach(ctx, &group->part_table, + &FN(UNION,check_disjoint_domain_entry), part); +} + +/* Check that the domain of "part1" is disjoint from the domain of "part2". + * This check is performed before "part2" is added to a UNION to ensure + * that the UNION expression remains a function. + */ +static isl_stat FN(UNION,check_disjoint_domain)(__isl_keep PART *part1, + __isl_keep PART *part2) +{ + isl_bool disjoint; + + disjoint = FN(UNION,disjoint_domain)(part1, part2); + if (disjoint < 0) + return isl_stat_error; + if (!disjoint) + isl_die(FN(PART,get_ctx)(part1), isl_error_invalid, + "domain of additional part should be disjoint", + return isl_stat_error); + return isl_stat_ok; +} + +/* Internal data structure for isl_union_*_foreach_inplace. + * "fn" is the function that needs to be called on each entry. + */ +S(UNION,foreach_inplace_data) +{ + isl_stat (*fn)(void **entry, void *user); + void *user; +}; + +/* isl_union_*_foreach_group callback for calling data->fn on + * each part entry in the group. + */ +static isl_stat FN(UNION,group_call_inplace)(__isl_keep S(UNION,group) *group, + void *user) +{ + isl_ctx *ctx; + S(UNION,foreach_inplace_data) *data; + + if (!group) + return isl_stat_error; + + data = (S(UNION,foreach_inplace_data) *) user; + ctx = isl_space_get_ctx(group->domain_space); + return isl_hash_table_foreach(ctx, &group->part_table, + data->fn, data->user); +} + +/* Call "fn" on each part entry of "u". + */ +static isl_stat FN(UNION,foreach_inplace)(__isl_keep UNION *u, + isl_stat (*fn)(void **part, void *user), void *user) +{ + S(UNION,foreach_inplace_data) data = { fn, user }; + + return FN(UNION,foreach_group)(u, &FN(UNION,group_call_inplace), &data); +} + +/* Does "u" have a single reference? + * That is, can we change "u" inplace? + */ +static isl_bool FN(UNION,has_single_reference)(__isl_keep UNION *u) +{ + if (!u) + return isl_bool_error; + return u->ref == 1; +} + +static isl_stat FN(UNION,free_u_entry)(void **entry, void *user) +{ + S(UNION,group) *group = *entry; + FN(UNION,group_free)(group); + return isl_stat_ok; +} + +#include Index: lib/Analysis/isl/isl_union_neg.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_neg.c @@ -0,0 +1,25 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include + +/* Return the opposite of "part". + */ +static __isl_give PART *FN(UNION,neg_entry)(__isl_take PART *part, void *user) +{ + return FN(PART,neg)(part); +} + +/* Return the opposite of "u". + */ +__isl_give UNION *FN(UNION,neg)(__isl_take UNION *u) +{ + return FN(UNION,transform_inplace)(u, &FN(UNION,neg_entry), NULL); +} Index: lib/Analysis/isl/isl_union_single.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_single.c @@ -0,0 +1,197 @@ +/* + * Copyright 2010 INRIA Saclay + * Copyright 2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include + +/* A union of expressions defined over different domain spaces. + * "space" describes the parameters. + * The entries of "table" are keyed on the domain space of the entry. + */ +struct UNION { + int ref; +#ifdef HAS_TYPE + enum isl_fold type; +#endif + isl_space *space; + + struct isl_hash_table table; +}; + +/* Return the number of base expressions in "u". + */ +int FN(FN(UNION,n),PARTS)(__isl_keep UNION *u) +{ + return u ? u->table.n : 0; +} + +S(UNION,foreach_data) +{ + isl_stat (*fn)(__isl_take PART *part, void *user); + void *user; +}; + +static isl_stat FN(UNION,call_on_copy)(void **entry, void *user) +{ + PART *part = *entry; + S(UNION,foreach_data) *data = (S(UNION,foreach_data) *)user; + + part = FN(PART,copy)(part); + if (!part) + return isl_stat_error; + return data->fn(part, data->user); +} + +isl_stat FN(FN(UNION,foreach),PARTS)(__isl_keep UNION *u, + isl_stat (*fn)(__isl_take PART *part, void *user), void *user) +{ + S(UNION,foreach_data) data = { fn, user }; + + if (!u) + return isl_stat_error; + + return isl_hash_table_foreach(u->space->ctx, &u->table, + &FN(UNION,call_on_copy), &data); +} + +/* Is the domain space of "entry" equal to the domain of "space"? + */ +static int FN(UNION,has_same_domain_space)(const void *entry, const void *val) +{ + PART *part = (PART *)entry; + isl_space *space = (isl_space *) val; + + if (isl_space_is_set(space)) + return isl_space_is_set(part->dim); + + return isl_space_tuple_is_equal(part->dim, isl_dim_in, + space, isl_dim_in); +} + +/* Return the entry, if any, in "u" that lives in "space". + * If "reserve" is set, then an entry is created if it does not exist yet. + * Return NULL on error and isl_hash_table_entry_none if no entry was found. + * Note that when "reserve" is set, the function will never return + * isl_hash_table_entry_none. + * + * First look for the entry (if any) with the same domain space. + * If it exists, then check if the range space also matches. + */ +static struct isl_hash_table_entry *FN(UNION,find_part_entry)( + __isl_keep UNION *u, __isl_keep isl_space *space, int reserve) +{ + isl_ctx *ctx; + uint32_t hash; + struct isl_hash_table_entry *entry; + isl_bool equal; + PART *part; + + if (!u || !space) + return NULL; + + ctx = FN(UNION,get_ctx)(u); + hash = isl_space_get_domain_hash(space); + entry = isl_hash_table_find(ctx, &u->table, hash, + &FN(UNION,has_same_domain_space), space, reserve); + if (!entry) + return reserve ? NULL : isl_hash_table_entry_none; + if (reserve && !entry->data) + return entry; + part = entry->data; + equal = isl_space_tuple_is_equal(part->dim, isl_dim_out, + space, isl_dim_out); + if (equal < 0) + return NULL; + if (equal) + return entry; + if (!reserve) + return isl_hash_table_entry_none; + isl_die(FN(UNION,get_ctx)(u), isl_error_invalid, + "union expression can only contain a single " + "expression over a given domain", return NULL); +} + +/* Remove "part_entry" from the hash table of "u". + */ +static __isl_give UNION *FN(UNION,remove_part_entry)(__isl_take UNION *u, + struct isl_hash_table_entry *part_entry) +{ + isl_ctx *ctx; + + if (!u || !part_entry) + return FN(UNION,free)(u); + + ctx = FN(UNION,get_ctx)(u); + isl_hash_table_remove(ctx, &u->table, part_entry); + FN(PART,free)(part_entry->data); + + return u; +} + +/* Check that the domain of "part" is disjoint from the domain of the entries + * in "u" that are defined on the same domain space, but have a different + * target space. + * Since a UNION with a single entry per domain space is not allowed + * to contain two entries with the same domain space, there cannot be + * any such other entry. + */ +static isl_stat FN(UNION,check_disjoint_domain_other)(__isl_keep UNION *u, + __isl_keep PART *part) +{ + return isl_stat_ok; +} + +/* Check that the domain of "part1" is disjoint from the domain of "part2". + * This check is performed before "part2" is added to a UNION to ensure + * that the UNION expression remains a function. + * Since a UNION with a single entry per domain space is not allowed + * to contain two entries with the same domain space, fail unconditionally. + */ +static isl_stat FN(UNION,check_disjoint_domain)(__isl_keep PART *part1, + __isl_keep PART *part2) +{ + isl_die(FN(PART,get_ctx)(part1), isl_error_invalid, + "additional part should live on separate space", + return isl_stat_error); +} + +/* Call "fn" on each part entry of "u". + */ +static isl_stat FN(UNION,foreach_inplace)(__isl_keep UNION *u, + isl_stat (*fn)(void **part, void *user), void *user) +{ + isl_ctx *ctx; + + if (!u) + return isl_stat_error; + ctx = FN(UNION,get_ctx)(u); + return isl_hash_table_foreach(ctx, &u->table, fn, user); +} + +/* Does "u" have a single reference? + * That is, can we change "u" inplace? + */ +static isl_bool FN(UNION,has_single_reference)(__isl_keep UNION *u) +{ + if (!u) + return isl_bool_error; + return u->ref == 1; +} + +static isl_stat FN(UNION,free_u_entry)(void **entry, void *user) +{ + PART *part = *entry; + FN(PART,free)(part); + return isl_stat_ok; +} + +#include Index: lib/Analysis/isl/isl_union_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_union_templ.c @@ -0,0 +1,1163 @@ +/* + * Copyright 2010 INRIA Saclay + * Copyright 2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +__isl_give UNION *FN(UNION,cow)(__isl_take UNION *u); + +isl_ctx *FN(UNION,get_ctx)(__isl_keep UNION *u) +{ + return u ? u->space->ctx : NULL; +} + +__isl_give isl_space *FN(UNION,get_space)(__isl_keep UNION *u) +{ + if (!u) + return NULL; + return isl_space_copy(u->space); +} + +/* Return the number of parameters of "u", where "type" + * is required to be set to isl_dim_param. + */ +unsigned FN(UNION,dim)(__isl_keep UNION *u, enum isl_dim_type type) +{ + if (!u) + return 0; + + if (type != isl_dim_param) + isl_die(FN(UNION,get_ctx)(u), isl_error_invalid, + "can only reference parameters", return 0); + + return isl_space_dim(u->space, type); +} + +/* Return the position of the parameter with the given name + * in "u". + * Return -1 if no such dimension can be found. + */ +int FN(UNION,find_dim_by_name)(__isl_keep UNION *u, enum isl_dim_type type, + const char *name) +{ + if (!u) + return -1; + return isl_space_find_dim_by_name(u->space, type, name); +} + +#ifdef HAS_TYPE +static __isl_give UNION *FN(UNION,alloc)(__isl_take isl_space *dim, + enum isl_fold type, int size) +#else +static __isl_give UNION *FN(UNION,alloc)(__isl_take isl_space *dim, int size) +#endif +{ + UNION *u; + + dim = isl_space_params(dim); + if (!dim) + return NULL; + + u = isl_calloc_type(dim->ctx, UNION); + if (!u) + goto error; + + u->ref = 1; +#ifdef HAS_TYPE + u->type = type; +#endif + u->space = dim; + if (isl_hash_table_init(dim->ctx, &u->table, size) < 0) + return FN(UNION,free)(u); + + return u; +error: + isl_space_free(dim); + return NULL; +} + +#ifdef HAS_TYPE +__isl_give UNION *FN(UNION,ZERO)(__isl_take isl_space *dim, enum isl_fold type) +{ + return FN(UNION,alloc)(dim, type, 16); +} +#else +__isl_give UNION *FN(UNION,ZERO)(__isl_take isl_space *dim) +{ + return FN(UNION,alloc)(dim, 16); +} +#endif + +__isl_give UNION *FN(UNION,copy)(__isl_keep UNION *u) +{ + if (!u) + return NULL; + + u->ref++; + return u; +} + +/* Extract the element of "u" living in "space" (ignoring parameters). + * + * Return the ZERO element if "u" does not contain any element + * living in "space". + */ +__isl_give PART *FN(FN(UNION,extract),PARTS)(__isl_keep UNION *u, + __isl_take isl_space *space) +{ + struct isl_hash_table_entry *entry; + + if (!u || !space) + goto error; + if (!isl_space_match(u->space, isl_dim_param, space, isl_dim_param)) { + space = isl_space_drop_dims(space, isl_dim_param, + 0, isl_space_dim(space, isl_dim_param)); + space = isl_space_align_params(space, + FN(UNION,get_space)(u)); + if (!space) + goto error; + } + + entry = FN(UNION,find_part_entry)(u, space, 0); + if (!entry) + goto error; + if (entry == isl_hash_table_entry_none) +#ifdef HAS_TYPE + return FN(PART,ZERO)(space, u->type); +#else + return FN(PART,ZERO)(space); +#endif + isl_space_free(space); + return FN(PART,copy)(entry->data); +error: + isl_space_free(space); + return NULL; +} + +/* Add "part" to "u". + * If "disjoint" is set, then "u" is not allowed to already have + * a part that is defined over a domain that overlaps with the domain + * of "part". + * Otherwise, compute the union sum of "part" and the part in "u" + * defined on the same space. + */ +static __isl_give UNION *FN(UNION,add_part_generic)(__isl_take UNION *u, + __isl_take PART *part, int disjoint) +{ + int empty; + struct isl_hash_table_entry *entry; + + if (!part) + goto error; + + empty = FN(PART,IS_ZERO)(part); + if (empty < 0) + goto error; + if (empty) { + FN(PART,free)(part); + return u; + } + + u = FN(UNION,align_params)(u, FN(PART,get_space)(part)); + part = FN(PART,align_params)(part, FN(UNION,get_space)(u)); + + u = FN(UNION,cow)(u); + + if (!u) + goto error; + + if (FN(UNION,check_disjoint_domain_other)(u, part) < 0) + goto error; + entry = FN(UNION,find_part_entry)(u, part->dim, 1); + if (!entry) + goto error; + + if (!entry->data) + entry->data = part; + else { + if (disjoint && + FN(UNION,check_disjoint_domain)(entry->data, part) < 0) + goto error; + entry->data = FN(PART,union_add_)(entry->data, + FN(PART,copy)(part)); + if (!entry->data) + goto error; + empty = FN(PART,IS_ZERO)(part); + if (empty < 0) + goto error; + if (empty) + u = FN(UNION,remove_part_entry)(u, entry); + FN(PART,free)(part); + } + + return u; +error: + FN(PART,free)(part); + FN(UNION,free)(u); + return NULL; +} + +/* Add "part" to "u", where "u" is assumed not to already have + * a part that is defined on the same space as "part". + */ +__isl_give UNION *FN(FN(UNION,add),PARTS)(__isl_take UNION *u, + __isl_take PART *part) +{ + return FN(UNION,add_part_generic)(u, part, 1); +} + +#ifdef HAS_TYPE +/* Allocate a UNION with the same type and the same size as "u" and + * with space "space". + */ +static __isl_give UNION *FN(UNION,alloc_same_size_on_space)(__isl_keep UNION *u, + __isl_take isl_space *space) +{ + if (!u) + goto error; + return FN(UNION,alloc)(space, u->type, u->table.n); +error: + isl_space_free(space); + return NULL; +} +#else +/* Allocate a UNION with the same size as "u" and with space "space". + */ +static __isl_give UNION *FN(UNION,alloc_same_size_on_space)(__isl_keep UNION *u, + __isl_take isl_space *space) +{ + if (!u) + goto error; + return FN(UNION,alloc)(space, u->table.n); +error: + isl_space_free(space); + return NULL; +} +#endif + +/* Allocate a UNION with the same space, the same type (if any) and + * the same size as "u". + */ +static __isl_give UNION *FN(UNION,alloc_same_size)(__isl_keep UNION *u) +{ + return FN(UNION,alloc_same_size_on_space)(u, FN(UNION,get_space)(u)); +} + +/* Internal data structure for isl_union_*_transform_space. + * "fn' is applied to each entry in the input. + * "res" collects the results. + */ +S(UNION,transform_data) +{ + __isl_give PART *(*fn)(__isl_take PART *part, void *user); + void *user; + + UNION *res; +}; + +/* Apply data->fn to "part" and add the result to data->res. + */ +static isl_stat FN(UNION,transform_entry)(__isl_take PART *part, void *user) +{ + S(UNION,transform_data) *data = (S(UNION,transform_data) *)user; + + part = data->fn(part, data->user); + data->res = FN(FN(UNION,add),PARTS)(data->res, part); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Return a UNION living in "space" that is obtained by applying "fn" + * to each of the entries in "u". + */ +static __isl_give UNION *FN(UNION,transform_space)(__isl_take UNION *u, + isl_space *space, + __isl_give PART *(*fn)(__isl_take PART *part, void *user), void *user) +{ + S(UNION,transform_data) data = { fn, user }; + + data.res = FN(UNION,alloc_same_size_on_space)(u, space); + if (FN(FN(UNION,foreach),PARTS)(u, + &FN(UNION,transform_entry), &data) < 0) + data.res = FN(UNION,free)(data.res); + FN(UNION,free)(u); + return data.res; +} + +/* Return a UNION that lives in the same space as "u" and that is obtained + * by applying "fn" to each of the entries in "u". + */ +static __isl_give UNION *FN(UNION,transform)(__isl_take UNION *u, + __isl_give PART *(*fn)(__isl_take PART *part, void *user), void *user) +{ + return FN(UNION,transform_space)(u, FN(UNION,get_space)(u), fn, user); +} + +/* Apply data->fn to *part and store the result back into *part. + */ +static isl_stat FN(UNION,transform_inplace_entry)(void **part, void *user) +{ + S(UNION,transform_data) *data = (S(UNION,transform_data) *) user; + + *part = data->fn(*part, data->user); + if (!*part) + return isl_stat_error; + return isl_stat_ok; +} + +/* Update "u" by applying "fn" to each entry. + * This operation is assumed not to change the number of entries nor + * the spaces of the entries. + * + * If there is only one reference to "u", then change "u" inplace. + * Otherwise, create a new UNION from "u" and discard the original. + */ +static __isl_give UNION *FN(UNION,transform_inplace)(__isl_take UNION *u, + __isl_give PART *(*fn)(__isl_take PART *part, void *user), void *user) +{ + isl_bool single_ref; + + single_ref = FN(UNION,has_single_reference)(u); + if (single_ref < 0) + return FN(UNION,free)(u); + if (single_ref) { + S(UNION,transform_data) data = { fn, user }; + if (FN(UNION,foreach_inplace)(u, + &FN(UNION,transform_inplace_entry), &data) < 0) + return FN(UNION,free)(u); + return u; + } + return FN(UNION,transform)(u, fn, user); +} + +/* An isl_union_*_transform callback for use in isl_union_*_dup + * that simply returns "part". + */ +static __isl_give PART *FN(UNION,copy_part)(__isl_take PART *part, void *user) +{ + return part; +} + +__isl_give UNION *FN(UNION,dup)(__isl_keep UNION *u) +{ + u = FN(UNION,copy)(u); + return FN(UNION,transform)(u, &FN(UNION,copy_part), NULL); +} + +__isl_give UNION *FN(UNION,cow)(__isl_take UNION *u) +{ + if (!u) + return NULL; + + if (u->ref == 1) + return u; + u->ref--; + return FN(UNION,dup)(u); +} + +__isl_null UNION *FN(UNION,free)(__isl_take UNION *u) +{ + if (!u) + return NULL; + + if (--u->ref > 0) + return NULL; + + isl_hash_table_foreach(u->space->ctx, &u->table, + &FN(UNION,free_u_entry), NULL); + isl_hash_table_clear(&u->table); + isl_space_free(u->space); + free(u); + return NULL; +} + +static __isl_give PART *FN(UNION,align_entry)(__isl_take PART *part, void *user) +{ + isl_reordering *exp = user; + + exp = isl_reordering_extend_space(isl_reordering_copy(exp), + FN(PART,get_domain_space)(part)); + return FN(PART,realign_domain)(part, exp); +} + +/* Reorder the parameters of "u" according to the given reordering. + */ +static __isl_give UNION *FN(UNION,realign_domain)(__isl_take UNION *u, + __isl_take isl_reordering *r) +{ + isl_space *space; + + if (!u || !r) + goto error; + + space = isl_space_copy(r->dim); + u = FN(UNION,transform_space)(u, space, &FN(UNION,align_entry), r); + isl_reordering_free(r); + return u; +error: + FN(UNION,free)(u); + isl_reordering_free(r); + return NULL; +} + +/* Align the parameters of "u" to those of "model". + */ +__isl_give UNION *FN(UNION,align_params)(__isl_take UNION *u, + __isl_take isl_space *model) +{ + isl_reordering *r; + + if (!u || !model) + goto error; + + if (isl_space_match(u->space, isl_dim_param, model, isl_dim_param)) { + isl_space_free(model); + return u; + } + + model = isl_space_params(model); + r = isl_parameter_alignment_reordering(u->space, model); + isl_space_free(model); + + return FN(UNION,realign_domain)(u, r); +error: + isl_space_free(model); + FN(UNION,free)(u); + return NULL; +} + +/* Add "part" to *u, taking the union sum if "u" already has + * a part defined on the same space as "part". + */ +static isl_stat FN(UNION,union_add_part)(__isl_take PART *part, void *user) +{ + UNION **u = (UNION **)user; + + *u = FN(UNION,add_part_generic)(*u, part, 0); + + return isl_stat_ok; +} + +/* Compute the sum of "u1" and "u2" on the union of their domains, + * with the actual sum on the shared domain and + * the defined expression on the symmetric difference of the domains. + * + * This is an internal function that is exposed under different + * names depending on whether the base expressions have a zero default + * value. + * If they do, then this function is called "add". + * Otherwise, it is called "union_add". + */ +static __isl_give UNION *FN(UNION,union_add_)(__isl_take UNION *u1, + __isl_take UNION *u2) +{ + u1 = FN(UNION,align_params)(u1, FN(UNION,get_space)(u2)); + u2 = FN(UNION,align_params)(u2, FN(UNION,get_space)(u1)); + + u1 = FN(UNION,cow)(u1); + + if (!u1 || !u2) + goto error; + + if (FN(FN(UNION,foreach),PARTS)(u2, &FN(UNION,union_add_part), &u1) < 0) + goto error; + + FN(UNION,free)(u2); + + return u1; +error: + FN(UNION,free)(u1); + FN(UNION,free)(u2); + return NULL; +} + +__isl_give UNION *FN(FN(UNION,from),PARTS)(__isl_take PART *part) +{ + isl_space *dim; + UNION *u; + + if (!part) + return NULL; + + dim = FN(PART,get_space)(part); + dim = isl_space_drop_dims(dim, isl_dim_in, 0, isl_space_dim(dim, isl_dim_in)); + dim = isl_space_drop_dims(dim, isl_dim_out, 0, isl_space_dim(dim, isl_dim_out)); +#ifdef HAS_TYPE + u = FN(UNION,ZERO)(dim, part->type); +#else + u = FN(UNION,ZERO)(dim); +#endif + u = FN(FN(UNION,add),PARTS)(u, part); + + return u; +} + +S(UNION,match_bin_data) { + UNION *u2; + UNION *res; + __isl_give PART *(*fn)(__isl_take PART *, __isl_take PART *); +}; + +/* Check if data->u2 has an element living in the same space as "part". + * If so, call data->fn on the two elements and add the result to + * data->res. + */ +static isl_stat FN(UNION,match_bin_entry)(__isl_take PART *part, void *user) +{ + S(UNION,match_bin_data) *data = user; + struct isl_hash_table_entry *entry2; + isl_space *space; + PART *part2; + + space = FN(PART,get_space)(part); + entry2 = FN(UNION,find_part_entry)(data->u2, space, 0); + isl_space_free(space); + if (!entry2) + goto error; + if (entry2 == isl_hash_table_entry_none) { + FN(PART,free)(part); + return isl_stat_ok; + } + + part2 = entry2->data; + if (!isl_space_tuple_is_equal(part->dim, isl_dim_out, + part2->dim, isl_dim_out)) + isl_die(FN(UNION,get_ctx)(data->u2), isl_error_invalid, + "entries should have the same range space", + goto error); + + part = data->fn(part, FN(PART, copy)(entry2->data)); + + data->res = FN(FN(UNION,add),PARTS)(data->res, part); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +error: + FN(PART,free)(part); + return isl_stat_error; +} + +/* This function is currently only used from isl_polynomial.c + * and not from isl_fold.c. + */ +static __isl_give UNION *FN(UNION,match_bin_op)(__isl_take UNION *u1, + __isl_take UNION *u2, + __isl_give PART *(*fn)(__isl_take PART *, __isl_take PART *)) + __attribute__ ((unused)); +/* For each pair of elements in "u1" and "u2" living in the same space, + * call "fn" and collect the results. + */ +static __isl_give UNION *FN(UNION,match_bin_op)(__isl_take UNION *u1, + __isl_take UNION *u2, + __isl_give PART *(*fn)(__isl_take PART *, __isl_take PART *)) +{ + S(UNION,match_bin_data) data = { NULL, NULL, fn }; + + u1 = FN(UNION,align_params)(u1, FN(UNION,get_space)(u2)); + u2 = FN(UNION,align_params)(u2, FN(UNION,get_space)(u1)); + + if (!u1 || !u2) + goto error; + + data.u2 = u2; + data.res = FN(UNION,alloc_same_size)(u1); + if (FN(FN(UNION,foreach),PARTS)(u1, + &FN(UNION,match_bin_entry), &data) < 0) + goto error; + + FN(UNION,free)(u1); + FN(UNION,free)(u2); + return data.res; +error: + FN(UNION,free)(u1); + FN(UNION,free)(u2); + FN(UNION,free)(data.res); + return NULL; +} + +/* Compute the sum of "u1" and "u2". + * + * If the base expressions have a default zero value, then the sum + * is computed on the union of the domains of "u1" and "u2". + * Otherwise, it is computed on their shared domains. + */ +__isl_give UNION *FN(UNION,add)(__isl_take UNION *u1, __isl_take UNION *u2) +{ +#if DEFAULT_IS_ZERO + return FN(UNION,union_add_)(u1, u2); +#else + return FN(UNION,match_bin_op)(u1, u2, &FN(PART,add)); +#endif +} + +#ifndef NO_SUB +/* Subtract "u2" from "u1" and return the result. + */ +__isl_give UNION *FN(UNION,sub)(__isl_take UNION *u1, __isl_take UNION *u2) +{ + return FN(UNION,match_bin_op)(u1, u2, &FN(PART,sub)); +} +#endif + +S(UNION,any_set_data) { + isl_set *set; + __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*); +}; + +static __isl_give PART *FN(UNION,any_set_entry)(__isl_take PART *part, + void *user) +{ + S(UNION,any_set_data) *data = user; + + return data->fn(part, isl_set_copy(data->set)); +} + +/* Update each element of "u" by calling "fn" on the element and "set". + */ +static __isl_give UNION *FN(UNION,any_set_op)(__isl_take UNION *u, + __isl_take isl_set *set, + __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*)) +{ + S(UNION,any_set_data) data = { NULL, fn }; + + u = FN(UNION,align_params)(u, isl_set_get_space(set)); + set = isl_set_align_params(set, FN(UNION,get_space)(u)); + + if (!u || !set) + goto error; + + data.set = set; + u = FN(UNION,transform)(u, &FN(UNION,any_set_entry), &data); + isl_set_free(set); + return u; +error: + FN(UNION,free)(u); + isl_set_free(set); + return NULL; +} + +/* Intersect the domain of "u" with the parameter domain "context". + */ +__isl_give UNION *FN(UNION,intersect_params)(__isl_take UNION *u, + __isl_take isl_set *set) +{ + return FN(UNION,any_set_op)(u, set, &FN(PW,intersect_params)); +} + +/* Compute the gist of the domain of "u" with respect to + * the parameter domain "context". + */ +__isl_give UNION *FN(UNION,gist_params)(__isl_take UNION *u, + __isl_take isl_set *set) +{ + return FN(UNION,any_set_op)(u, set, &FN(PW,gist_params)); +} + +S(UNION,match_domain_data) { + isl_union_set *uset; + UNION *res; + __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*); +}; + +static int FN(UNION,set_has_dim)(const void *entry, const void *val) +{ + isl_set *set = (isl_set *)entry; + isl_space *dim = (isl_space *)val; + + return isl_space_is_equal(set->dim, dim); +} + +/* Find the set in data->uset that lives in the same space as the domain + * of "part", apply data->fn to *entry and this set (if any), and add + * the result to data->res. + */ +static isl_stat FN(UNION,match_domain_entry)(__isl_take PART *part, void *user) +{ + S(UNION,match_domain_data) *data = user; + uint32_t hash; + struct isl_hash_table_entry *entry2; + isl_space *space; + + space = FN(PART,get_domain_space)(part); + hash = isl_space_get_hash(space); + entry2 = isl_hash_table_find(data->uset->dim->ctx, &data->uset->table, + hash, &FN(UNION,set_has_dim), space, 0); + isl_space_free(space); + if (!entry2) { + FN(PART,free)(part); + return isl_stat_ok; + } + + part = data->fn(part, isl_set_copy(entry2->data)); + + data->res = FN(FN(UNION,add),PARTS)(data->res, part); + if (!data->res) + return isl_stat_error; + + return isl_stat_ok; +} + +/* Apply fn to each pair of PW in u and set in uset such that + * the set lives in the same space as the domain of PW + * and collect the results. + */ +static __isl_give UNION *FN(UNION,match_domain_op)(__isl_take UNION *u, + __isl_take isl_union_set *uset, + __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*)) +{ + S(UNION,match_domain_data) data = { NULL, NULL, fn }; + + u = FN(UNION,align_params)(u, isl_union_set_get_space(uset)); + uset = isl_union_set_align_params(uset, FN(UNION,get_space)(u)); + + if (!u || !uset) + goto error; + + data.uset = uset; + data.res = FN(UNION,alloc_same_size)(u); + if (FN(FN(UNION,foreach),PARTS)(u, + &FN(UNION,match_domain_entry), &data) < 0) + goto error; + + FN(UNION,free)(u); + isl_union_set_free(uset); + return data.res; +error: + FN(UNION,free)(u); + isl_union_set_free(uset); + FN(UNION,free)(data.res); + return NULL; +} + +/* Intersect the domain of "u" with "uset". + * If "uset" is a parameters domain, then intersect the parameter + * domain of "u" with this set. + */ +__isl_give UNION *FN(UNION,intersect_domain)(__isl_take UNION *u, + __isl_take isl_union_set *uset) +{ + if (isl_union_set_is_params(uset)) + return FN(UNION,intersect_params)(u, + isl_set_from_union_set(uset)); + return FN(UNION,match_domain_op)(u, uset, &FN(PW,intersect_domain)); +} + +/* Take the set (which may be empty) in data->uset that lives + * in the same space as the domain of "pw", subtract it from the domain + * of "part" and return the result. + */ +static __isl_give PART *FN(UNION,subtract_domain_entry)(__isl_take PART *part, + void *user) +{ + isl_union_set *uset = user; + isl_space *space; + isl_set *set; + + space = FN(PART,get_domain_space)(part); + set = isl_union_set_extract_set(uset, space); + return FN(PART,subtract_domain)(part, set); +} + +/* Subtract "uset' from the domain of "u". + */ +__isl_give UNION *FN(UNION,subtract_domain)(__isl_take UNION *u, + __isl_take isl_union_set *uset) +{ + u = FN(UNION,transform)(u, &FN(UNION,subtract_domain_entry), uset); + isl_union_set_free(uset); + return u; +} + +__isl_give UNION *FN(UNION,gist)(__isl_take UNION *u, + __isl_take isl_union_set *uset) +{ + if (isl_union_set_is_params(uset)) + return FN(UNION,gist_params)(u, isl_set_from_union_set(uset)); + return FN(UNION,match_domain_op)(u, uset, &FN(PW,gist)); +} + +/* Coalesce an entry in a UNION. Coalescing is performed in-place. + * Since the UNION may have several references, the entry is only + * replaced if the coalescing is successful. + */ +static isl_stat FN(UNION,coalesce_entry)(void **entry, void *user) +{ + PART **part_p = (PART **) entry; + PART *part; + + part = FN(PART,copy)(*part_p); + part = FN(PW,coalesce)(part); + if (!part) + return isl_stat_error; + FN(PART,free)(*part_p); + *part_p = part; + + return isl_stat_ok; +} + +__isl_give UNION *FN(UNION,coalesce)(__isl_take UNION *u) +{ + if (FN(UNION,foreach_inplace)(u, &FN(UNION,coalesce_entry), NULL) < 0) + goto error; + + return u; +error: + FN(UNION,free)(u); + return NULL; +} + +static isl_stat FN(UNION,domain_entry)(__isl_take PART *part, void *user) +{ + isl_union_set **uset = (isl_union_set **)user; + + *uset = isl_union_set_add_set(*uset, FN(PART,domain)(part)); + + return isl_stat_ok; +} + +__isl_give isl_union_set *FN(UNION,domain)(__isl_take UNION *u) +{ + isl_union_set *uset; + + uset = isl_union_set_empty(FN(UNION,get_space)(u)); + if (FN(FN(UNION,foreach),PARTS)(u, &FN(UNION,domain_entry), &uset) < 0) + goto error; + + FN(UNION,free)(u); + + return uset; +error: + isl_union_set_free(uset); + FN(UNION,free)(u); + return NULL; +} + +#ifdef HAS_TYPE +/* Negate the type of "u". + */ +static __isl_give UNION *FN(UNION,negate_type)(__isl_take UNION *u) +{ + u = FN(UNION,cow)(u); + if (!u) + return NULL; + u->type = isl_fold_type_negate(u->type); + return u; +} +#else +/* Negate the type of "u". + * Since "u" does not have a type, do nothing. + */ +static __isl_give UNION *FN(UNION,negate_type)(__isl_take UNION *u) +{ + return u; +} +#endif + +static __isl_give PART *FN(UNION,mul_isl_int_entry)(__isl_take PART *part, + void *user) +{ + isl_int *v = user; + + return FN(PW,mul_isl_int)(part, *v); +} + +__isl_give UNION *FN(UNION,mul_isl_int)(__isl_take UNION *u, isl_int v) +{ + if (isl_int_is_one(v)) + return u; + + if (DEFAULT_IS_ZERO && u && isl_int_is_zero(v)) { + UNION *zero; + isl_space *dim = FN(UNION,get_space)(u); +#ifdef HAS_TYPE + zero = FN(UNION,ZERO)(dim, u->type); +#else + zero = FN(UNION,ZERO)(dim); +#endif + FN(UNION,free)(u); + return zero; + } + + u = FN(UNION,transform_inplace)(u, &FN(UNION,mul_isl_int_entry), &v); + if (isl_int_is_neg(v)) + u = FN(UNION,negate_type)(u); + + return u; +} + +/* Multiply "part" by the isl_val "user" and return the result. + */ +static __isl_give PART *FN(UNION,scale_val_entry)(__isl_take PART *part, + void *user) +{ + isl_val *v = user; + + return FN(PART,scale_val)(part, isl_val_copy(v)); +} + +/* Multiply "u" by "v" and return the result. + */ +__isl_give UNION *FN(UNION,scale_val)(__isl_take UNION *u, + __isl_take isl_val *v) +{ + if (!u || !v) + goto error; + if (isl_val_is_one(v)) { + isl_val_free(v); + return u; + } + + if (DEFAULT_IS_ZERO && u && isl_val_is_zero(v)) { + UNION *zero; + isl_space *space = FN(UNION,get_space)(u); +#ifdef HAS_TYPE + zero = FN(UNION,ZERO)(space, u->type); +#else + zero = FN(UNION,ZERO)(space); +#endif + FN(UNION,free)(u); + isl_val_free(v); + return zero; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational factor", goto error); + + u = FN(UNION,transform_inplace)(u, &FN(UNION,scale_val_entry), v); + if (isl_val_is_neg(v)) + u = FN(UNION,negate_type)(u); + + isl_val_free(v); + return u; +error: + isl_val_free(v); + FN(UNION,free)(u); + return NULL; +} + +/* Divide "part" by the isl_val "user" and return the result. + */ +static __isl_give PART *FN(UNION,scale_down_val_entry)(__isl_take PART *part, + void *user) +{ + isl_val *v = user; + + return FN(PART,scale_down_val)(part, isl_val_copy(v)); +} + +/* Divide "u" by "v" and return the result. + */ +__isl_give UNION *FN(UNION,scale_down_val)(__isl_take UNION *u, + __isl_take isl_val *v) +{ + if (!u || !v) + goto error; + if (isl_val_is_one(v)) { + isl_val_free(v); + return u; + } + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational factor", goto error); + if (isl_val_is_zero(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "cannot scale down by zero", goto error); + + u = FN(UNION,transform_inplace)(u, &FN(UNION,scale_down_val_entry), v); + if (isl_val_is_neg(v)) + u = FN(UNION,negate_type)(u); + + isl_val_free(v); + return u; +error: + isl_val_free(v); + FN(UNION,free)(u); + return NULL; +} + +S(UNION,plain_is_equal_data) +{ + UNION *u2; + isl_bool is_equal; +}; + +static isl_stat FN(UNION,plain_is_equal_entry)(void **entry, void *user) +{ + S(UNION,plain_is_equal_data) *data = user; + struct isl_hash_table_entry *entry2; + PW *pw = *entry; + + entry2 = FN(UNION,find_part_entry)(data->u2, pw->dim, 0); + if (!entry2 || entry2 == isl_hash_table_entry_none) { + if (!entry2) + data->is_equal = isl_bool_error; + else + data->is_equal = isl_bool_false; + return isl_stat_error; + } + + data->is_equal = FN(PW,plain_is_equal)(pw, entry2->data); + if (data->is_equal < 0 || !data->is_equal) + return isl_stat_error; + + return isl_stat_ok; +} + +isl_bool FN(UNION,plain_is_equal)(__isl_keep UNION *u1, __isl_keep UNION *u2) +{ + S(UNION,plain_is_equal_data) data = { NULL, isl_bool_true }; + int n1, n2; + + if (!u1 || !u2) + return isl_bool_error; + if (u1 == u2) + return isl_bool_true; + if (u1->table.n != u2->table.n) + return isl_bool_false; + n1 = FN(FN(UNION,n),PARTS)(u1); + n2 = FN(FN(UNION,n),PARTS)(u2); + if (n1 < 0 || n2 < 0) + return isl_bool_error; + if (n1 != n2) + return isl_bool_false; + + u1 = FN(UNION,copy)(u1); + u2 = FN(UNION,copy)(u2); + u1 = FN(UNION,align_params)(u1, FN(UNION,get_space)(u2)); + u2 = FN(UNION,align_params)(u2, FN(UNION,get_space)(u1)); + if (!u1 || !u2) + goto error; + + data.u2 = u2; + if (FN(UNION,foreach_inplace)(u1, + &FN(UNION,plain_is_equal_entry), &data) < 0 && + data.is_equal) + goto error; + + FN(UNION,free)(u1); + FN(UNION,free)(u2); + + return data.is_equal; +error: + FN(UNION,free)(u1); + FN(UNION,free)(u2); + return isl_bool_error; +} + +/* Internal data structure for isl_union_*_drop_dims. + * type, first and n are passed to isl_*_drop_dims. + */ +S(UNION,drop_dims_data) { + enum isl_dim_type type; + unsigned first; + unsigned n; +}; + +/* Drop the parameters specified by "data" from "part" and return the result. + */ +static __isl_give PART *FN(UNION,drop_dims_entry)(__isl_take PART *part, + void *user) +{ + S(UNION,drop_dims_data) *data = user; + + return FN(PART,drop_dims)(part, data->type, data->first, data->n); +} + +/* Drop the specified parameters from "u". + * That is, type is required to be isl_dim_param. + */ +__isl_give UNION *FN(UNION,drop_dims)( __isl_take UNION *u, + enum isl_dim_type type, unsigned first, unsigned n) +{ + isl_space *space; + S(UNION,drop_dims_data) data = { type, first, n }; + + if (!u) + return NULL; + + if (type != isl_dim_param) + isl_die(FN(UNION,get_ctx)(u), isl_error_invalid, + "can only project out parameters", + return FN(UNION,free)(u)); + + space = FN(UNION,get_space)(u); + space = isl_space_drop_dims(space, type, first, n); + return FN(UNION,transform_space)(u, space, &FN(UNION,drop_dims_entry), + &data); +} + +/* Internal data structure for isl_union_*_set_dim_name. + * pos is the position of the parameter that needs to be renamed. + * s is the new name. + */ +S(UNION,set_dim_name_data) { + unsigned pos; + const char *s; +}; + +/* Change the name of the parameter at position data->pos of "part" to data->s + * and return the result. + */ +static __isl_give PART *FN(UNION,set_dim_name_entry)(__isl_take PART *part, + void *user) +{ + S(UNION,set_dim_name_data) *data = user; + + return FN(PART,set_dim_name)(part, isl_dim_param, data->pos, data->s); +} + +/* Change the name of the parameter at position "pos" to "s". + * That is, type is required to be isl_dim_param. + */ +__isl_give UNION *FN(UNION,set_dim_name)(__isl_take UNION *u, + enum isl_dim_type type, unsigned pos, const char *s) +{ + S(UNION,set_dim_name_data) data = { pos, s }; + isl_space *space; + + if (!u) + return NULL; + + if (type != isl_dim_param) + isl_die(FN(UNION,get_ctx)(u), isl_error_invalid, + "can only set parameter names", + return FN(UNION,free)(u)); + + space = FN(UNION,get_space)(u); + space = isl_space_set_dim_name(space, type, pos, s); + return FN(UNION,transform_space)(u, space, + &FN(UNION,set_dim_name_entry), &data); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the space of "part" and return the result. + */ +static __isl_give PART *FN(UNION,reset_user_entry)(__isl_take PART *part, + void *user) +{ + return FN(PART,reset_user)(part); +} + +/* Reset the user pointer on all identifiers of parameters and tuples + * of the spaces of "u". + */ +__isl_give UNION *FN(UNION,reset_user)(__isl_take UNION *u) +{ + isl_space *space; + + space = FN(UNION,get_space)(u); + space = isl_space_reset_user(space); + return FN(UNION,transform_space)(u, space, &FN(UNION,reset_user_entry), + NULL); +} Index: lib/Analysis/isl/isl_val.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_val.c @@ -0,0 +1,1664 @@ +/* + * Copyright 2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, + * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France + */ + +#include +#include +#include + +#undef BASE +#define BASE val + +#include + +/* Allocate an isl_val object with indeterminate value. + */ +__isl_give isl_val *isl_val_alloc(isl_ctx *ctx) +{ + isl_val *v; + + v = isl_alloc_type(ctx, struct isl_val); + if (!v) + return NULL; + + v->ctx = ctx; + isl_ctx_ref(ctx); + v->ref = 1; + isl_int_init(v->n); + isl_int_init(v->d); + + return v; +} + +/* Return a reference to an isl_val representing zero. + */ +__isl_give isl_val *isl_val_zero(isl_ctx *ctx) +{ + return isl_val_int_from_si(ctx, 0); +} + +/* Return a reference to an isl_val representing one. + */ +__isl_give isl_val *isl_val_one(isl_ctx *ctx) +{ + return isl_val_int_from_si(ctx, 1); +} + +/* Return a reference to an isl_val representing negative one. + */ +__isl_give isl_val *isl_val_negone(isl_ctx *ctx) +{ + return isl_val_int_from_si(ctx, -1); +} + +/* Return a reference to an isl_val representing NaN. + */ +__isl_give isl_val *isl_val_nan(isl_ctx *ctx) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set_si(v->n, 0); + isl_int_set_si(v->d, 0); + + return v; +} + +/* Change "v" into a NaN. + */ +__isl_give isl_val *isl_val_set_nan(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_nan(v)) + return v; + v = isl_val_cow(v); + if (!v) + return NULL; + + isl_int_set_si(v->n, 0); + isl_int_set_si(v->d, 0); + + return v; +} + +/* Return a reference to an isl_val representing +infinity. + */ +__isl_give isl_val *isl_val_infty(isl_ctx *ctx) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set_si(v->n, 1); + isl_int_set_si(v->d, 0); + + return v; +} + +/* Return a reference to an isl_val representing -infinity. + */ +__isl_give isl_val *isl_val_neginfty(isl_ctx *ctx) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set_si(v->n, -1); + isl_int_set_si(v->d, 0); + + return v; +} + +/* Return a reference to an isl_val representing the integer "i". + */ +__isl_give isl_val *isl_val_int_from_si(isl_ctx *ctx, long i) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set_si(v->n, i); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Change the value of "v" to be equal to the integer "i". + */ +__isl_give isl_val *isl_val_set_si(__isl_take isl_val *v, long i) +{ + if (!v) + return NULL; + if (isl_val_is_int(v) && isl_int_cmp_si(v->n, i) == 0) + return v; + v = isl_val_cow(v); + if (!v) + return NULL; + + isl_int_set_si(v->n, i); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Change the value of "v" to be equal to zero. + */ +__isl_give isl_val *isl_val_set_zero(__isl_take isl_val *v) +{ + return isl_val_set_si(v, 0); +} + +/* Return a reference to an isl_val representing the unsigned integer "u". + */ +__isl_give isl_val *isl_val_int_from_ui(isl_ctx *ctx, unsigned long u) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set_ui(v->n, u); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Return a reference to an isl_val representing the integer "n". + */ +__isl_give isl_val *isl_val_int_from_isl_int(isl_ctx *ctx, isl_int n) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set(v->n, n); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Return a reference to an isl_val representing the rational value "n"/"d". + * Normalizing the isl_val (if needed) is left to the caller. + */ +__isl_give isl_val *isl_val_rat_from_isl_int(isl_ctx *ctx, + isl_int n, isl_int d) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set(v->n, n); + isl_int_set(v->d, d); + + return v; +} + +/* Return a new reference to "v". + */ +__isl_give isl_val *isl_val_copy(__isl_keep isl_val *v) +{ + if (!v) + return NULL; + + v->ref++; + return v; +} + +/* Return a fresh copy of "val". + */ +__isl_give isl_val *isl_val_dup(__isl_keep isl_val *val) +{ + isl_val *dup; + + if (!val) + return NULL; + + dup = isl_val_alloc(isl_val_get_ctx(val)); + if (!dup) + return NULL; + + isl_int_set(dup->n, val->n); + isl_int_set(dup->d, val->d); + + return dup; +} + +/* Return an isl_val that is equal to "val" and that has only + * a single reference. + */ +__isl_give isl_val *isl_val_cow(__isl_take isl_val *val) +{ + if (!val) + return NULL; + + if (val->ref == 1) + return val; + val->ref--; + return isl_val_dup(val); +} + +/* Free "v" and return NULL. + */ +__isl_null isl_val *isl_val_free(__isl_take isl_val *v) +{ + if (!v) + return NULL; + + if (--v->ref > 0) + return NULL; + + isl_ctx_deref(v->ctx); + isl_int_clear(v->n); + isl_int_clear(v->d); + free(v); + return NULL; +} + +/* Extract the numerator of a rational value "v" as an integer. + * + * If "v" is not a rational value, then the result is undefined. + */ +long isl_val_get_num_si(__isl_keep isl_val *v) +{ + if (!v) + return 0; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return 0); + if (!isl_int_fits_slong(v->n)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "numerator too large", return 0); + return isl_int_get_si(v->n); +} + +/* Extract the numerator of a rational value "v" as an isl_int. + * + * If "v" is not a rational value, then the result is undefined. + */ +int isl_val_get_num_isl_int(__isl_keep isl_val *v, isl_int *n) +{ + if (!v) + return -1; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return -1); + isl_int_set(*n, v->n); + return 0; +} + +/* Extract the denominator of a rational value "v" as an integer. + * + * If "v" is not a rational value, then the result is undefined. + */ +long isl_val_get_den_si(__isl_keep isl_val *v) +{ + if (!v) + return 0; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return 0); + if (!isl_int_fits_slong(v->d)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "denominator too large", return 0); + return isl_int_get_si(v->d); +} + +/* Extract the denominator of a rational value "v" as an isl_val. + * + * If "v" is not a rational value, then the result is undefined. + */ +__isl_give isl_val *isl_val_get_den_val(__isl_keep isl_val *v) +{ + if (!v) + return NULL; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return NULL); + return isl_val_int_from_isl_int(isl_val_get_ctx(v), v->d); +} + +/* Return an approximation of "v" as a double. + */ +double isl_val_get_d(__isl_keep isl_val *v) +{ + if (!v) + return 0; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return 0); + return isl_int_get_d(v->n) / isl_int_get_d(v->d); +} + +/* Return the isl_ctx to which "val" belongs. + */ +isl_ctx *isl_val_get_ctx(__isl_keep isl_val *val) +{ + return val ? val->ctx : NULL; +} + +/* Return a hash value that digests "val". + */ +uint32_t isl_val_get_hash(__isl_keep isl_val *val) +{ + uint32_t hash; + + if (!val) + return 0; + + hash = isl_hash_init(); + hash = isl_int_hash(val->n, hash); + hash = isl_int_hash(val->d, hash); + + return hash; +} + +/* Normalize "v". + * + * In particular, make sure that the denominator of a rational value + * is positive and the numerator and denominator do not have any + * common divisors. + * + * This function should not be called by an external user + * since it will only be given normalized values. + */ +__isl_give isl_val *isl_val_normalize(__isl_take isl_val *v) +{ + isl_ctx *ctx; + + if (!v) + return NULL; + if (isl_val_is_int(v)) + return v; + if (!isl_val_is_rat(v)) + return v; + if (isl_int_is_neg(v->d)) { + isl_int_neg(v->d, v->d); + isl_int_neg(v->n, v->n); + } + ctx = isl_val_get_ctx(v); + isl_int_gcd(ctx->normalize_gcd, v->n, v->d); + if (isl_int_is_one(ctx->normalize_gcd)) + return v; + isl_int_divexact(v->n, v->n, ctx->normalize_gcd); + isl_int_divexact(v->d, v->d, ctx->normalize_gcd); + return v; +} + +/* Return the opposite of "v". + */ +__isl_give isl_val *isl_val_neg(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_nan(v)) + return v; + if (isl_val_is_zero(v)) + return v; + + v = isl_val_cow(v); + if (!v) + return NULL; + isl_int_neg(v->n, v->n); + + return v; +} + +/* Return the inverse of "v". + */ +__isl_give isl_val *isl_val_inv(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_nan(v)) + return v; + if (isl_val_is_zero(v)) { + isl_ctx *ctx = isl_val_get_ctx(v); + isl_val_free(v); + return isl_val_nan(ctx); + } + if (isl_val_is_infty(v) || isl_val_is_neginfty(v)) { + isl_ctx *ctx = isl_val_get_ctx(v); + isl_val_free(v); + return isl_val_zero(ctx); + } + + v = isl_val_cow(v); + if (!v) + return NULL; + isl_int_swap(v->n, v->d); + + return isl_val_normalize(v); +} + +/* Return the absolute value of "v". + */ +__isl_give isl_val *isl_val_abs(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_nan(v)) + return v; + if (isl_val_is_nonneg(v)) + return v; + return isl_val_neg(v); +} + +/* Return the "floor" (greatest integer part) of "v". + * That is, return the result of rounding towards -infinity. + */ +__isl_give isl_val *isl_val_floor(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_int(v)) + return v; + if (!isl_val_is_rat(v)) + return v; + + v = isl_val_cow(v); + if (!v) + return NULL; + isl_int_fdiv_q(v->n, v->n, v->d); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Return the "ceiling" of "v". + * That is, return the result of rounding towards +infinity. + */ +__isl_give isl_val *isl_val_ceil(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_int(v)) + return v; + if (!isl_val_is_rat(v)) + return v; + + v = isl_val_cow(v); + if (!v) + return NULL; + isl_int_cdiv_q(v->n, v->n, v->d); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Truncate "v". + * That is, return the result of rounding towards zero. + */ +__isl_give isl_val *isl_val_trunc(__isl_take isl_val *v) +{ + if (!v) + return NULL; + if (isl_val_is_int(v)) + return v; + if (!isl_val_is_rat(v)) + return v; + + v = isl_val_cow(v); + if (!v) + return NULL; + isl_int_tdiv_q(v->n, v->n, v->d); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Return 2^v, where v is an integer (that is not too large). + */ +__isl_give isl_val *isl_val_2exp(__isl_take isl_val *v) +{ + unsigned long exp; + int neg; + + v = isl_val_cow(v); + if (!v) + return NULL; + if (!isl_val_is_int(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "can only compute integer powers", + return isl_val_free(v)); + neg = isl_val_is_neg(v); + if (neg) + isl_int_neg(v->n, v->n); + if (!isl_int_fits_ulong(v->n)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "exponent too large", return isl_val_free(v)); + exp = isl_int_get_ui(v->n); + if (neg) { + isl_int_mul_2exp(v->d, v->d, exp); + isl_int_set_si(v->n, 1); + } else { + isl_int_mul_2exp(v->n, v->d, exp); + } + + return v; +} + +/* Return the minimum of "v1" and "v2". + */ +__isl_give isl_val *isl_val_min(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + + if (isl_val_is_nan(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v1); + return v2; + } + if (isl_val_le(v1, v2)) { + isl_val_free(v2); + return v1; + } else { + isl_val_free(v1); + return v2; + } +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Return the maximum of "v1" and "v2". + */ +__isl_give isl_val *isl_val_max(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + + if (isl_val_is_nan(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v1); + return v2; + } + if (isl_val_ge(v1, v2)) { + isl_val_free(v2); + return v1; + } else { + isl_val_free(v1); + return v2; + } +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Return the sum of "v1" and "v2". + */ +__isl_give isl_val *isl_val_add(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + if (isl_val_is_nan(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v1); + return v2; + } + if ((isl_val_is_infty(v1) && isl_val_is_neginfty(v2)) || + (isl_val_is_neginfty(v1) && isl_val_is_infty(v2))) { + isl_val_free(v2); + return isl_val_set_nan(v1); + } + if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) { + isl_val_free(v1); + return v2; + } + if (isl_val_is_zero(v1)) { + isl_val_free(v1); + return v2; + } + if (isl_val_is_zero(v2)) { + isl_val_free(v2); + return v1; + } + + v1 = isl_val_cow(v1); + if (!v1) + goto error; + if (isl_val_is_int(v1) && isl_val_is_int(v2)) + isl_int_add(v1->n, v1->n, v2->n); + else { + if (isl_int_eq(v1->d, v2->d)) + isl_int_add(v1->n, v1->n, v2->n); + else { + isl_int_mul(v1->n, v1->n, v2->d); + isl_int_addmul(v1->n, v2->n, v1->d); + isl_int_mul(v1->d, v1->d, v2->d); + } + v1 = isl_val_normalize(v1); + } + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Return the sum of "v1" and "v2". + */ +__isl_give isl_val *isl_val_add_ui(__isl_take isl_val *v1, unsigned long v2) +{ + if (!v1) + return NULL; + if (!isl_val_is_rat(v1)) + return v1; + if (v2 == 0) + return v1; + v1 = isl_val_cow(v1); + if (!v1) + return NULL; + + isl_int_addmul_ui(v1->n, v1->d, v2); + + return v1; +} + +/* Subtract "v2" from "v1". + */ +__isl_give isl_val *isl_val_sub(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + if (isl_val_is_nan(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v1); + return v2; + } + if ((isl_val_is_infty(v1) && isl_val_is_infty(v2)) || + (isl_val_is_neginfty(v1) && isl_val_is_neginfty(v2))) { + isl_val_free(v2); + return isl_val_set_nan(v1); + } + if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) { + isl_val_free(v1); + return isl_val_neg(v2); + } + if (isl_val_is_zero(v2)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_zero(v1)) { + isl_val_free(v1); + return isl_val_neg(v2); + } + + v1 = isl_val_cow(v1); + if (!v1) + goto error; + if (isl_val_is_int(v1) && isl_val_is_int(v2)) + isl_int_sub(v1->n, v1->n, v2->n); + else { + if (isl_int_eq(v1->d, v2->d)) + isl_int_sub(v1->n, v1->n, v2->n); + else { + isl_int_mul(v1->n, v1->n, v2->d); + isl_int_submul(v1->n, v2->n, v1->d); + isl_int_mul(v1->d, v1->d, v2->d); + } + v1 = isl_val_normalize(v1); + } + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Subtract "v2" from "v1". + */ +__isl_give isl_val *isl_val_sub_ui(__isl_take isl_val *v1, unsigned long v2) +{ + if (!v1) + return NULL; + if (!isl_val_is_rat(v1)) + return v1; + if (v2 == 0) + return v1; + v1 = isl_val_cow(v1); + if (!v1) + return NULL; + + isl_int_submul_ui(v1->n, v1->d, v2); + + return v1; +} + +/* Return the product of "v1" and "v2". + */ +__isl_give isl_val *isl_val_mul(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + if (isl_val_is_nan(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v1); + return v2; + } + if ((!isl_val_is_rat(v1) && isl_val_is_zero(v2)) || + (isl_val_is_zero(v1) && !isl_val_is_rat(v2))) { + isl_val_free(v2); + return isl_val_set_nan(v1); + } + if (isl_val_is_zero(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_zero(v2)) { + isl_val_free(v1); + return v2; + } + if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) { + if (isl_val_is_neg(v2)) + v1 = isl_val_neg(v1); + isl_val_free(v2); + return v1; + } + if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) { + if (isl_val_is_neg(v1)) + v2 = isl_val_neg(v2); + isl_val_free(v1); + return v2; + } + + v1 = isl_val_cow(v1); + if (!v1) + goto error; + if (isl_val_is_int(v1) && isl_val_is_int(v2)) + isl_int_mul(v1->n, v1->n, v2->n); + else { + isl_int_mul(v1->n, v1->n, v2->n); + isl_int_mul(v1->d, v1->d, v2->d); + v1 = isl_val_normalize(v1); + } + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Return the product of "v1" and "v2". + * + * This is a private copy of isl_val_mul for use in the generic + * isl_multi_*_scale_val instantiated for isl_val. + */ +__isl_give isl_val *isl_val_scale_val(__isl_take isl_val *v1, + __isl_take isl_val *v2) +{ + return isl_val_mul(v1, v2); +} + +/* Return the product of "v1" and "v2". + */ +__isl_give isl_val *isl_val_mul_ui(__isl_take isl_val *v1, unsigned long v2) +{ + if (!v1) + return NULL; + if (isl_val_is_nan(v1)) + return v1; + if (!isl_val_is_rat(v1)) { + if (v2 == 0) + v1 = isl_val_set_nan(v1); + return v1; + } + if (v2 == 1) + return v1; + v1 = isl_val_cow(v1); + if (!v1) + return NULL; + + isl_int_mul_ui(v1->n, v1->n, v2); + + return isl_val_normalize(v1); +} + +/* Divide "v1" by "v2". + */ +__isl_give isl_val *isl_val_div(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + if (isl_val_is_nan(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_nan(v2)) { + isl_val_free(v1); + return v2; + } + if (isl_val_is_zero(v2) || + (!isl_val_is_rat(v1) && !isl_val_is_rat(v2))) { + isl_val_free(v2); + return isl_val_set_nan(v1); + } + if (isl_val_is_zero(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) { + if (isl_val_is_neg(v2)) + v1 = isl_val_neg(v1); + isl_val_free(v2); + return v1; + } + if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) { + isl_val_free(v2); + return isl_val_set_zero(v1); + } + + v1 = isl_val_cow(v1); + if (!v1) + goto error; + if (isl_val_is_int(v2)) { + isl_int_mul(v1->d, v1->d, v2->n); + v1 = isl_val_normalize(v1); + } else { + isl_int_mul(v1->d, v1->d, v2->n); + isl_int_mul(v1->n, v1->n, v2->d); + v1 = isl_val_normalize(v1); + } + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Divide "v1" by "v2". + * + * This is a private copy of isl_val_div for use in the generic + * isl_multi_*_scale_down_val instantiated for isl_val. + */ +__isl_give isl_val *isl_val_scale_down_val(__isl_take isl_val *v1, + __isl_take isl_val *v2) +{ + return isl_val_div(v1, v2); +} + +/* Given two integer values "v1" and "v2", check if "v1" is divisible by "v2". + */ +isl_bool isl_val_is_divisible_by(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + if (!v1 || !v2) + return isl_bool_error; + + if (!isl_val_is_int(v1) || !isl_val_is_int(v2)) + isl_die(isl_val_get_ctx(v1), isl_error_invalid, + "expecting two integers", return isl_bool_error); + + return isl_int_is_divisible_by(v1->n, v2->n); +} + +/* Given two integer values "v1" and "v2", return the residue of "v1" + * modulo "v2". + */ +__isl_give isl_val *isl_val_mod(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + if (!isl_val_is_int(v1) || !isl_val_is_int(v2)) + isl_die(isl_val_get_ctx(v1), isl_error_invalid, + "expecting two integers", goto error); + if (isl_val_is_nonneg(v1) && isl_val_lt(v1, v2)) { + isl_val_free(v2); + return v1; + } + v1 = isl_val_cow(v1); + if (!v1) + goto error; + isl_int_fdiv_r(v1->n, v1->n, v2->n); + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Given two integer values "v1" and "v2", return the residue of "v1" + * modulo "v2". + * + * This is a private copy of isl_val_mod for use in the generic + * isl_multi_*_mod_multi_val instantiated for isl_val. + */ +__isl_give isl_val *isl_val_mod_val(__isl_take isl_val *v1, + __isl_take isl_val *v2) +{ + return isl_val_mod(v1, v2); +} + +/* Given two integer values, return their greatest common divisor. + */ +__isl_give isl_val *isl_val_gcd(__isl_take isl_val *v1, __isl_take isl_val *v2) +{ + if (!v1 || !v2) + goto error; + if (!isl_val_is_int(v1) || !isl_val_is_int(v2)) + isl_die(isl_val_get_ctx(v1), isl_error_invalid, + "expecting two integers", goto error); + if (isl_val_eq(v1, v2)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_one(v1)) { + isl_val_free(v2); + return v1; + } + if (isl_val_is_one(v2)) { + isl_val_free(v1); + return v2; + } + v1 = isl_val_cow(v1); + if (!v1) + goto error; + isl_int_gcd(v1->n, v1->n, v2->n); + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + return NULL; +} + +/* Compute x, y and g such that g = gcd(a,b) and a*x+b*y = g. + */ +static void isl_int_gcdext(isl_int *g, isl_int *x, isl_int *y, + isl_int a, isl_int b) +{ + isl_int d, tmp; + isl_int a_copy, b_copy; + + isl_int_init(a_copy); + isl_int_init(b_copy); + isl_int_init(d); + isl_int_init(tmp); + isl_int_set(a_copy, a); + isl_int_set(b_copy, b); + isl_int_abs(*g, a_copy); + isl_int_abs(d, b_copy); + isl_int_set_si(*x, 1); + isl_int_set_si(*y, 0); + while (isl_int_is_pos(d)) { + isl_int_fdiv_q(tmp, *g, d); + isl_int_submul(*x, tmp, *y); + isl_int_submul(*g, tmp, d); + isl_int_swap(*g, d); + isl_int_swap(*x, *y); + } + if (isl_int_is_zero(a_copy)) + isl_int_set_si(*x, 0); + else if (isl_int_is_neg(a_copy)) + isl_int_neg(*x, *x); + if (isl_int_is_zero(b_copy)) + isl_int_set_si(*y, 0); + else { + isl_int_mul(tmp, a_copy, *x); + isl_int_sub(tmp, *g, tmp); + isl_int_divexact(*y, tmp, b_copy); + } + isl_int_clear(d); + isl_int_clear(tmp); + isl_int_clear(a_copy); + isl_int_clear(b_copy); +} + +/* Given two integer values v1 and v2, return their greatest common divisor g, + * as well as two integers x and y such that x * v1 + y * v2 = g. + */ +__isl_give isl_val *isl_val_gcdext(__isl_take isl_val *v1, + __isl_take isl_val *v2, __isl_give isl_val **x, __isl_give isl_val **y) +{ + isl_ctx *ctx; + isl_val *a = NULL, *b = NULL; + + if (!x && !y) + return isl_val_gcd(v1, v2); + + if (!v1 || !v2) + goto error; + + ctx = isl_val_get_ctx(v1); + if (!isl_val_is_int(v1) || !isl_val_is_int(v2)) + isl_die(ctx, isl_error_invalid, + "expecting two integers", goto error); + + v1 = isl_val_cow(v1); + a = isl_val_alloc(ctx); + b = isl_val_alloc(ctx); + if (!v1 || !a || !b) + goto error; + isl_int_gcdext(&v1->n, &a->n, &b->n, v1->n, v2->n); + if (x) { + isl_int_set_si(a->d, 1); + *x = a; + } else + isl_val_free(a); + if (y) { + isl_int_set_si(b->d, 1); + *y = b; + } else + isl_val_free(b); + isl_val_free(v2); + return v1; +error: + isl_val_free(v1); + isl_val_free(v2); + isl_val_free(a); + isl_val_free(b); + if (x) + *x = NULL; + if (y) + *y = NULL; + return NULL; +} + +/* Does "v" represent an integer value? + */ +isl_bool isl_val_is_int(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_one(v->d); +} + +/* Does "v" represent a rational value? + */ +isl_bool isl_val_is_rat(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return !isl_int_is_zero(v->d); +} + +/* Does "v" represent NaN? + */ +isl_bool isl_val_is_nan(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_zero(v->n) && isl_int_is_zero(v->d); +} + +/* Does "v" represent +infinity? + */ +isl_bool isl_val_is_infty(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_pos(v->n) && isl_int_is_zero(v->d); +} + +/* Does "v" represent -infinity? + */ +isl_bool isl_val_is_neginfty(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_neg(v->n) && isl_int_is_zero(v->d); +} + +/* Does "v" represent the integer zero? + */ +isl_bool isl_val_is_zero(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_zero(v->n) && !isl_int_is_zero(v->d); +} + +/* Does "v" represent the integer one? + */ +isl_bool isl_val_is_one(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_eq(v->n, v->d); +} + +/* Does "v" represent the integer negative one? + */ +isl_bool isl_val_is_negone(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_neg(v->n) && isl_int_abs_eq(v->n, v->d); +} + +/* Is "v" (strictly) positive? + */ +isl_bool isl_val_is_pos(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_pos(v->n); +} + +/* Is "v" (strictly) negative? + */ +isl_bool isl_val_is_neg(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + return isl_int_is_neg(v->n); +} + +/* Is "v" non-negative? + */ +isl_bool isl_val_is_nonneg(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + if (isl_val_is_nan(v)) + return isl_bool_false; + + return isl_int_is_nonneg(v->n); +} + +/* Is "v" non-positive? + */ +isl_bool isl_val_is_nonpos(__isl_keep isl_val *v) +{ + if (!v) + return isl_bool_error; + + if (isl_val_is_nan(v)) + return isl_bool_false; + + return isl_int_is_nonpos(v->n); +} + +/* Return the sign of "v". + * + * The sign of NaN is undefined. + */ +int isl_val_sgn(__isl_keep isl_val *v) +{ + if (!v) + return 0; + if (isl_val_is_zero(v)) + return 0; + if (isl_val_is_pos(v)) + return 1; + return -1; +} + +/* Is "v1" (strictly) less than "v2"? + */ +isl_bool isl_val_lt(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + isl_int t; + isl_bool lt; + + if (!v1 || !v2) + return isl_bool_error; + if (isl_val_is_int(v1) && isl_val_is_int(v2)) + return isl_int_lt(v1->n, v2->n); + if (isl_val_is_nan(v1) || isl_val_is_nan(v2)) + return isl_bool_false; + if (isl_val_eq(v1, v2)) + return isl_bool_false; + if (isl_val_is_infty(v2)) + return isl_bool_true; + if (isl_val_is_infty(v1)) + return isl_bool_false; + if (isl_val_is_neginfty(v1)) + return isl_bool_true; + if (isl_val_is_neginfty(v2)) + return isl_bool_false; + + isl_int_init(t); + isl_int_mul(t, v1->n, v2->d); + isl_int_submul(t, v2->n, v1->d); + lt = isl_int_is_neg(t); + isl_int_clear(t); + + return lt; +} + +/* Is "v1" (strictly) greater than "v2"? + */ +isl_bool isl_val_gt(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + return isl_val_lt(v2, v1); +} + +/* Is "v1" less than or equal to "v2"? + */ +isl_bool isl_val_le(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + isl_int t; + isl_bool le; + + if (!v1 || !v2) + return isl_bool_error; + if (isl_val_is_int(v1) && isl_val_is_int(v2)) + return isl_int_le(v1->n, v2->n); + if (isl_val_is_nan(v1) || isl_val_is_nan(v2)) + return isl_bool_false; + if (isl_val_eq(v1, v2)) + return isl_bool_true; + if (isl_val_is_infty(v2)) + return isl_bool_true; + if (isl_val_is_infty(v1)) + return isl_bool_false; + if (isl_val_is_neginfty(v1)) + return isl_bool_true; + if (isl_val_is_neginfty(v2)) + return isl_bool_false; + + isl_int_init(t); + isl_int_mul(t, v1->n, v2->d); + isl_int_submul(t, v2->n, v1->d); + le = isl_int_is_nonpos(t); + isl_int_clear(t); + + return le; +} + +/* Is "v1" greater than or equal to "v2"? + */ +isl_bool isl_val_ge(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + return isl_val_le(v2, v1); +} + +/* How does "v" compare to "i"? + * + * Return 1 if v is greater, -1 if v is smaller and 0 if v is equal to i. + * + * If v is NaN (or NULL), then the result is undefined. + */ +int isl_val_cmp_si(__isl_keep isl_val *v, long i) +{ + isl_int t; + int cmp; + + if (!v) + return 0; + if (isl_val_is_int(v)) + return isl_int_cmp_si(v->n, i); + if (isl_val_is_nan(v)) + return 0; + if (isl_val_is_infty(v)) + return 1; + if (isl_val_is_neginfty(v)) + return -1; + + isl_int_init(t); + isl_int_mul_si(t, v->d, i); + isl_int_sub(t, v->n, t); + cmp = isl_int_sgn(t); + isl_int_clear(t); + + return cmp; +} + +/* Is "v1" equal to "v2"? + */ +isl_bool isl_val_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + if (!v1 || !v2) + return isl_bool_error; + if (isl_val_is_nan(v1) || isl_val_is_nan(v2)) + return isl_bool_false; + + return isl_int_eq(v1->n, v2->n) && isl_int_eq(v1->d, v2->d); +} + +/* Is "v1" equal to "v2" in absolute value? + */ +isl_bool isl_val_abs_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + if (!v1 || !v2) + return isl_bool_error; + if (isl_val_is_nan(v1) || isl_val_is_nan(v2)) + return isl_bool_false; + + return isl_int_abs_eq(v1->n, v2->n) && isl_int_eq(v1->d, v2->d); +} + +/* Is "v1" different from "v2"? + */ +isl_bool isl_val_ne(__isl_keep isl_val *v1, __isl_keep isl_val *v2) +{ + if (!v1 || !v2) + return isl_bool_error; + if (isl_val_is_nan(v1) || isl_val_is_nan(v2)) + return isl_bool_false; + + return isl_int_ne(v1->n, v2->n) || isl_int_ne(v1->d, v2->d); +} + +/* Print a textual representation of "v" onto "p". + */ +__isl_give isl_printer *isl_printer_print_val(__isl_take isl_printer *p, + __isl_keep isl_val *v) +{ + int neg; + + if (!p || !v) + return isl_printer_free(p); + + neg = isl_int_is_neg(v->n); + if (neg) { + p = isl_printer_print_str(p, "-"); + isl_int_neg(v->n, v->n); + } + if (isl_int_is_zero(v->d)) { + int sgn = isl_int_sgn(v->n); + p = isl_printer_print_str(p, sgn < 0 ? "-infty" : + sgn == 0 ? "NaN" : "infty"); + } else + p = isl_printer_print_isl_int(p, v->n); + if (neg) + isl_int_neg(v->n, v->n); + if (!isl_int_is_zero(v->d) && !isl_int_is_one(v->d)) { + p = isl_printer_print_str(p, "/"); + p = isl_printer_print_isl_int(p, v->d); + } + + return p; +} + +/* Is "val1" (obviously) equal to "val2"? + * + * This is a private copy of isl_val_eq for use in the generic + * isl_multi_*_plain_is_equal instantiated for isl_val. + */ +int isl_val_plain_is_equal(__isl_keep isl_val *val1, __isl_keep isl_val *val2) +{ + return isl_val_eq(val1, val2); +} + +/* Does "v" have any non-zero coefficients + * for any dimension in the given range? + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have any coefficients, this function + * always return 0. + */ +int isl_val_involves_dims(__isl_keep isl_val *v, enum isl_dim_type type, + unsigned first, unsigned n) +{ + if (!v) + return -1; + + return 0; +} + +/* Insert "n" dimensions of type "type" at position "first". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * does not do anything. + */ +__isl_give isl_val *isl_val_insert_dims(__isl_take isl_val *v, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return v; +} + +/* Drop the the "n" first dimensions of type "type" at position "first". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * does not do anything. + */ +__isl_give isl_val *isl_val_drop_dims(__isl_take isl_val *v, + enum isl_dim_type type, unsigned first, unsigned n) +{ + return v; +} + +/* Change the name of the dimension of type "type" at position "pos" to "s". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * does not do anything. + */ +__isl_give isl_val *isl_val_set_dim_name(__isl_take isl_val *v, + enum isl_dim_type type, unsigned pos, const char *s) +{ + return v; +} + +/* Return the space of "v". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. The conditions surrounding the call to this function make sure + * that this function will never actually get called. We return a valid + * space anyway, just in case. + */ +__isl_give isl_space *isl_val_get_space(__isl_keep isl_val *v) +{ + if (!v) + return NULL; + + return isl_space_params_alloc(isl_val_get_ctx(v), 0); +} + +/* Reset the domain space of "v" to "space". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * does not do anything, apart from error handling and cleaning up memory. + */ +__isl_give isl_val *isl_val_reset_domain_space(__isl_take isl_val *v, + __isl_take isl_space *space) +{ + if (!space) + return isl_val_free(v); + isl_space_free(space); + return v; +} + +/* Align the parameters of "v" to those of "space". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * does not do anything, apart from error handling and cleaning up memory. + * Note that the conditions surrounding the call to this function make sure + * that this function will never actually get called. + */ +__isl_give isl_val *isl_val_align_params(__isl_take isl_val *v, + __isl_take isl_space *space) +{ + if (!space) + return isl_val_free(v); + isl_space_free(space); + return v; +} + +/* Reorder the dimensions of the domain of "v" according + * to the given reordering. + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * does not do anything, apart from error handling and cleaning up memory. + */ +__isl_give isl_val *isl_val_realign_domain(__isl_take isl_val *v, + __isl_take isl_reordering *r) +{ + if (!r) + return isl_val_free(v); + isl_reordering_free(r); + return v; +} + +/* Return an isl_val that is zero on "ls". + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * simply returns a zero isl_val in the same context as "ls". + */ +__isl_give isl_val *isl_val_zero_on_domain(__isl_take isl_local_space *ls) +{ + isl_ctx *ctx; + + if (!ls) + return NULL; + ctx = isl_local_space_get_ctx(ls); + isl_local_space_free(ls); + return isl_val_zero(ctx); +} + +/* Do the parameters of "v" match those of "space"? + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * simply returns 1, except if "v" or "space" are NULL. + */ +int isl_val_matching_params(__isl_keep isl_val *v, __isl_keep isl_space *space) +{ + if (!v || !space) + return -1; + return 1; +} + +/* Check that the domain space of "v" matches "space". + * + * Return 0 on success and -1 on error. + * + * This function is only meant to be used in the generic isl_multi_* + * functions which have to deal with base objects that have an associated + * space. Since an isl_val does not have an associated space, this function + * simply returns 0, except if "v" or "space" are NULL. + */ +int isl_val_check_match_domain_space(__isl_keep isl_val *v, + __isl_keep isl_space *space) +{ + if (!v || !space) + return -1; + return 0; +} + +#undef BASE +#define BASE val + +#define NO_DOMAIN +#define NO_IDENTITY +#define NO_FROM_BASE +#define NO_MOVE_DIMS +#include + +/* Apply "fn" to each of the elements of "mv" with as second argument "v". + */ +static __isl_give isl_multi_val *isl_multi_val_fn_val( + __isl_take isl_multi_val *mv, + __isl_give isl_val *(*fn)(__isl_take isl_val *v1, + __isl_take isl_val *v2), + __isl_take isl_val *v) +{ + int i; + + mv = isl_multi_val_cow(mv); + if (!mv || !v) + goto error; + + for (i = 0; i < mv->n; ++i) { + mv->p[i] = fn(mv->p[i], isl_val_copy(v)); + if (!mv->p[i]) + goto error; + } + + isl_val_free(v); + return mv; +error: + isl_val_free(v); + isl_multi_val_free(mv); + return NULL; +} + +/* Add "v" to each of the elements of "mv". + */ +__isl_give isl_multi_val *isl_multi_val_add_val(__isl_take isl_multi_val *mv, + __isl_take isl_val *v) +{ + if (!v) + return isl_multi_val_free(mv); + if (isl_val_is_zero(v)) { + isl_val_free(v); + return mv; + } + return isl_multi_val_fn_val(mv, &isl_val_add, v); +} + +/* Reduce the elements of "mv" modulo "v". + */ +__isl_give isl_multi_val *isl_multi_val_mod_val(__isl_take isl_multi_val *mv, + __isl_take isl_val *v) +{ + return isl_multi_val_fn_val(mv, &isl_val_mod, v); +} Index: lib/Analysis/isl/isl_val_gmp.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_val_gmp.c @@ -0,0 +1,128 @@ +#include +#include +#include + +/* Return a reference to an isl_val representing the integer "z". + */ +__isl_give isl_val *isl_val_int_from_gmp(isl_ctx *ctx, mpz_t z) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set(v->n, z); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Return a reference to an isl_val representing the rational value "n"/"d". + */ +__isl_give isl_val *isl_val_from_gmp(isl_ctx *ctx, const mpz_t n, const mpz_t d) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + isl_int_set(v->n, n); + isl_int_set(v->d, d); + + return isl_val_normalize(v); +} + +/* Extract the numerator of a rational value "v" in "z". + * + * If "v" is not a rational value, then the result is undefined. + */ +int isl_val_get_num_gmp(__isl_keep isl_val *v, mpz_t z) +{ + if (!v) + return -1; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return -1); + mpz_set(z, v->n); + return 0; +} + +/* Extract the denominator of a rational value "v" in "z". + * + * If "v" is not a rational value, then the result is undefined. + */ +int isl_val_get_den_gmp(__isl_keep isl_val *v, mpz_t z) +{ + if (!v) + return -1; + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return -1); + mpz_set(z, v->d); + return 0; +} + +/* Return a reference to an isl_val representing the unsigned + * integer value stored in the "n" chunks of size "size" at "chunks". + * The least significant chunk is assumed to be stored first. + */ +__isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, size_t n, + size_t size, const void *chunks) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + mpz_import(v->n, n, -1, size, 0, 0, chunks); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Return the number of chunks of size "size" required to + * store the absolute value of the numerator of "v". + */ +size_t isl_val_n_abs_num_chunks(__isl_keep isl_val *v, size_t size) +{ + if (!v) + return 0; + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return 0); + + size *= 8; + return (mpz_sizeinbase(v->n, 2) + size - 1) / size; +} + +/* Store a representation of the absolute value of the numerator of "v" + * in terms of chunks of size "size" at "chunks". + * The least significant chunk is stored first. + * The number of chunks in the result can be obtained by calling + * isl_val_n_abs_num_chunks. The user is responsible for allocating + * enough memory to store the results. + * + * In the special case of a zero value, isl_val_n_abs_num_chunks will + * return one, while mpz_export will not fill in any chunks. We therefore + * do it ourselves. + */ +int isl_val_get_abs_num_chunks(__isl_keep isl_val *v, size_t size, + void *chunks) +{ + if (!v || !chunks) + return -1; + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return -1); + + mpz_export(chunks, NULL, -1, size, 0, 0, v->n); + if (isl_val_is_zero(v)) + memset(chunks, 0, size); + + return 0; +} Index: lib/Analysis/isl/isl_val_imath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_val_imath.c @@ -0,0 +1,64 @@ +#include + +/* Return a reference to an isl_val representing the unsigned + * integer value stored in the "n" chunks of size "size" at "chunks". + * The least significant chunk is assumed to be stored first. + */ +__isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, size_t n, + size_t size, const void *chunks) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + impz_import(v->n, n, -1, size, 0, 0, chunks); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Store a representation of the absolute value of the numerator of "v" + * in terms of chunks of size "size" at "chunks". + * The least significant chunk is stored first. + * The number of chunks in the result can be obtained by calling + * isl_val_n_abs_num_chunks. The user is responsible for allocating + * enough memory to store the results. + * + * In the special case of a zero value, isl_val_n_abs_num_chunks will + * return one, while impz_export will not fill in any chunks. We therefore + * do it ourselves. + */ +int isl_val_get_abs_num_chunks(__isl_keep isl_val *v, size_t size, + void *chunks) +{ + if (!v || !chunks) + return -1; + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return -1); + + impz_export(chunks, NULL, -1, size, 0, 0, v->n); + if (isl_val_is_zero(v)) + memset(chunks, 0, size); + + return 0; +} + +/* Return the number of chunks of size "size" required to + * store the absolute value of the numerator of "v". + */ +size_t isl_val_n_abs_num_chunks(__isl_keep isl_val *v, size_t size) +{ + if (!v) + return 0; + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return 0); + + size *= 8; + return (impz_sizeinbase(v->n, 2) + size - 1) / size; +} Index: lib/Analysis/isl/isl_val_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_val_private.h @@ -0,0 +1,72 @@ +#ifndef ISL_VAL_PRIVATE_H +#define ISL_VAL_PRIVATE_H + +#include +#include +#include +#include + +/* Represents a "value", which may be an integer value, a rational value, + * plus or minus infinity or "not a number". + * + * Internally, +infinity is represented as 1/0, + * -infinity as -1/0 and NaN as 0/0. + * + * A rational value is always normalized before it is passed to the user. + */ +struct isl_val { + int ref; + isl_ctx *ctx; + + isl_int n; + isl_int d; +}; + +#undef EL +#define EL isl_val + +#include + +__isl_give isl_val *isl_val_alloc(isl_ctx *ctx); +__isl_give isl_val *isl_val_normalize(__isl_take isl_val *v); +__isl_give isl_val *isl_val_int_from_isl_int(isl_ctx *ctx, isl_int n); +__isl_give isl_val *isl_val_rat_from_isl_int(isl_ctx *ctx, + isl_int n, isl_int d); +__isl_give isl_val *isl_val_cow(__isl_take isl_val *val); + +int isl_val_involves_dims(__isl_keep isl_val *v, enum isl_dim_type type, + unsigned first, unsigned n); +__isl_give isl_val *isl_val_insert_dims(__isl_take isl_val *v, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_val *isl_val_drop_dims(__isl_take isl_val *v, + enum isl_dim_type type, unsigned first, unsigned n); +__isl_give isl_val *isl_val_set_dim_name(__isl_take isl_val *v, + enum isl_dim_type type, unsigned pos, const char *s); +__isl_give isl_space *isl_val_get_space(__isl_keep isl_val *v); +__isl_give isl_val *isl_val_reset_domain_space(__isl_take isl_val *v, + __isl_take isl_space *space); +__isl_give isl_val *isl_val_align_params(__isl_take isl_val *v, + __isl_take isl_space *space); +__isl_give isl_val *isl_val_realign_domain(__isl_take isl_val *v, + __isl_take isl_reordering *r); +__isl_give isl_val *isl_val_zero_on_domain(__isl_take isl_local_space *ls); + +__isl_give isl_val *isl_val_scale_val(__isl_take isl_val *v1, + __isl_take isl_val *v2); +__isl_give isl_val *isl_val_scale_down_val(__isl_take isl_val *v1, + __isl_take isl_val *v2); +__isl_give isl_val *isl_val_mod_val(__isl_take isl_val *v1, + __isl_take isl_val *v2); + +int isl_val_plain_is_equal(__isl_keep isl_val *val1, __isl_keep isl_val *val2); + +int isl_val_matching_params(__isl_keep isl_val *v, __isl_keep isl_space *space); +int isl_val_check_match_domain_space(__isl_keep isl_val *v, + __isl_keep isl_space *space); + +#undef BASE +#define BASE val + +#include + +#endif Index: lib/Analysis/isl/isl_val_sioimath.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_val_sioimath.c @@ -0,0 +1,68 @@ +#include + +/* Return a reference to an isl_val representing the unsigned + * integer value stored in the "n" chunks of size "size" at "chunks". + * The least significant chunk is assumed to be stored first. + */ +__isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, size_t n, + size_t size, const void *chunks) +{ + isl_val *v; + + v = isl_val_alloc(ctx); + if (!v) + return NULL; + + impz_import(isl_sioimath_reinit_big(v->n), n, -1, size, 0, 0, chunks); + isl_sioimath_try_demote(v->n); + isl_int_set_si(v->d, 1); + + return v; +} + +/* Store a representation of the absolute value of the numerator of "v" + * in terms of chunks of size "size" at "chunks". + * The least significant chunk is stored first. + * The number of chunks in the result can be obtained by calling + * isl_val_n_abs_num_chunks. The user is responsible for allocating + * enough memory to store the results. + * + * In the special case of a zero value, isl_val_n_abs_num_chunks will + * return one, while impz_export will not fill in any chunks. We therefore + * do it ourselves. + */ +int isl_val_get_abs_num_chunks(__isl_keep isl_val *v, size_t size, + void *chunks) +{ + isl_sioimath_scratchspace_t scratch; + + if (!v || !chunks) + return -1; + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return -1); + + impz_export(chunks, NULL, -1, size, 0, 0, + isl_sioimath_bigarg_src(*v->n, &scratch)); + if (isl_val_is_zero(v)) + memset(chunks, 0, size); + + return 0; +} + +/* Return the number of chunks of size "size" required to + * store the absolute value of the numerator of "v". + */ +size_t isl_val_n_abs_num_chunks(__isl_keep isl_val *v, size_t size) +{ + if (!v) + return 0; + + if (!isl_val_is_rat(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting rational value", return 0); + + size *= 8; + return (isl_sioimath_sizeinbase(*v->n, 2) + size - 1) / size; +} Index: lib/Analysis/isl/isl_vec.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_vec.c @@ -0,0 +1,637 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * Copyright 2013 Ecole Normale Superieure + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France + */ + +#include +#include +#include +#include +#include + +isl_ctx *isl_vec_get_ctx(__isl_keep isl_vec *vec) +{ + return vec ? vec->ctx : NULL; +} + +/* Return a hash value that digests "vec". + */ +uint32_t isl_vec_get_hash(__isl_keep isl_vec *vec) +{ + if (!vec) + return 0; + + return isl_seq_get_hash(vec->el, vec->size); +} + +struct isl_vec *isl_vec_alloc(struct isl_ctx *ctx, unsigned size) +{ + struct isl_vec *vec; + + vec = isl_alloc_type(ctx, struct isl_vec); + if (!vec) + return NULL; + + vec->block = isl_blk_alloc(ctx, size); + if (isl_blk_is_error(vec->block)) + goto error; + + vec->ctx = ctx; + isl_ctx_ref(ctx); + vec->ref = 1; + vec->size = size; + vec->el = vec->block.data; + + return vec; +error: + isl_blk_free(ctx, vec->block); + free(vec); + return NULL; +} + +__isl_give isl_vec *isl_vec_extend(__isl_take isl_vec *vec, unsigned size) +{ + if (!vec) + return NULL; + if (size <= vec->size) + return vec; + + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + + vec->block = isl_blk_extend(vec->ctx, vec->block, size); + if (!vec->block.data) + goto error; + + vec->size = size; + vec->el = vec->block.data; + + return vec; +error: + isl_vec_free(vec); + return NULL; +} + +/* Apply the expansion specified by "exp" to the "n" elements starting at "pos". + * "expanded" it the number of elements that need to replace those "n" + * elements. The entries in "exp" have increasing values between + * 0 and "expanded". + */ +__isl_give isl_vec *isl_vec_expand(__isl_take isl_vec *vec, int pos, int n, + int *exp, int expanded) +{ + int i, j; + int old_size, extra; + + if (!vec) + return NULL; + if (expanded < n) + isl_die(isl_vec_get_ctx(vec), isl_error_invalid, + "not an expansion", isl_vec_free(vec)); + if (expanded == n) + return vec; + if (pos < 0 || n < 0 || pos + n > vec->size) + isl_die(isl_vec_get_ctx(vec), isl_error_invalid, + "position out of bounds", return isl_vec_free(vec)); + + old_size = vec->size; + extra = expanded - n; + vec = isl_vec_extend(vec, old_size + extra); + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + + for (i = old_size - 1; i >= pos + n; --i) + isl_int_set(vec->el[i + extra], vec->el[i]); + + j = n - 1; + for (i = expanded - 1; i >= 0; --i) { + if (j >= 0 && exp[j] == i) { + if (i != j) + isl_int_swap(vec->el[pos + i], + vec->el[pos + j]); + j--; + } else { + isl_int_set_si(vec->el[pos + i], 0); + } + } + + return vec; +} + +__isl_give isl_vec *isl_vec_zero_extend(__isl_take isl_vec *vec, unsigned size) +{ + int extra; + + if (!vec) + return NULL; + if (size <= vec->size) + return vec; + + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + + extra = size - vec->size; + vec = isl_vec_extend(vec, size); + if (!vec) + return NULL; + + isl_seq_clr(vec->el + size - extra, extra); + + return vec; +} + +/* Return a vector containing the elements of "vec1" followed by + * those of "vec2". + */ +__isl_give isl_vec *isl_vec_concat(__isl_take isl_vec *vec1, + __isl_take isl_vec *vec2) +{ + if (!vec1 || !vec2) + goto error; + + if (vec2->size == 0) { + isl_vec_free(vec2); + return vec1; + } + + if (vec1->size == 0) { + isl_vec_free(vec1); + return vec2; + } + + vec1 = isl_vec_extend(vec1, vec1->size + vec2->size); + if (!vec1) + goto error; + + isl_seq_cpy(vec1->el + vec1->size - vec2->size, vec2->el, vec2->size); + + isl_vec_free(vec2); + return vec1; +error: + isl_vec_free(vec1); + isl_vec_free(vec2); + return NULL; +} + +struct isl_vec *isl_vec_copy(struct isl_vec *vec) +{ + if (!vec) + return NULL; + + vec->ref++; + return vec; +} + +struct isl_vec *isl_vec_dup(struct isl_vec *vec) +{ + struct isl_vec *vec2; + + if (!vec) + return NULL; + vec2 = isl_vec_alloc(vec->ctx, vec->size); + if (!vec2) + return NULL; + isl_seq_cpy(vec2->el, vec->el, vec->size); + return vec2; +} + +struct isl_vec *isl_vec_cow(struct isl_vec *vec) +{ + struct isl_vec *vec2; + if (!vec) + return NULL; + + if (vec->ref == 1) + return vec; + + vec2 = isl_vec_dup(vec); + isl_vec_free(vec); + return vec2; +} + +__isl_null isl_vec *isl_vec_free(__isl_take isl_vec *vec) +{ + if (!vec) + return NULL; + + if (--vec->ref > 0) + return NULL; + + isl_ctx_deref(vec->ctx); + isl_blk_free(vec->ctx, vec->block); + free(vec); + + return NULL; +} + +int isl_vec_size(__isl_keep isl_vec *vec) +{ + return vec ? vec->size : -1; +} + +int isl_vec_get_element(__isl_keep isl_vec *vec, int pos, isl_int *v) +{ + if (!vec) + return -1; + + if (pos < 0 || pos >= vec->size) + isl_die(vec->ctx, isl_error_invalid, "position out of range", + return -1); + isl_int_set(*v, vec->el[pos]); + return 0; +} + +/* Extract the element at position "pos" of "vec". + */ +__isl_give isl_val *isl_vec_get_element_val(__isl_keep isl_vec *vec, int pos) +{ + isl_ctx *ctx; + + if (!vec) + return NULL; + ctx = isl_vec_get_ctx(vec); + if (pos < 0 || pos >= vec->size) + isl_die(ctx, isl_error_invalid, "position out of range", + return NULL); + return isl_val_int_from_isl_int(ctx, vec->el[pos]); +} + +__isl_give isl_vec *isl_vec_set_element(__isl_take isl_vec *vec, + int pos, isl_int v) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + if (pos < 0 || pos >= vec->size) + isl_die(vec->ctx, isl_error_invalid, "position out of range", + goto error); + isl_int_set(vec->el[pos], v); + return vec; +error: + isl_vec_free(vec); + return NULL; +} + +__isl_give isl_vec *isl_vec_set_element_si(__isl_take isl_vec *vec, + int pos, int v) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + if (pos < 0 || pos >= vec->size) + isl_die(vec->ctx, isl_error_invalid, "position out of range", + goto error); + isl_int_set_si(vec->el[pos], v); + return vec; +error: + isl_vec_free(vec); + return NULL; +} + +/* Replace the element at position "pos" of "vec" by "v". + */ +__isl_give isl_vec *isl_vec_set_element_val(__isl_take isl_vec *vec, + int pos, __isl_take isl_val *v) +{ + if (!v) + return isl_vec_free(vec); + if (!isl_val_is_int(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting integer value", goto error); + vec = isl_vec_set_element(vec, pos, v->n); + isl_val_free(v); + return vec; +error: + isl_val_free(v); + return isl_vec_free(vec); +} + +/* Compare the elements of "vec1" and "vec2" at position "pos". + */ +int isl_vec_cmp_element(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2, + int pos) +{ + if (!vec1 || !vec2) + return 0; + if (pos < 0 || pos >= vec1->size || pos >= vec2->size) + isl_die(isl_vec_get_ctx(vec1), isl_error_invalid, + "position out of range", return 0); + return isl_int_cmp(vec1->el[pos], vec2->el[pos]); +} + +isl_bool isl_vec_is_equal(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2) +{ + if (!vec1 || !vec2) + return isl_bool_error; + + if (vec1->size != vec2->size) + return isl_bool_false; + + return isl_seq_eq(vec1->el, vec2->el, vec1->size); +} + +__isl_give isl_printer *isl_printer_print_vec(__isl_take isl_printer *printer, + __isl_keep isl_vec *vec) +{ + int i; + + if (!printer || !vec) + goto error; + + printer = isl_printer_print_str(printer, "["); + for (i = 0; i < vec->size; ++i) { + if (i) + printer = isl_printer_print_str(printer, ","); + printer = isl_printer_print_isl_int(printer, vec->el[i]); + } + printer = isl_printer_print_str(printer, "]"); + + return printer; +error: + isl_printer_free(printer); + return NULL; +} + +void isl_vec_dump(struct isl_vec *vec) +{ + isl_printer *printer; + + if (!vec) + return; + + printer = isl_printer_to_file(vec->ctx, stderr); + printer = isl_printer_print_vec(printer, vec); + printer = isl_printer_end_line(printer); + + isl_printer_free(printer); +} + +__isl_give isl_vec *isl_vec_set(__isl_take isl_vec *vec, isl_int v) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + isl_seq_set(vec->el, v, vec->size); + return vec; +} + +__isl_give isl_vec *isl_vec_set_si(__isl_take isl_vec *vec, int v) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + isl_seq_set_si(vec->el, v, vec->size); + return vec; +} + +/* Replace all elements of "vec" by "v". + */ +__isl_give isl_vec *isl_vec_set_val(__isl_take isl_vec *vec, + __isl_take isl_val *v) +{ + vec = isl_vec_cow(vec); + if (!vec || !v) + goto error; + if (!isl_val_is_int(v)) + isl_die(isl_val_get_ctx(v), isl_error_invalid, + "expecting integer value", goto error); + isl_seq_set(vec->el, v->n, vec->size); + isl_val_free(v); + return vec; +error: + isl_vec_free(vec); + isl_val_free(v); + return NULL; +} + +__isl_give isl_vec *isl_vec_clr(__isl_take isl_vec *vec) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + isl_seq_clr(vec->el, vec->size); + return vec; +} + +void isl_vec_lcm(struct isl_vec *vec, isl_int *lcm) +{ + isl_seq_lcm(vec->block.data, vec->size, lcm); +} + +/* Given a rational vector, with the denominator in the first element + * of the vector, round up all coordinates. + */ +struct isl_vec *isl_vec_ceil(struct isl_vec *vec) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + + isl_seq_cdiv_q(vec->el + 1, vec->el + 1, vec->el[0], vec->size - 1); + + isl_int_set_si(vec->el[0], 1); + + return vec; +} + +struct isl_vec *isl_vec_normalize(struct isl_vec *vec) +{ + if (!vec) + return NULL; + isl_seq_normalize(vec->ctx, vec->el, vec->size); + return vec; +} + +__isl_give isl_vec *isl_vec_neg(__isl_take isl_vec *vec) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + isl_seq_neg(vec->el, vec->el, vec->size); + return vec; +} + +__isl_give isl_vec *isl_vec_scale(__isl_take isl_vec *vec, isl_int m) +{ + if (isl_int_is_one(m)) + return vec; + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + isl_seq_scale(vec->el, vec->el, m, vec->size); + return vec; +} + +/* Reduce the elements of "vec" modulo "m". + */ +__isl_give isl_vec *isl_vec_fdiv_r(__isl_take isl_vec *vec, isl_int m) +{ + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + + isl_seq_fdiv_r(vec->el, vec->el, m, vec->size); + + return vec; +} + +__isl_give isl_vec *isl_vec_add(__isl_take isl_vec *vec1, + __isl_take isl_vec *vec2) +{ + vec1 = isl_vec_cow(vec1); + if (!vec1 || !vec2) + goto error; + + isl_assert(vec1->ctx, vec1->size == vec2->size, goto error); + + isl_seq_combine(vec1->el, vec1->ctx->one, vec1->el, + vec1->ctx->one, vec2->el, vec1->size); + + isl_vec_free(vec2); + return vec1; +error: + isl_vec_free(vec1); + isl_vec_free(vec2); + return NULL; +} + +static int qsort_int_cmp(const void *p1, const void *p2) +{ + const isl_int *i1 = (const isl_int *) p1; + const isl_int *i2 = (const isl_int *) p2; + + return isl_int_cmp(*i1, *i2); +} + +__isl_give isl_vec *isl_vec_sort(__isl_take isl_vec *vec) +{ + if (!vec) + return NULL; + + qsort(vec->el, vec->size, sizeof(*vec->el), &qsort_int_cmp); + + return vec; +} + +__isl_give isl_vec *isl_vec_drop_els(__isl_take isl_vec *vec, + unsigned pos, unsigned n) +{ + if (n == 0) + return vec; + vec = isl_vec_cow(vec); + if (!vec) + return NULL; + + if (pos + n > vec->size) + isl_die(vec->ctx, isl_error_invalid, + "range out of bounds", goto error); + + if (pos + n != vec->size) + isl_seq_cpy(vec->el + pos, vec->el + pos + n, + vec->size - pos - n); + + vec->size -= n; + + return vec; +error: + isl_vec_free(vec); + return NULL; +} + +__isl_give isl_vec *isl_vec_insert_els(__isl_take isl_vec *vec, + unsigned pos, unsigned n) +{ + isl_vec *ext = NULL; + + if (n == 0) + return vec; + if (!vec) + return NULL; + + if (pos > vec->size) + isl_die(vec->ctx, isl_error_invalid, + "position out of bounds", goto error); + + ext = isl_vec_alloc(vec->ctx, vec->size + n); + if (!ext) + goto error; + + isl_seq_cpy(ext->el, vec->el, pos); + isl_seq_cpy(ext->el + pos + n, vec->el + pos, vec->size - pos); + + isl_vec_free(vec); + return ext; +error: + isl_vec_free(vec); + isl_vec_free(ext); + return NULL; +} + +__isl_give isl_vec *isl_vec_insert_zero_els(__isl_take isl_vec *vec, + unsigned pos, unsigned n) +{ + vec = isl_vec_insert_els(vec, pos, n); + if (!vec) + return NULL; + + isl_seq_clr(vec->el + pos, n); + + return vec; +} + +/* Move the "n" elements starting as "src_pos" of "vec" + * to "dst_pos". The elements originally at "dst_pos" are moved + * up or down depending on whether "dst_pos" is smaller or greater + * than "src_pos". + */ +__isl_give isl_vec *isl_vec_move_els(__isl_take isl_vec *vec, + unsigned dst_pos, unsigned src_pos, unsigned n) +{ + isl_vec *res; + + if (!vec) + return NULL; + + if (src_pos + n > vec->size) + isl_die(vec->ctx, isl_error_invalid, + "source range out of bounds", return isl_vec_free(vec)); + if (dst_pos + n > vec->size) + isl_die(vec->ctx, isl_error_invalid, + "destination range out of bounds", + return isl_vec_free(vec)); + + if (n == 0 || dst_pos == src_pos) + return vec; + + res = isl_vec_alloc(vec->ctx, vec->size); + if (!res) + return isl_vec_free(vec); + + if (dst_pos < src_pos) { + isl_seq_cpy(res->el, vec->el, dst_pos); + isl_seq_cpy(res->el + dst_pos, vec->el + src_pos, n); + isl_seq_cpy(res->el + dst_pos + n, + vec->el + dst_pos, src_pos - dst_pos); + isl_seq_cpy(res->el + src_pos + n, + vec->el + src_pos + n, res->size - src_pos - n); + } else { + isl_seq_cpy(res->el, vec->el, src_pos); + isl_seq_cpy(res->el + src_pos, + vec->el + src_pos + n, dst_pos - src_pos); + isl_seq_cpy(res->el + dst_pos, vec->el + src_pos, n); + isl_seq_cpy(res->el + dst_pos + n, + vec->el + dst_pos + n, res->size - dst_pos - n); + } + + isl_vec_free(vec); + return res; +} Index: lib/Analysis/isl/isl_vec_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_vec_private.h @@ -0,0 +1,29 @@ +#ifndef ISL_VEC_PRIVATE_H +#define ISL_VEC_PRIVATE_H + +#include +#include + +struct isl_vec { + int ref; + + struct isl_ctx *ctx; + + unsigned size; + isl_int *el; + + struct isl_blk block; +}; + +uint32_t isl_vec_get_hash(__isl_keep isl_vec *vec); + +__isl_give isl_vec *isl_vec_cow(__isl_take isl_vec *vec); + +void isl_vec_lcm(struct isl_vec *vec, isl_int *lcm); +int isl_vec_get_element(__isl_keep isl_vec *vec, int pos, isl_int *v); +__isl_give isl_vec *isl_vec_set(__isl_take isl_vec *vec, isl_int v); + +__isl_give isl_vec *isl_vec_expand(__isl_take isl_vec *vec, int pos, int n, + int *exp, int expanded); + +#endif Index: lib/Analysis/isl/isl_version.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_version.c @@ -0,0 +1,17 @@ +#include "isl_config.h" +#include "gitversion.h" + +const char *isl_version(void) +{ + return GIT_HEAD_ID +#ifdef USE_GMP_FOR_MP + "-GMP" +#endif +#ifdef USE_IMATH_FOR_MP + "-IMath" +#ifdef USE_SMALL_INT_OPT + "-32" +#endif +#endif + "\n"; +} Index: lib/Analysis/isl/isl_vertices.c =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_vertices.c @@ -0,0 +1,1570 @@ +/* + * Copyright 2010 INRIA Saclay + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, + * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, + * 91893 Orsay, France + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SELECTED 1 +#define DESELECTED -1 +#define UNSELECTED 0 + +static __isl_give isl_vertices *compute_chambers(__isl_take isl_basic_set *bset, + __isl_take isl_vertices *vertices); + +__isl_give isl_vertices *isl_vertices_copy(__isl_keep isl_vertices *vertices) +{ + if (!vertices) + return NULL; + + vertices->ref++; + return vertices; +} + +void isl_vertices_free(__isl_take isl_vertices *vertices) +{ + int i; + + if (!vertices) + return; + + if (--vertices->ref > 0) + return; + + for (i = 0; i < vertices->n_vertices; ++i) { + isl_basic_set_free(vertices->v[i].vertex); + isl_basic_set_free(vertices->v[i].dom); + } + free(vertices->v); + + for (i = 0; i < vertices->n_chambers; ++i) { + free(vertices->c[i].vertices); + isl_basic_set_free(vertices->c[i].dom); + } + free(vertices->c); + + isl_basic_set_free(vertices->bset); + free(vertices); +} + +struct isl_vertex_list { + struct isl_vertex v; + struct isl_vertex_list *next; +}; + +static void free_vertex_list(struct isl_vertex_list *list) +{ + struct isl_vertex_list *next; + + for (; list; list = next) { + next = list->next; + isl_basic_set_free(list->v.vertex); + isl_basic_set_free(list->v.dom); + free(list); + } +} + +static __isl_give isl_vertices *vertices_from_list(__isl_keep isl_basic_set *bset, + int n_vertices, struct isl_vertex_list *list) +{ + int i; + struct isl_vertex_list *next; + isl_vertices *vertices; + + vertices = isl_calloc_type(bset->ctx, isl_vertices); + if (!vertices) + goto error; + vertices->ref = 1; + vertices->bset = isl_basic_set_copy(bset); + vertices->v = isl_alloc_array(bset->ctx, struct isl_vertex, n_vertices); + if (n_vertices && !vertices->v) + goto error; + vertices->n_vertices = n_vertices; + + for (i = 0; list; list = next, i++) { + next = list->next; + vertices->v[i] = list->v; + free(list); + } + + return vertices; +error: + isl_vertices_free(vertices); + free_vertex_list(list); + return NULL; +} + +/* Prepend a vertex to the linked list "list" based on the equalities in "tab". + */ +static int add_vertex(struct isl_vertex_list **list, + __isl_keep isl_basic_set *bset, struct isl_tab *tab) +{ + unsigned nvar; + struct isl_vertex_list *v = NULL; + + if (isl_tab_detect_implicit_equalities(tab) < 0) + return -1; + + nvar = isl_basic_set_dim(bset, isl_dim_set); + + v = isl_calloc_type(tab->mat->ctx, struct isl_vertex_list); + if (!v) + goto error; + + v->v.vertex = isl_basic_set_copy(bset); + v->v.vertex = isl_basic_set_cow(v->v.vertex); + v->v.vertex = isl_basic_set_update_from_tab(v->v.vertex, tab); + v->v.vertex = isl_basic_set_simplify(v->v.vertex); + v->v.vertex = isl_basic_set_finalize(v->v.vertex); + if (!v->v.vertex) + goto error; + isl_assert(bset->ctx, v->v.vertex->n_eq >= nvar, goto error); + v->v.dom = isl_basic_set_copy(v->v.vertex); + v->v.dom = isl_basic_set_params(v->v.dom); + if (!v->v.dom) + goto error; + + v->next = *list; + *list = v; + + return 0; +error: + free_vertex_list(v); + return -1; +} + +/* Compute the parametric vertices and the chamber decomposition + * of an empty parametric polytope. + */ +static __isl_give isl_vertices *vertices_empty(__isl_keep isl_basic_set *bset) +{ + isl_vertices *vertices; + + if (!bset) + return NULL; + + vertices = isl_calloc_type(bset->ctx, isl_vertices); + if (!vertices) + return NULL; + vertices->bset = isl_basic_set_copy(bset); + vertices->ref = 1; + + vertices->n_vertices = 0; + vertices->n_chambers = 0; + + return vertices; +} + +/* Compute the parametric vertices and the chamber decomposition + * of the parametric polytope defined using the same constraints + * as "bset" in the 0D case. + * There is exactly one 0D vertex and a single chamber containing + * the vertex. + */ +static __isl_give isl_vertices *vertices_0D(__isl_keep isl_basic_set *bset) +{ + isl_vertices *vertices; + + if (!bset) + return NULL; + + vertices = isl_calloc_type(bset->ctx, isl_vertices); + if (!vertices) + return NULL; + vertices->ref = 1; + vertices->bset = isl_basic_set_copy(bset); + + vertices->v = isl_calloc_array(bset->ctx, struct isl_vertex, 1); + if (!vertices->v) + goto error; + vertices->n_vertices = 1; + vertices->v[0].vertex = isl_basic_set_copy(bset); + vertices->v[0].dom = isl_basic_set_params(isl_basic_set_copy(bset)); + if (!vertices->v[0].vertex || !vertices->v[0].dom) + goto error; + + vertices->c = isl_calloc_array(bset->ctx, struct isl_chamber, 1); + if (!vertices->c) + goto error; + vertices->n_chambers = 1; + vertices->c[0].n_vertices = 1; + vertices->c[0].vertices = isl_calloc_array(bset->ctx, int, 1); + if (!vertices->c[0].vertices) + goto error; + vertices->c[0].dom = isl_basic_set_copy(vertices->v[0].dom); + if (!vertices->c[0].dom) + goto error; + + return vertices; +error: + isl_vertices_free(vertices); + return NULL; +} + +static int isl_mat_rank(__isl_keep isl_mat *mat) +{ + int row, col; + isl_mat *H; + + H = isl_mat_left_hermite(isl_mat_copy(mat), 0, NULL, NULL); + if (!H) + return -1; + + for (col = 0; col < H->n_col; ++col) { + for (row = 0; row < H->n_row; ++row) + if (!isl_int_is_zero(H->row[row][col])) + break; + if (row == H->n_row) + break; + } + + isl_mat_free(H); + + return col; +} + +/* Is the row pointed to by "f" linearly independent of the "n" first + * rows in "facets"? + */ +static int is_independent(__isl_keep isl_mat *facets, int n, isl_int *f) +{ + int rank; + + if (isl_seq_first_non_zero(f, facets->n_col) < 0) + return 0; + + isl_seq_cpy(facets->row[n], f, facets->n_col); + facets->n_row = n + 1; + rank = isl_mat_rank(facets); + if (rank < 0) + return -1; + + return rank == n + 1; +} + +/* Check whether we can select constraint "level", given the current selection + * reflected by facets in "tab", the rows of "facets" and the earlier + * "selected" elements of "selection". + * + * If the constraint is (strictly) redundant in the tableau, selecting it would + * result in an empty tableau, so it can't be selected. + * If the set variable part of the constraint is not linearly indepedent + * of the set variable parts of the already selected constraints, + * the constraint cannot be selected. + * If selecting the constraint results in an empty tableau, the constraint + * cannot be selected. + * Finally, if selecting the constraint results in some explicitly + * deselected constraints turning into equalities, then the corresponding + * vertices have already been generated, so the constraint cannot be selected. + */ +static int can_select(__isl_keep isl_basic_set *bset, int level, + struct isl_tab *tab, __isl_keep isl_mat *facets, int selected, + int *selection) +{ + int i; + int indep; + unsigned ovar; + struct isl_tab_undo *snap; + + if (isl_tab_is_redundant(tab, level)) + return 0; + + ovar = isl_space_offset(bset->dim, isl_dim_set); + + indep = is_independent(facets, selected, bset->ineq[level] + 1 + ovar); + if (indep < 0) + return -1; + if (!indep) + return 0; + + snap = isl_tab_snap(tab); + if (isl_tab_select_facet(tab, level) < 0) + return -1; + + if (tab->empty) { + if (isl_tab_rollback(tab, snap) < 0) + return -1; + return 0; + } + + for (i = 0; i < level; ++i) { + int sgn; + + if (selection[i] != DESELECTED) + continue; + + if (isl_tab_is_equality(tab, i)) + sgn = 0; + else if (isl_tab_is_redundant(tab, i)) + sgn = 1; + else + sgn = isl_tab_sign_of_max(tab, i); + if (sgn < -1) + return -1; + if (sgn <= 0) { + if (isl_tab_rollback(tab, snap) < 0) + return -1; + return 0; + } + } + + return 1; +} + +/* Compute the parametric vertices and the chamber decomposition + * of a parametric polytope that is not full-dimensional. + * + * Simply map the parametric polytope to a lower dimensional space + * and map the resulting vertices back. + */ +static __isl_give isl_vertices *lower_dim_vertices( + __isl_keep isl_basic_set *bset) +{ + isl_morph *morph; + isl_vertices *vertices; + + bset = isl_basic_set_copy(bset); + morph = isl_basic_set_full_compression(bset); + bset = isl_morph_basic_set(isl_morph_copy(morph), bset); + + vertices = isl_basic_set_compute_vertices(bset); + isl_basic_set_free(bset); + + morph = isl_morph_inverse(morph); + + vertices = isl_morph_vertices(morph, vertices); + + return vertices; +} + +/* Compute the parametric vertices and the chamber decomposition + * of the parametric polytope defined using the same constraints + * as "bset". "bset" is assumed to have no existentially quantified + * variables. + * + * The vertices themselves are computed in a fairly simplistic way. + * We simply run through all combinations of d constraints, + * with d the number of set variables, and check if those d constraints + * define a vertex. To avoid the generation of duplicate vertices, + * which we may happen if a vertex is defined by more that d constraints, + * we make sure we only generate the vertex for the d constraints with + * smallest index. + * + * We set up a tableau and keep track of which facets have been + * selected. The tableau is marked strict_redundant so that we can be + * sure that any constraint that is marked redundant (and that is not + * also marked zero) is not an equality. + * If a constraint is marked DESELECTED, it means the constraint was + * SELECTED before (in combination with the same selection of earlier + * constraints). If such a deselected constraint turns out to be an + * equality, then any vertex that may still be found with the current + * selection has already been generated when the constraint was selected. + * A constraint is marked UNSELECTED when there is no way selecting + * the constraint could lead to a vertex (in combination with the current + * selection of earlier constraints). + * + * The set variable coefficients of the selected constraints are stored + * in the facets matrix. + */ +__isl_give isl_vertices *isl_basic_set_compute_vertices( + __isl_keep isl_basic_set *bset) +{ + struct isl_tab *tab; + int level; + int init; + unsigned nvar; + int *selection = NULL; + int selected; + struct isl_tab_undo **snap = NULL; + isl_mat *facets = NULL; + struct isl_vertex_list *list = NULL; + int n_vertices = 0; + isl_vertices *vertices; + + if (!bset) + return NULL; + + if (isl_basic_set_plain_is_empty(bset)) + return vertices_empty(bset); + + if (bset->n_eq != 0) + return lower_dim_vertices(bset); + + isl_assert(bset->ctx, isl_basic_set_dim(bset, isl_dim_div) == 0, + return NULL); + + if (isl_basic_set_dim(bset, isl_dim_set) == 0) + return vertices_0D(bset); + + nvar = isl_basic_set_dim(bset, isl_dim_set); + + bset = isl_basic_set_copy(bset); + bset = isl_basic_set_set_rational(bset); + if (!bset) + return NULL; + + tab = isl_tab_from_basic_set(bset, 0); + if (!tab) + goto error; + tab->strict_redundant = 1; + + if (tab->empty) { + vertices = vertices_empty(bset); + isl_basic_set_free(bset); + isl_tab_free(tab); + return vertices; + } + + selection = isl_alloc_array(bset->ctx, int, bset->n_ineq); + snap = isl_alloc_array(bset->ctx, struct isl_tab_undo *, bset->n_ineq); + facets = isl_mat_alloc(bset->ctx, nvar, nvar); + if ((bset->n_ineq && (!selection || !snap)) || !facets) + goto error; + + level = 0; + init = 1; + selected = 0; + + while (level >= 0) { + if (level >= bset->n_ineq || + (!init && selection[level] != SELECTED)) { + --level; + init = 0; + continue; + } + if (init) { + int ok; + snap[level] = isl_tab_snap(tab); + ok = can_select(bset, level, tab, facets, selected, + selection); + if (ok < 0) + goto error; + if (ok) { + selection[level] = SELECTED; + selected++; + } else + selection[level] = UNSELECTED; + } else { + selection[level] = DESELECTED; + selected--; + if (isl_tab_rollback(tab, snap[level]) < 0) + goto error; + } + if (selected == nvar) { + if (tab->n_dead == nvar) { + if (add_vertex(&list, bset, tab) < 0) + goto error; + n_vertices++; + } + init = 0; + continue; + } + ++level; + init = 1; + } + + isl_mat_free(facets); + free(selection); + free(snap); + + isl_tab_free(tab); + + vertices = vertices_from_list(bset, n_vertices, list); + + vertices = compute_chambers(bset, vertices); + + return vertices; +error: + free_vertex_list(list); + isl_mat_free(facets); + free(selection); + free(snap); + isl_tab_free(tab); + isl_basic_set_free(bset); + return NULL; +} + +struct isl_chamber_list { + struct isl_chamber c; + struct isl_chamber_list *next; +}; + +static void free_chamber_list(struct isl_chamber_list *list) +{ + struct isl_chamber_list *next; + + for (; list; list = next) { + next = list->next; + isl_basic_set_free(list->c.dom); + free(list->c.vertices); + free(list); + } +} + +/* Check whether the basic set "bset" is a superset of the basic set described + * by "tab", i.e., check whether all constraints of "bset" are redundant. + */ +static int bset_covers_tab(__isl_keep isl_basic_set *bset, struct isl_tab *tab) +{ + int i; + + if (!bset || !tab) + return -1; + + for (i = 0; i < bset->n_ineq; ++i) { + enum isl_ineq_type type = isl_tab_ineq_type(tab, bset->ineq[i]); + switch (type) { + case isl_ineq_error: return -1; + case isl_ineq_redundant: continue; + default: return 0; + } + } + + return 1; +} + +static __isl_give isl_vertices *vertices_add_chambers( + __isl_take isl_vertices *vertices, int n_chambers, + struct isl_chamber_list *list) +{ + int i; + isl_ctx *ctx; + struct isl_chamber_list *next; + + ctx = isl_vertices_get_ctx(vertices); + vertices->c = isl_alloc_array(ctx, struct isl_chamber, n_chambers); + if (!vertices->c) + goto error; + vertices->n_chambers = n_chambers; + + for (i = 0; list; list = next, i++) { + next = list->next; + vertices->c[i] = list->c; + free(list); + } + + return vertices; +error: + isl_vertices_free(vertices); + free_chamber_list(list); + return NULL; +} + +/* Can "tab" be intersected with "bset" without resulting in + * a lower-dimensional set. + */ +static int can_intersect(struct isl_tab *tab, __isl_keep isl_basic_set *bset) +{ + int i; + struct isl_tab_undo *snap; + + if (isl_tab_extend_cons(tab, bset->n_ineq) < 0) + return -1; + + snap = isl_tab_snap(tab); + + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_tab_ineq_type(tab, bset->ineq[i]) == isl_ineq_redundant) + continue; + if (isl_tab_add_ineq(tab, bset->ineq[i]) < 0) + return -1; + } + + if (isl_tab_detect_implicit_equalities(tab) < 0) + return -1; + if (tab->n_dead) { + if (isl_tab_rollback(tab, snap) < 0) + return -1; + return 0; + } + + return 1; +} + +static int add_chamber(struct isl_chamber_list **list, + __isl_keep isl_vertices *vertices, struct isl_tab *tab, int *selection) +{ + int n_frozen; + int i, j; + int n_vertices = 0; + struct isl_tab_undo *snap; + struct isl_chamber_list *c = NULL; + + for (i = 0; i < vertices->n_vertices; ++i) + if (selection[i]) + n_vertices++; + + snap = isl_tab_snap(tab); + + for (i = 0; i < tab->n_con && tab->con[i].frozen; ++i) + tab->con[i].frozen = 0; + n_frozen = i; + + if (isl_tab_detect_redundant(tab) < 0) + return -1; + + c = isl_calloc_type(tab->mat->ctx, struct isl_chamber_list); + if (!c) + goto error; + c->c.vertices = isl_alloc_array(tab->mat->ctx, int, n_vertices); + if (n_vertices && !c->c.vertices) + goto error; + c->c.dom = isl_basic_set_copy(isl_tab_peek_bset(tab)); + c->c.dom = isl_basic_set_set_rational(c->c.dom); + c->c.dom = isl_basic_set_cow(c->c.dom); + c->c.dom = isl_basic_set_update_from_tab(c->c.dom, tab); + c->c.dom = isl_basic_set_simplify(c->c.dom); + c->c.dom = isl_basic_set_finalize(c->c.dom); + if (!c->c.dom) + goto error; + + c->c.n_vertices = n_vertices; + + for (i = 0, j = 0; i < vertices->n_vertices; ++i) + if (selection[i]) { + c->c.vertices[j] = i; + j++; + } + + c->next = *list; + *list = c; + + for (i = 0; i < n_frozen; ++i) + tab->con[i].frozen = 1; + + if (isl_tab_rollback(tab, snap) < 0) + return -1; + + return 0; +error: + free_chamber_list(c); + return -1; +} + +struct isl_facet_todo { + struct isl_tab *tab; /* A tableau representation of the facet */ + isl_basic_set *bset; /* A normalized basic set representation */ + isl_vec *constraint; /* Constraint pointing to the other side */ + struct isl_facet_todo *next; +}; + +static void free_todo(struct isl_facet_todo *todo) +{ + while (todo) { + struct isl_facet_todo *next = todo->next; + + isl_tab_free(todo->tab); + isl_basic_set_free(todo->bset); + isl_vec_free(todo->constraint); + free(todo); + + todo = next; + } +} + +static struct isl_facet_todo *create_todo(struct isl_tab *tab, int con) +{ + int i; + int n_frozen; + struct isl_tab_undo *snap; + struct isl_facet_todo *todo; + + snap = isl_tab_snap(tab); + + for (i = 0; i < tab->n_con && tab->con[i].frozen; ++i) + tab->con[i].frozen = 0; + n_frozen = i; + + if (isl_tab_detect_redundant(tab) < 0) + return NULL; + + todo = isl_calloc_type(tab->mat->ctx, struct isl_facet_todo); + if (!todo) + return NULL; + + todo->constraint = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var); + if (!todo->constraint) + goto error; + isl_seq_neg(todo->constraint->el, tab->bmap->ineq[con], 1 + tab->n_var); + todo->bset = isl_basic_set_copy(isl_tab_peek_bset(tab)); + todo->bset = isl_basic_set_set_rational(todo->bset); + todo->bset = isl_basic_set_cow(todo->bset); + todo->bset = isl_basic_set_update_from_tab(todo->bset, tab); + todo->bset = isl_basic_set_simplify(todo->bset); + todo->bset = isl_basic_set_sort_constraints(todo->bset); + if (!todo->bset) + goto error; + ISL_F_SET(todo->bset, ISL_BASIC_SET_NORMALIZED); + todo->tab = isl_tab_dup(tab); + if (!todo->tab) + goto error; + + for (i = 0; i < n_frozen; ++i) + tab->con[i].frozen = 1; + + if (isl_tab_rollback(tab, snap) < 0) + goto error; + + return todo; +error: + free_todo(todo); + return NULL; +} + +/* Create todo items for all interior facets of the chamber represented + * by "tab" and collect them in "next". + */ +static int init_todo(struct isl_facet_todo **next, struct isl_tab *tab) +{ + int i; + struct isl_tab_undo *snap; + struct isl_facet_todo *todo; + + snap = isl_tab_snap(tab); + + for (i = 0; i < tab->n_con; ++i) { + if (tab->con[i].frozen) + continue; + if (tab->con[i].is_redundant) + continue; + + if (isl_tab_select_facet(tab, i) < 0) + return -1; + + todo = create_todo(tab, i); + if (!todo) + return -1; + + todo->next = *next; + *next = todo; + + if (isl_tab_rollback(tab, snap) < 0) + return -1; + } + + return 0; +} + +/* Does the linked list contain a todo item that is the opposite of "todo". + * If so, return 1 and remove the opposite todo item. + */ +static int has_opposite(struct isl_facet_todo *todo, + struct isl_facet_todo **list) +{ + for (; *list; list = &(*list)->next) { + int eq; + eq = isl_basic_set_plain_is_equal(todo->bset, (*list)->bset); + if (eq < 0) + return -1; + if (!eq) + continue; + todo = *list; + *list = todo->next; + todo->next = NULL; + free_todo(todo); + return 1; + } + + return 0; +} + +/* Create todo items for all interior facets of the chamber represented + * by "tab" and collect them in first->next, taking care to cancel + * opposite todo items. + */ +static int update_todo(struct isl_facet_todo *first, struct isl_tab *tab) +{ + int i; + struct isl_tab_undo *snap; + struct isl_facet_todo *todo; + + snap = isl_tab_snap(tab); + + for (i = 0; i < tab->n_con; ++i) { + int drop; + + if (tab->con[i].frozen) + continue; + if (tab->con[i].is_redundant) + continue; + + if (isl_tab_select_facet(tab, i) < 0) + return -1; + + todo = create_todo(tab, i); + if (!todo) + return -1; + + drop = has_opposite(todo, &first->next); + if (drop < 0) + return -1; + + if (drop) + free_todo(todo); + else { + todo->next = first->next; + first->next = todo; + } + + if (isl_tab_rollback(tab, snap) < 0) + return -1; + } + + return 0; +} + +/* Compute the chamber decomposition of the parametric polytope respresented + * by "bset" given the parametric vertices and their activity domains. + * + * We are only interested in full-dimensional chambers. + * Each of these chambers is the intersection of the activity domains of + * one or more vertices and the union of all chambers is equal to the + * projection of the entire parametric polytope onto the parameter space. + * + * We first create an initial chamber by intersecting as many activity + * domains as possible without ending up with an empty or lower-dimensional + * set. As a minor optimization, we only consider those activity domains + * that contain some arbitrary point. + * + * For each of interior facets of the chamber, we construct a todo item, + * containing the facet and a constraint containing the other side of the facet, + * for constructing the chamber on the other side. + * While their are any todo items left, we pick a todo item and + * create the required chamber by intersecting all activity domains + * that contain the facet and have a full-dimensional intersection with + * the other side of the facet. For each of the interior facets, we + * again create todo items, taking care to cancel opposite todo items. + */ +static __isl_give isl_vertices *compute_chambers(__isl_take isl_basic_set *bset, + __isl_take isl_vertices *vertices) +{ + int i; + isl_ctx *ctx; + isl_vec *sample = NULL; + struct isl_tab *tab = NULL; + struct isl_tab_undo *snap; + int *selection = NULL; + int n_chambers = 0; + struct isl_chamber_list *list = NULL; + struct isl_facet_todo *todo = NULL; + + if (!bset || !vertices) + goto error; + + ctx = isl_vertices_get_ctx(vertices); + selection = isl_alloc_array(ctx, int, vertices->n_vertices); + if (vertices->n_vertices && !selection) + goto error; + + bset = isl_basic_set_params(bset); + + tab = isl_tab_from_basic_set(bset, 1); + if (!tab) + goto error; + for (i = 0; i < bset->n_ineq; ++i) + if (isl_tab_freeze_constraint(tab, i) < 0) + goto error; + isl_basic_set_free(bset); + + snap = isl_tab_snap(tab); + + sample = isl_tab_get_sample_value(tab); + + for (i = 0; i < vertices->n_vertices; ++i) { + selection[i] = isl_basic_set_contains(vertices->v[i].dom, sample); + if (selection[i] < 0) + goto error; + if (!selection[i]) + continue; + selection[i] = can_intersect(tab, vertices->v[i].dom); + if (selection[i] < 0) + goto error; + } + + if (isl_tab_detect_redundant(tab) < 0) + goto error; + + if (add_chamber(&list, vertices, tab, selection) < 0) + goto error; + n_chambers++; + + if (init_todo(&todo, tab) < 0) + goto error; + + while (todo) { + struct isl_facet_todo *next; + + if (isl_tab_rollback(tab, snap) < 0) + goto error; + + if (isl_tab_add_ineq(tab, todo->constraint->el) < 0) + goto error; + if (isl_tab_freeze_constraint(tab, tab->n_con - 1) < 0) + goto error; + + for (i = 0; i < vertices->n_vertices; ++i) { + selection[i] = bset_covers_tab(vertices->v[i].dom, + todo->tab); + if (selection[i] < 0) + goto error; + if (!selection[i]) + continue; + selection[i] = can_intersect(tab, vertices->v[i].dom); + if (selection[i] < 0) + goto error; + } + + if (isl_tab_detect_redundant(tab) < 0) + goto error; + + if (add_chamber(&list, vertices, tab, selection) < 0) + goto error; + n_chambers++; + + if (update_todo(todo, tab) < 0) + goto error; + + next = todo->next; + todo->next = NULL; + free_todo(todo); + todo = next; + } + + isl_vec_free(sample); + + isl_tab_free(tab); + free(selection); + + vertices = vertices_add_chambers(vertices, n_chambers, list); + + for (i = 0; vertices && i < vertices->n_vertices; ++i) { + isl_basic_set_free(vertices->v[i].dom); + vertices->v[i].dom = NULL; + } + + return vertices; +error: + free_chamber_list(list); + free_todo(todo); + isl_vec_free(sample); + isl_tab_free(tab); + free(selection); + if (!tab) + isl_basic_set_free(bset); + isl_vertices_free(vertices); + return NULL; +} + +isl_ctx *isl_vertex_get_ctx(__isl_keep isl_vertex *vertex) +{ + return vertex ? isl_vertices_get_ctx(vertex->vertices) : NULL; +} + +int isl_vertex_get_id(__isl_keep isl_vertex *vertex) +{ + return vertex ? vertex->id : -1; +} + +__isl_give isl_basic_set *isl_basic_set_set_integral(__isl_take isl_basic_set *bset) +{ + if (!bset) + return NULL; + + if (!ISL_F_ISSET(bset, ISL_BASIC_MAP_RATIONAL)) + return bset; + + bset = isl_basic_set_cow(bset); + if (!bset) + return NULL; + + ISL_F_CLR(bset, ISL_BASIC_MAP_RATIONAL); + + return isl_basic_set_finalize(bset); +} + +/* Return the activity domain of the vertex "vertex". + */ +__isl_give isl_basic_set *isl_vertex_get_domain(__isl_keep isl_vertex *vertex) +{ + struct isl_vertex *v; + + if (!vertex) + return NULL; + + v = &vertex->vertices->v[vertex->id]; + if (!v->dom) { + v->dom = isl_basic_set_copy(v->vertex); + v->dom = isl_basic_set_params(v->dom); + v->dom = isl_basic_set_set_integral(v->dom); + } + + return isl_basic_set_copy(v->dom); +} + +/* Return a multiple quasi-affine expression describing the vertex "vertex" + * in terms of the parameters, + */ +__isl_give isl_multi_aff *isl_vertex_get_expr(__isl_keep isl_vertex *vertex) +{ + struct isl_vertex *v; + isl_basic_set *bset; + + if (!vertex) + return NULL; + + v = &vertex->vertices->v[vertex->id]; + + bset = isl_basic_set_copy(v->vertex); + return isl_multi_aff_from_basic_set_equalities(bset); +} + +static __isl_give isl_vertex *isl_vertex_alloc(__isl_take isl_vertices *vertices, + int id) +{ + isl_ctx *ctx; + isl_vertex *vertex; + + if (!vertices) + return NULL; + + ctx = isl_vertices_get_ctx(vertices); + vertex = isl_alloc_type(ctx, isl_vertex); + if (!vertex) + goto error; + + vertex->vertices = vertices; + vertex->id = id; + + return vertex; +error: + isl_vertices_free(vertices); + return NULL; +} + +void isl_vertex_free(__isl_take isl_vertex *vertex) +{ + if (!vertex) + return; + isl_vertices_free(vertex->vertices); + free(vertex); +} + +isl_ctx *isl_cell_get_ctx(__isl_keep isl_cell *cell) +{ + return cell ? cell->dom->ctx : NULL; +} + +__isl_give isl_basic_set *isl_cell_get_domain(__isl_keep isl_cell *cell) +{ + return cell ? isl_basic_set_copy(cell->dom) : NULL; +} + +static __isl_give isl_cell *isl_cell_alloc(__isl_take isl_vertices *vertices, + __isl_take isl_basic_set *dom, int id) +{ + int i; + isl_cell *cell = NULL; + + if (!vertices || !dom) + goto error; + + cell = isl_calloc_type(dom->ctx, isl_cell); + if (!cell) + goto error; + + cell->n_vertices = vertices->c[id].n_vertices; + cell->ids = isl_alloc_array(dom->ctx, int, cell->n_vertices); + if (cell->n_vertices && !cell->ids) + goto error; + for (i = 0; i < cell->n_vertices; ++i) + cell->ids[i] = vertices->c[id].vertices[i]; + cell->vertices = vertices; + cell->dom = dom; + + return cell; +error: + isl_cell_free(cell); + isl_vertices_free(vertices); + isl_basic_set_free(dom); + return NULL; +} + +void isl_cell_free(__isl_take isl_cell *cell) +{ + if (!cell) + return; + + isl_vertices_free(cell->vertices); + free(cell->ids); + isl_basic_set_free(cell->dom); + free(cell); +} + +/* Create a tableau of the cone obtained by first homogenizing the given + * polytope and then making all inequalities strict by setting the + * constant term to -1. + */ +static struct isl_tab *tab_for_shifted_cone(__isl_keep isl_basic_set *bset) +{ + int i; + isl_vec *c = NULL; + struct isl_tab *tab; + + if (!bset) + return NULL; + tab = isl_tab_alloc(bset->ctx, bset->n_ineq + 1, + 1 + isl_basic_set_total_dim(bset), 0); + if (!tab) + return NULL; + tab->rational = ISL_F_ISSET(bset, ISL_BASIC_SET_RATIONAL); + if (ISL_F_ISSET(bset, ISL_BASIC_MAP_EMPTY)) { + if (isl_tab_mark_empty(tab) < 0) + goto error; + return tab; + } + + c = isl_vec_alloc(bset->ctx, 1 + 1 + isl_basic_set_total_dim(bset)); + if (!c) + goto error; + + isl_int_set_si(c->el[0], 0); + for (i = 0; i < bset->n_eq; ++i) { + isl_seq_cpy(c->el + 1, bset->eq[i], c->size - 1); + if (isl_tab_add_eq(tab, c->el) < 0) + goto error; + } + + isl_int_set_si(c->el[0], -1); + for (i = 0; i < bset->n_ineq; ++i) { + isl_seq_cpy(c->el + 1, bset->ineq[i], c->size - 1); + if (isl_tab_add_ineq(tab, c->el) < 0) + goto error; + if (tab->empty) { + isl_vec_free(c); + return tab; + } + } + + isl_seq_clr(c->el + 1, c->size - 1); + isl_int_set_si(c->el[1], 1); + if (isl_tab_add_ineq(tab, c->el) < 0) + goto error; + + isl_vec_free(c); + return tab; +error: + isl_vec_free(c); + isl_tab_free(tab); + return NULL; +} + +/* Compute an interior point of "bset" by selecting an interior + * point in homogeneous space and projecting the point back down. + */ +static __isl_give isl_vec *isl_basic_set_interior_point( + __isl_keep isl_basic_set *bset) +{ + isl_vec *vec; + struct isl_tab *tab; + + tab = tab_for_shifted_cone(bset); + vec = isl_tab_get_sample_value(tab); + isl_tab_free(tab); + if (!vec) + return NULL; + + isl_seq_cpy(vec->el, vec->el + 1, vec->size - 1); + vec->size--; + + return vec; +} + +/* Call "fn" on all chambers of the parametric polytope with the shared + * facets of neighboring chambers only appearing in one of the chambers. + * + * We pick an interior point from one of the chambers and then make + * all constraints that do not satisfy this point strict. + */ +int isl_vertices_foreach_disjoint_cell(__isl_keep isl_vertices *vertices, + int (*fn)(__isl_take isl_cell *cell, void *user), void *user) +{ + int i, j; + isl_vec *vec; + isl_int v; + isl_cell *cell; + + if (!vertices) + return -1; + + if (vertices->n_chambers == 0) + return 0; + + if (vertices->n_chambers == 1) { + isl_basic_set *dom = isl_basic_set_copy(vertices->c[0].dom); + dom = isl_basic_set_set_integral(dom); + cell = isl_cell_alloc(isl_vertices_copy(vertices), dom, 0); + if (!cell) + return -1; + return fn(cell, user); + } + + vec = isl_basic_set_interior_point(vertices->c[0].dom); + if (!vec) + return -1; + + isl_int_init(v); + + for (i = 0; i < vertices->n_chambers; ++i) { + int r; + isl_basic_set *dom = isl_basic_set_copy(vertices->c[i].dom); + dom = isl_basic_set_cow(dom); + if (!dom) + goto error; + for (j = 0; i && j < dom->n_ineq; ++j) { + isl_seq_inner_product(vec->el, dom->ineq[j], vec->size, + &v); + if (!isl_int_is_neg(v)) + continue; + isl_int_sub_ui(dom->ineq[j][0], dom->ineq[j][0], 1); + } + dom = isl_basic_set_set_integral(dom); + cell = isl_cell_alloc(isl_vertices_copy(vertices), dom, i); + if (!cell) + goto error; + r = fn(cell, user); + if (r < 0) + goto error; + } + + isl_int_clear(v); + isl_vec_free(vec); + + return 0; +error: + isl_int_clear(v); + isl_vec_free(vec); + return -1; +} + +isl_stat isl_vertices_foreach_cell(__isl_keep isl_vertices *vertices, + isl_stat (*fn)(__isl_take isl_cell *cell, void *user), void *user) +{ + int i; + isl_cell *cell; + + if (!vertices) + return isl_stat_error; + + if (vertices->n_chambers == 0) + return isl_stat_ok; + + for (i = 0; i < vertices->n_chambers; ++i) { + isl_stat r; + isl_basic_set *dom = isl_basic_set_copy(vertices->c[i].dom); + + cell = isl_cell_alloc(isl_vertices_copy(vertices), dom, i); + if (!cell) + return isl_stat_error; + + r = fn(cell, user); + if (r < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +isl_stat isl_vertices_foreach_vertex(__isl_keep isl_vertices *vertices, + isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user) +{ + int i; + isl_vertex *vertex; + + if (!vertices) + return isl_stat_error; + + if (vertices->n_vertices == 0) + return isl_stat_ok; + + for (i = 0; i < vertices->n_vertices; ++i) { + isl_stat r; + + vertex = isl_vertex_alloc(isl_vertices_copy(vertices), i); + if (!vertex) + return isl_stat_error; + + r = fn(vertex, user); + if (r < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +isl_stat isl_cell_foreach_vertex(__isl_keep isl_cell *cell, + isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user) +{ + int i; + isl_vertex *vertex; + + if (!cell) + return isl_stat_error; + + if (cell->n_vertices == 0) + return isl_stat_ok; + + for (i = 0; i < cell->n_vertices; ++i) { + isl_stat r; + + vertex = isl_vertex_alloc(isl_vertices_copy(cell->vertices), + cell->ids[i]); + if (!vertex) + return isl_stat_error; + + r = fn(vertex, user); + if (r < 0) + return isl_stat_error; + } + + return isl_stat_ok; +} + +isl_ctx *isl_vertices_get_ctx(__isl_keep isl_vertices *vertices) +{ + return vertices ? vertices->bset->ctx : NULL; +} + +int isl_vertices_get_n_vertices(__isl_keep isl_vertices *vertices) +{ + return vertices ? vertices->n_vertices : -1; +} + +__isl_give isl_vertices *isl_morph_vertices(__isl_take isl_morph *morph, + __isl_take isl_vertices *vertices) +{ + int i; + isl_morph *param_morph = NULL; + + if (!morph || !vertices) + goto error; + + isl_assert(vertices->bset->ctx, vertices->ref == 1, goto error); + + param_morph = isl_morph_copy(morph); + param_morph = isl_morph_dom_params(param_morph); + param_morph = isl_morph_ran_params(param_morph); + + for (i = 0; i < vertices->n_vertices; ++i) { + vertices->v[i].dom = isl_morph_basic_set( + isl_morph_copy(param_morph), vertices->v[i].dom); + vertices->v[i].vertex = isl_morph_basic_set( + isl_morph_copy(morph), vertices->v[i].vertex); + if (!vertices->v[i].vertex) + goto error; + } + + for (i = 0; i < vertices->n_chambers; ++i) { + vertices->c[i].dom = isl_morph_basic_set( + isl_morph_copy(param_morph), vertices->c[i].dom); + if (!vertices->c[i].dom) + goto error; + } + + isl_morph_free(param_morph); + isl_morph_free(morph); + return vertices; +error: + isl_morph_free(param_morph); + isl_morph_free(morph); + isl_vertices_free(vertices); + return NULL; +} + +/* Construct a simplex isl_cell spanned by the vertices with indices in + * "simplex_ids" and "other_ids" and call "fn" on this isl_cell. + */ +static int call_on_simplex(__isl_keep isl_cell *cell, + int *simplex_ids, int n_simplex, int *other_ids, int n_other, + int (*fn)(__isl_take isl_cell *simplex, void *user), void *user) +{ + int i; + isl_ctx *ctx; + struct isl_cell *simplex; + + ctx = isl_cell_get_ctx(cell); + + simplex = isl_calloc_type(ctx, struct isl_cell); + if (!simplex) + return -1; + simplex->vertices = isl_vertices_copy(cell->vertices); + if (!simplex->vertices) + goto error; + simplex->dom = isl_basic_set_copy(cell->dom); + if (!simplex->dom) + goto error; + simplex->n_vertices = n_simplex + n_other; + simplex->ids = isl_alloc_array(ctx, int, simplex->n_vertices); + if (!simplex->ids) + goto error; + + for (i = 0; i < n_simplex; ++i) + simplex->ids[i] = simplex_ids[i]; + for (i = 0; i < n_other; ++i) + simplex->ids[n_simplex + i] = other_ids[i]; + + return fn(simplex, user); +error: + isl_cell_free(simplex); + return -1; +} + +/* Check whether the parametric vertex described by "vertex" + * lies on the facet corresponding to constraint "facet" of "bset". + * The isl_vec "v" is a temporary vector than can be used by this function. + * + * We eliminate the variables from the facet constraint using the + * equalities defining the vertex and check if the result is identical + * to zero. + * + * It would probably be better to keep track of the constraints defining + * a vertex during the vertex construction so that we could simply look + * it up here. + */ +static int vertex_on_facet(__isl_keep isl_basic_set *vertex, + __isl_keep isl_basic_set *bset, int facet, __isl_keep isl_vec *v) +{ + int i; + isl_int m; + + isl_seq_cpy(v->el, bset->ineq[facet], v->size); + + isl_int_init(m); + for (i = 0; i < vertex->n_eq; ++i) { + int k = isl_seq_last_non_zero(vertex->eq[i], v->size); + isl_seq_elim(v->el, vertex->eq[i], k, v->size, &m); + } + isl_int_clear(m); + + return isl_seq_first_non_zero(v->el, v->size) == -1; +} + +/* Triangulate the polytope spanned by the vertices with ids + * in "simplex_ids" and "other_ids" and call "fn" on each of + * the resulting simplices. + * If the input polytope is already a simplex, we simply call "fn". + * Otherwise, we pick a point from "other_ids" and add it to "simplex_ids". + * Then we consider each facet of "bset" that does not contain the point + * we just picked, but does contain some of the other points in "other_ids" + * and call ourselves recursively on the polytope spanned by the new + * "simplex_ids" and those points in "other_ids" that lie on the facet. + */ +static int triangulate(__isl_keep isl_cell *cell, __isl_keep isl_vec *v, + int *simplex_ids, int n_simplex, int *other_ids, int n_other, + int (*fn)(__isl_take isl_cell *simplex, void *user), void *user) +{ + int i, j, k; + int d, nparam; + int *ids; + isl_ctx *ctx; + isl_basic_set *vertex; + isl_basic_set *bset; + + ctx = isl_cell_get_ctx(cell); + d = isl_basic_set_dim(cell->vertices->bset, isl_dim_set); + nparam = isl_basic_set_dim(cell->vertices->bset, isl_dim_param); + + if (n_simplex + n_other == d + 1) + return call_on_simplex(cell, simplex_ids, n_simplex, + other_ids, n_other, fn, user); + + simplex_ids[n_simplex] = other_ids[0]; + vertex = cell->vertices->v[other_ids[0]].vertex; + bset = cell->vertices->bset; + + ids = isl_alloc_array(ctx, int, n_other - 1); + for (i = 0; i < bset->n_ineq; ++i) { + if (isl_seq_first_non_zero(bset->ineq[i] + 1 + nparam, d) == -1) + continue; + if (vertex_on_facet(vertex, bset, i, v)) + continue; + + for (j = 1, k = 0; j < n_other; ++j) { + isl_basic_set *ov; + ov = cell->vertices->v[other_ids[j]].vertex; + if (vertex_on_facet(ov, bset, i, v)) + ids[k++] = other_ids[j]; + } + if (k == 0) + continue; + + if (triangulate(cell, v, simplex_ids, n_simplex + 1, + ids, k, fn, user) < 0) + goto error; + } + free(ids); + + return 0; +error: + free(ids); + return -1; +} + +/* Triangulate the given cell and call "fn" on each of the resulting + * simplices. + */ +int isl_cell_foreach_simplex(__isl_take isl_cell *cell, + int (*fn)(__isl_take isl_cell *simplex, void *user), void *user) +{ + int d, total; + int r; + isl_ctx *ctx; + isl_vec *v = NULL; + int *simplex_ids = NULL; + + if (!cell) + return -1; + + d = isl_basic_set_dim(cell->vertices->bset, isl_dim_set); + total = isl_basic_set_total_dim(cell->vertices->bset); + + if (cell->n_vertices == d + 1) + return fn(cell, user); + + ctx = isl_cell_get_ctx(cell); + simplex_ids = isl_alloc_array(ctx, int, d + 1); + if (!simplex_ids) + goto error; + + v = isl_vec_alloc(ctx, 1 + total); + if (!v) + goto error; + + r = triangulate(cell, v, simplex_ids, 0, + cell->ids, cell->n_vertices, fn, user); + + isl_vec_free(v); + free(simplex_ids); + + isl_cell_free(cell); + + return r; +error: + free(simplex_ids); + isl_vec_free(v); + isl_cell_free(cell); + return -1; +} Index: lib/Analysis/isl/isl_vertices_private.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_vertices_private.h @@ -0,0 +1,64 @@ +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct isl_morph; + +/* A parametric vertex. "vertex" contains the actual description + * of the vertex as a singleton parametric set. "dom" is the projection + * of "vertex" onto the parameter space, i.e., the activity domain + * of the vertex. + */ +struct isl_vertex { + isl_basic_set *dom; + isl_basic_set *vertex; +}; + +/* A chamber in the chamber decomposition. The indices of the "n_vertices" + * active vertices are stored in "vertices". + */ +struct isl_chamber { + int n_vertices; + int *vertices; + isl_basic_set *dom; +}; + +struct isl_vertices { + int ref; + + /* The rational basic set spanned by the vertices. */ + isl_basic_set *bset; + + int n_vertices; + struct isl_vertex *v; + + int n_chambers; + struct isl_chamber *c; +}; + +struct isl_cell { + int n_vertices; + int *ids; + isl_vertices *vertices; + isl_basic_set *dom; +}; + +struct isl_external_vertex { + isl_vertices *vertices; + int id; +}; + +int isl_vertices_foreach_disjoint_cell(__isl_keep isl_vertices *vertices, + int (*fn)(__isl_take isl_cell *cell, void *user), void *user); +int isl_cell_foreach_simplex(__isl_take isl_cell *cell, + int (*fn)(__isl_take isl_cell *simplex, void *user), void *user); + +__isl_give isl_vertices *isl_morph_vertices(__isl_take struct isl_morph *morph, + __isl_take isl_vertices *vertices); + +#if defined(__cplusplus) +} +#endif Index: lib/Analysis/isl/isl_yaml.h =================================================================== --- /dev/null +++ lib/Analysis/isl/isl_yaml.h @@ -0,0 +1,18 @@ +#ifndef ISL_YAML_H +#define ISL_YAML_H + +#define ISL_YAML_INDENT_FLOW -1 + +enum isl_yaml_state { + isl_yaml_none, + isl_yaml_mapping_first_key_start, + isl_yaml_mapping_key_start, + isl_yaml_mapping_key, + isl_yaml_mapping_val_start, + isl_yaml_mapping_val, + isl_yaml_sequence_first_start, + isl_yaml_sequence_start, + isl_yaml_sequence +}; + +#endif Index: lib/Analysis/isl/ltmain.sh =================================================================== --- /dev/null +++ lib/Analysis/isl/ltmain.sh @@ -0,0 +1,9661 @@ + +# libtool (GNU libtool) 2.4.2 +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, +# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. +# +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --no-quiet, --no-silent +# print informational messages (default) +# --no-warn don't display warning messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print more informational messages than default +# --no-verbose don't print the extra informational messages +# --version print version information +# -h, --help, --help-all print short, long, or detailed help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. When passed as first option, +# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.11 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . +# GNU libtool home page: . +# General help using GNU software: . + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.2 Debian-2.4.2-1.11" +TIMESTAMP="" +package_revision=1.3337 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# NLS nuisances: We save the old values to restore during execute mode. +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done +LC_ALL=C +LANGUAGE=C +export LANGUAGE LC_ALL + +$lt_unset CDPATH + + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + + + +: ${CP="cp -f"} +test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} # func_dirname may be replaced by extended shell implementation + + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} # func_basename may be replaced by extended shell implementation + + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` +} # func_dirname_and_basename may be replaced by extended shell implementation + + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname may be replaced by extended shell implementation + + +# These SED scripts presuppose an absolute path with a trailing slash. +pathcar='s,^/\([^/]*\).*$,\1,' +pathcdr='s,^/[^/]*,,' +removedotparts=':dotsl + s@/\./@/@g + t dotsl + s,/\.$,/,' +collapseslashes='s@/\{1,\}@/@g' +finalslash='s,/*$,/,' + +# func_normal_abspath PATH +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +# value returned in "$func_normal_abspath_result" +func_normal_abspath () +{ + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` + while :; do + # Processed it all yet? + if test "$func_normal_abspath_tpath" = / ; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result" ; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + +# func_relative_path SRCDIR DSTDIR +# generates a relative path from SRCDIR to DSTDIR, with a trailing +# slash if non-empty, suitable for immediately appending a filename +# without needing to append a separator. +# value returned in "$func_relative_path_result" +func_relative_path () +{ + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=${func_dirname_result} + if test "x$func_relative_path_tlibdir" = x ; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test "x$func_stripname_result" != x ; then + func_relative_path_result=${func_relative_path_result}/${func_stripname_result} + fi + + # Normalisation. If bindir is libdir, return empty string, + # else relative path ending with a slash; either way, target + # file name can be directly appended. + if test ! -z "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result/" + func_relative_path_result=$func_stripname_result + fi +} + +# The name of this program: +func_dirname_and_basename "$progpath" +progname=$func_basename_result + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' + +# Sed substitution that converts a w32 file name or path +# which contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }$*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` + done + my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "$my_tmpdir" +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "$1" | $SED \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + +# func_tr_sh +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $opt_debug + + $SED -n '/(C)/!b go + :more + /\./!{ + N + s/\n# / / + b more + } + :go + /^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $opt_debug + + $SED -n '/^# Usage:/,/^# *.*--help/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + echo + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help [NOEXIT] +# Echo long help message to standard output and exit, +# unless 'noexit' is passed as argument. +func_help () +{ + $opt_debug + + $SED -n '/^# Usage:/,/# Report bugs to/ { + :print + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ + p + d + } + /^# .* home page:/b print + /^# General help using/b print + ' < "$progpath" + ret=$? + if test -z "$1"; then + exit $ret + fi +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $opt_debug + + func_error "missing argument for $1." + exit_cmd=exit +} + + +# func_split_short_opt shortopt +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +func_split_short_opt () +{ + my_sed_short_opt='1s/^\(..\).*$/\1/;q' + my_sed_short_rest='1s/^..\(.*\)$/\1/;q' + + func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` + func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` +} # func_split_short_opt may be replaced by extended shell implementation + + +# func_split_long_opt longopt +# Set func_split_long_opt_name and func_split_long_opt_arg shell +# variables after splitting LONGOPT at the `=' sign. +func_split_long_opt () +{ + my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' + my_sed_long_arg='1s/^--[^=]*=//' + + func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` + func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` +} # func_split_long_opt may be replaced by extended shell implementation + +exit_cmd=: + + + + + +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" + +# Global variables. +nonopt= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "${1}=\$${1}\${2}" +} # func_append may be replaced by extended shell implementation + +# func_append_quoted var value +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +func_append_quoted () +{ + func_quote_for_eval "${2}" + eval "${1}=\$${1}\\ \$func_quote_for_eval_result" +} # func_append_quoted may be replaced by extended shell implementation + + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "${@}"` +} # func_arith may be replaced by extended shell implementation + + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` +} # func_len may be replaced by extended shell implementation + + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} # func_lo2o may be replaced by extended shell implementation + + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} # func_xform may be replaced by extended shell implementation + + +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + +# func_features +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test "$build_libtool_libs" = yes; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# Shorthand for --mode=foo, only valid as the first argument +case $1 in +clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; +compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; +execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; +finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; +install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; +link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; +uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; +esac + + + +# Option defaults: +opt_debug=: +opt_dry_run=false +opt_config=false +opt_preserve_dup_deps=false +opt_features=false +opt_finish=false +opt_help=false +opt_help_all=false +opt_silent=: +opt_warning=: +opt_verbose=: +opt_silent=false +opt_verbose=false + + +# Parse options once, thoroughly. This comes as soon as possible in the +# script to make things like `--version' happen as quickly as we can. +{ + # this just eases exit handling + while test $# -gt 0; do + opt="$1" + shift + case $opt in + --debug|-x) opt_debug='set -x' + func_echo "enabling shell trace mode" + $opt_debug + ;; + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + --config) + opt_config=: +func_config + ;; + --dlopen|-dlopen) + optarg="$1" + opt_dlopen="${opt_dlopen+$opt_dlopen +}$optarg" + shift + ;; + --preserve-dup-deps) + opt_preserve_dup_deps=: + ;; + --features) + opt_features=: +func_features + ;; + --finish) + opt_finish=: +set dummy --mode finish ${1+"$@"}; shift + ;; + --help) + opt_help=: + ;; + --help-all) + opt_help_all=: +opt_help=': help-all' + ;; + --mode) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_mode="$optarg" +case $optarg in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; +esac + shift + ;; + --no-silent|--no-quiet) + opt_silent=false +func_append preserve_args " $opt" + ;; + --no-warning|--no-warn) + opt_warning=false +func_append preserve_args " $opt" + ;; + --no-verbose) + opt_verbose=false +func_append preserve_args " $opt" + ;; + --silent|--quiet) + opt_silent=: +func_append preserve_args " $opt" + opt_verbose=false + ;; + --verbose|-v) + opt_verbose=: +func_append preserve_args " $opt" +opt_silent=false + ;; + --tag) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_tag="$optarg" +func_append preserve_args " $opt $optarg" +func_enable_tag "$optarg" + shift + ;; + + -\?|-h) func_usage ;; + --help) func_help ;; + --version) func_version ;; + + # Separate optargs to long options: + --*=*) + func_split_long_opt "$opt" + set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-n*|-v*) + func_split_short_opt "$opt" + set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognized option \`$opt'" ;; + *) set dummy "$opt" ${1+"$@"}; shift; break ;; + esac + done + + # Validate options: + + # save first non-option argument + if test "$#" -gt 0; then + nonopt="$opt" + shift + fi + + # preserve --debug + test "$opt_debug" = : || func_append preserve_args " --debug" + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test "$opt_mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$opt_mode' for more information." + } + + + # Bail if the options were screwed + $exit_cmd $EXIT_FAILURE +} + + + + +## ----------- ## +## Main. ## +## ----------- ## + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case "$lt_sysroot:$1" in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result="=$func_stripname_result" + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T </dev/null` + if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$lt_sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $opt_debug + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result="" + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $opt_debug + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $opt_debug + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $opt_debug + if test -z "$2" && test -n "$1" ; then + func_error "Could not determine host file name corresponding to" + func_error " \`$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result="$1" + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $opt_debug + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " \`$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result="$3" + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $opt_debug + case $4 in + $1 ) func_to_host_path_result="$3$func_to_host_path_result" + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via `$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $opt_debug + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $opt_debug + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result="$1" +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result="$func_convert_core_msys_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via `$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $opt_debug + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd="func_convert_path_${func_stripname_result}" + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $opt_debug + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result="$1" +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_mode_compile arg... +func_mode_compile () +{ + $opt_debug + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify \`-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + func_append_quoted lastarg "$arg" + done + IFS="$save_ifs" + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with \`-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from \`$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test "$opt_mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode \`$opt_mode'" + ;; + esac + + echo + $ECHO "Try \`$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test "$opt_help" = :; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | sed -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + sed '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "\`$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument \`$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test "$opt_mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test "x$prev" = x-m && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename="" + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname" ; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename="$func_basename_result" + else + # no lafile. user explicitly requested -dlpreopen . + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename" ; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $opt_debug + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $opt_debug + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive which possess that section. Heuristic: eliminate + # all those which have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $opt_debug + if func_cygming_gnu_implib_p "$1" ; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1" ; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result="" + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + if test "$lock_old_archive_extraction" = yes; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test "$lock_old_archive_extraction" = yes; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +#else +# include +# include +# ifdef __CYGWIN__ +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +/* declarations of non-ANSI functions */ +#if defined(__MINGW32__) +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined(__CYGWIN__) +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined (other platforms) ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined(_MSC_VER) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +# ifndef _INTPTR_T_DEFINED +# define _INTPTR_T_DEFINED +# define intptr_t int +# endif +#elif defined(__MINGW32__) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined(__CYGWIN__) +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined (other platforms) ... */ +#endif + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#if defined(LT_DEBUGWRAPPER) +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $opt_debug + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir="$arg" + prev= + continue + ;; + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps ; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + else + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test "$prefer_static_libs" = yes || + test "$prefer_static_libs,$installed" = "built,no"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib="$l" + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$lt_sysroot$libdir" + absdir="$lt_sysroot$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + case "$host" in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + echo + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$opt_mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$absdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$opt_mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system can not link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|qnx|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type \`$version_type'" + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + func_append verstring ":${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + func_append libobjs " $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$opt_mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test "X$deplibs_check_method" = "Xnone"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + # Remove ${wl} instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$opt_mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd1 in $cmds; do + IFS="$save_ifs" + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test "$try_normal_branch" = yes \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=${output_objdir}/${output_la}.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " ${wl}-bind_at_load" + func_append finalize_command " ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=no + ;; + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + func_append oldobjs " $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + func_resolve_sysroot "$deplib" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test "x$bindir" != x ; + then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$opt_mode" = link || test "$opt_mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=yes ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + odir="$objdir" + else + odir="$dir/$objdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$opt_mode" = uninstall && odir="$dir" + + # Remember odir for removal later, being careful to avoid duplicates + if test "$opt_mode" = clean; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case "$opt_mode" in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$opt_mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + func_append rmfiles " $odir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$opt_mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# vi:sw=2 + Index: lib/Analysis/isl/m4/ax_c___attribute__.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_c___attribute__.m4 @@ -0,0 +1,66 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_c___attribute__.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_C___ATTRIBUTE__ +# +# DESCRIPTION +# +# Provides a test for the compiler support of __attribute__ extensions. +# Defines HAVE___ATTRIBUTE__ if it is found. +# +# LICENSE +# +# Copyright (c) 2008 Stepan Kasal +# Copyright (c) 2008 Christian Haggstrom +# Copyright (c) 2008 Ryan McCabe +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 8 + +AC_DEFUN([AX_C___ATTRIBUTE__], [ + AC_CACHE_CHECK([for __attribute__], [ax_cv___attribute__], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + static void foo(void) __attribute__ ((unused)); + static void + foo(void) { + exit(1); + } + ]], [])], + [ax_cv___attribute__=yes], + [ax_cv___attribute__=no] + ) + ]) + if test "$ax_cv___attribute__" = "yes"; then + AC_DEFINE([HAVE___ATTRIBUTE__], 1, [define if your compiler has __attribute__]) + fi +]) Index: lib/Analysis/isl/m4/ax_cc_maxopt.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_cc_maxopt.m4 @@ -0,0 +1,188 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_cc_maxopt.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CC_MAXOPT +# +# DESCRIPTION +# +# Try to turn on "good" C optimization flags for various compilers and +# architectures, for some definition of "good". (In our case, good for +# FFTW and hopefully for other scientific codes. Modify as needed.) +# +# The user can override the flags by setting the CFLAGS environment +# variable. The user can also specify --enable-portable-binary in order to +# disable any optimization flags that might result in a binary that only +# runs on the host architecture. +# +# Note also that the flags assume that ANSI C aliasing rules are followed +# by the code (e.g. for gcc's -fstrict-aliasing), and that floating-point +# computations can be re-ordered as needed. +# +# Requires macros: AX_CHECK_COMPILER_FLAGS, AX_COMPILER_VENDOR, +# AX_GCC_ARCHFLAG, AX_GCC_X86_CPUID. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_CC_MAXOPT], +[ +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AX_COMPILER_VENDOR]) +AC_REQUIRE([AC_CANONICAL_HOST]) + +AC_ARG_ENABLE(portable-binary, [AC_HELP_STRING([--enable-portable-binary], [disable compiler optimizations that would produce unportable binaries])], + acx_maxopt_portable=$withval, acx_maxopt_portable=no) + +# Try to determine "good" native compiler flags if none specified via CFLAGS +if test "$ac_test_CFLAGS" != "set"; then + CFLAGS="" + case $ax_cv_c_compiler_vendor in + dec) CFLAGS="-newc -w0 -O5 -ansi_alias -ansi_args -fp_reorder -tune host" + if test "x$acx_maxopt_portable" = xno; then + CFLAGS="$CFLAGS -arch host" + fi;; + + sun) CFLAGS="-native -fast -xO5 -dalign" + if test "x$acx_maxopt_portable" = xyes; then + CFLAGS="$CFLAGS -xarch=generic" + fi;; + + hp) CFLAGS="+Oall +Optrs_ansi +DSnative" + if test "x$acx_maxopt_portable" = xyes; then + CFLAGS="$CFLAGS +DAportable" + fi;; + + ibm) if test "x$acx_maxopt_portable" = xno; then + xlc_opt="-qarch=auto -qtune=auto" + else + xlc_opt="-qtune=auto" + fi + AX_CHECK_COMPILER_FLAGS($xlc_opt, + CFLAGS="-O3 -qansialias -w $xlc_opt", + [CFLAGS="-O3 -qansialias -w" + echo "******************************************************" + echo "* You seem to have the IBM C compiler. It is *" + echo "* recommended for best performance that you use: *" + echo "* *" + echo "* CFLAGS=-O3 -qarch=xxx -qtune=xxx -qansialias -w *" + echo "* ^^^ ^^^ *" + echo "* where xxx is pwr2, pwr3, 604, or whatever kind of *" + echo "* CPU you have. (Set the CFLAGS environment var. *" + echo "* and re-run configure.) For more info, man cc. *" + echo "******************************************************"]) + ;; + + intel) CFLAGS="-O3 -ansi_alias" + if test "x$acx_maxopt_portable" = xno; then + icc_archflag=unknown + icc_flags="" + case $host_cpu in + i686*|x86_64*) + # icc accepts gcc assembly syntax, so these should work: + AX_GCC_X86_CPUID(0) + AX_GCC_X86_CPUID(1) + case $ax_cv_gcc_x86_cpuid_0 in # see AX_GCC_ARCHFLAG + *:756e6547:*:*) # Intel + case $ax_cv_gcc_x86_cpuid_1 in + *6a?:*[[234]]:*:*|*6[[789b]]?:*:*:*) icc_flags="-xK";; + *f3[[347]]:*:*:*|*f4[1347]:*:*:*) icc_flags="-xP -xN -xW -xK";; + *f??:*:*:*) icc_flags="-xN -xW -xK";; + esac ;; + esac ;; + esac + if test "x$icc_flags" != x; then + for flag in $icc_flags; do + AX_CHECK_COMPILER_FLAGS($flag, [icc_archflag=$flag; break]) + done + fi + AC_MSG_CHECKING([for icc architecture flag]) + AC_MSG_RESULT($icc_archflag) + if test "x$icc_archflag" != xunknown; then + CFLAGS="$CFLAGS $icc_archflag" + fi + fi + ;; + + gnu) + # default optimization flags for gcc on all systems + CFLAGS="-O3 -fomit-frame-pointer" + + # -malign-double for x86 systems + AX_CHECK_COMPILER_FLAGS(-malign-double, CFLAGS="$CFLAGS -malign-double") + + # -fstrict-aliasing for gcc-2.95+ + AX_CHECK_COMPILER_FLAGS(-fstrict-aliasing, + CFLAGS="$CFLAGS -fstrict-aliasing") + + # note that we enable "unsafe" fp optimization with other compilers, too + AX_CHECK_COMPILER_FLAGS(-ffast-math, CFLAGS="$CFLAGS -ffast-math") + + AX_GCC_ARCHFLAG($acx_maxopt_portable) + + # drop to -O1 for gcc 4.2 + $CC --version | + sed -e 's/.* \(@<:@0-9@:>@@<:@0-9@:>@*\)\.\(@<:@0-9@:>@@<:@0-9@:>@*\).*/\1 \2/' | + (read major minor + if test $major -eq 4 -a $minor -eq 2; then + exit 0 + fi + exit 1 + ) && CFLAGS="-O1" + ;; + esac + + if test -z "$CFLAGS"; then + echo "" + echo "********************************************************" + echo "* WARNING: Don't know the best CFLAGS for this system *" + echo "* Use ./configure CFLAGS=... to specify your own flags *" + echo "* (otherwise, a default of CFLAGS=-O3 will be used) *" + echo "********************************************************" + echo "" + CFLAGS="-O3" + fi + + AX_CHECK_COMPILER_FLAGS($CFLAGS, [], [ + echo "" + echo "********************************************************" + echo "* WARNING: The guessed CFLAGS don't seem to work with *" + echo "* your compiler. *" + echo "* Use ./configure CFLAGS=... to specify your own flags *" + echo "********************************************************" + echo "" + CFLAGS="" + ]) + +fi +]) Index: lib/Analysis/isl/m4/ax_check_compiler_flags.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_check_compiler_flags.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_check_compiler_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILER_FLAGS(FLAGS, [ACTION-SUCCESS], [ACTION-FAILURE]) +# +# DESCRIPTION +# +# Check whether the given compiler FLAGS work with the current language's +# compiler, or whether they give an error. (Warnings, however, are +# ignored.) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# LICENSE +# +# Copyright (c) 2009 Steven G. Johnson +# Copyright (c) 2009 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_CHECK_COMPILER_FLAGS], +[AC_PREREQ(2.59) dnl for _AC_LANG_PREFIX +AC_MSG_CHECKING([whether _AC_LANG compiler accepts $1]) +dnl Some hackery here since AC_CACHE_VAL can't handle a non-literal varname: +AS_LITERAL_IF([$1], + [AC_CACHE_VAL(AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1]), [ + ax_save_FLAGS=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$1" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], + AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=yes, + AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=no) + _AC_LANG_PREFIX[]FLAGS=$ax_save_FLAGS])], + [ax_save_FLAGS=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$1" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], + eval AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=yes, + eval AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1])=no) + _AC_LANG_PREFIX[]FLAGS=$ax_save_FLAGS]) +eval ax_check_compiler_flags=$AS_TR_SH(ax_cv_[]_AC_LANG_ABBREV[]_flags_[$1]) +AC_MSG_RESULT($ax_check_compiler_flags) +if test "x$ax_check_compiler_flags" = xyes; then + m4_default([$2], :) +else + m4_default([$3], :) +fi +])dnl AX_CHECK_COMPILER_FLAGS Index: lib/Analysis/isl/m4/ax_compiler_vendor.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_compiler_vendor.m4 @@ -0,0 +1,63 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun, +# hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft, +# watcom, etc. The vendor is returned in the cache variable +# $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 9 + +AC_DEFUN([AX_COMPILER_VENDOR], +[ +AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + [ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=unknown + # note: don't check for gcc first since some other compilers define __GNUC__ + for ventest in intel:__ICC,__ECC,__INTEL_COMPILER ibm:__xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale:__PATHCC__,__PATHSCALE__ clang:__clang__ gnu:__GNUC__ sun:__SUNPRO_C,__SUNPRO_CC hp:__HP_cc,__HP_aCC dec:__DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland:__BORLANDC__,__TURBOC__ comeau:__COMO__ cray:_CRAYC kai:__KCC lcc:__LCC__ metrowerks:__MWERKS__ sgi:__sgi,sgi microsoft:_MSC_VER watcom:__WATCOMC__ portland:__PGI; do + vencpp="defined("`echo $ventest | cut -d: -f2 | sed 's/,/) || defined(/g'`")" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ +#if !($vencpp) + thisisanerror; +#endif +])], [ax_cv_]_AC_LANG_ABBREV[_compiler_vendor=`echo $ventest | cut -d: -f1`; break]) + done + ]) +]) Index: lib/Analysis/isl/m4/ax_create_pkgconfig_info.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_create_pkgconfig_info.m4 @@ -0,0 +1,351 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_create_pkgconfig_info.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CREATE_PKGCONFIG_INFO [(outputfile, [requires [,libs [,summary [,cflags [, ldflags]]]]])] +# +# DESCRIPTION +# +# Defaults: +# +# $1 = $PACKAGE_NAME.pc +# $2 = (empty) +# $3 = $PACKAGE_LIBS $LIBS (as set at that point in configure.ac) +# $4 = $PACKAGE_SUMMARY (or $1 Library) +# $5 = $PACKAGE_CFLAGS (as set at the point in configure.ac) +# $6 = $PACKAGE_LDFLAGS (as set at the point in configure.ac) +# +# PACKAGE_NAME defaults to $PACKAGE if not set. +# PACKAGE_LIBS defaults to -l$PACKAGE_NAME if not set. +# +# The resulting file is called $PACKAGE.pc.in / $PACKAGE.pc +# +# You will find this macro most useful in conjunction with +# ax_spec_defaults that can read good initializers from the .spec file. In +# consequencd, most of the generatable installable stuff can be made from +# information being updated in a single place for the whole project. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2008 Sven Verdoolaege +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 12 + +AC_DEFUN([AX_CREATE_PKGCONFIG_INFO],[dnl +AS_VAR_PUSHDEF([PKGCONFIG_suffix],[ax_create_pkgconfig_suffix])dnl +AS_VAR_PUSHDEF([PKGCONFIG_libdir],[ax_create_pkgconfig_libdir])dnl +AS_VAR_PUSHDEF([PKGCONFIG_libfile],[ax_create_pkgconfig_libfile])dnl +AS_VAR_PUSHDEF([PKGCONFIG_libname],[ax_create_pkgconfig_libname])dnl +AS_VAR_PUSHDEF([PKGCONFIG_version],[ax_create_pkgconfig_version])dnl +AS_VAR_PUSHDEF([PKGCONFIG_description],[ax_create_pkgconfig_description])dnl +AS_VAR_PUSHDEF([PKGCONFIG_requires],[ax_create_pkgconfig_requires])dnl +AS_VAR_PUSHDEF([PKGCONFIG_pkglibs],[ax_create_pkgconfig_pkglibs])dnl +AS_VAR_PUSHDEF([PKGCONFIG_libs],[ax_create_pkgconfig_libs])dnl +AS_VAR_PUSHDEF([PKGCONFIG_ldflags],[ax_create_pkgconfig_ldflags])dnl +AS_VAR_PUSHDEF([PKGCONFIG_cppflags],[ax_create_pkgconfig_cppflags])dnl +AS_VAR_PUSHDEF([PKGCONFIG_generate],[ax_create_pkgconfig_generate])dnl +AS_VAR_PUSHDEF([PKGCONFIG_src_libdir],[ax_create_pkgconfig_src_libdir])dnl +AS_VAR_PUSHDEF([PKGCONFIG_src_headers],[ax_create_pkgconfig_src_headers])dnl + +# we need the expanded forms... +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +AC_MSG_CHECKING(our pkgconfig libname) +test ".$PKGCONFIG_libname" != "." || \ +PKGCONFIG_libname="ifelse($1,,${PACKAGE_NAME},`basename $1 .pc`)" +test ".$PKGCONFIG_libname" != "." || \ +PKGCONFIG_libname="$PACKAGE" +PKGCONFIG_libname=`eval echo "$PKGCONFIG_libname"` +PKGCONFIG_libname=`eval echo "$PKGCONFIG_libname"` +AC_MSG_RESULT($PKGCONFIG_libname) + +AC_MSG_CHECKING(our pkgconfig version) +test ".$PKGCONFIG_version" != "." || \ +PKGCONFIG_version="${PACKAGE_VERSION}" +test ".$PKGCONFIG_version" != "." || \ +PKGCONFIG_version="$VERSION" +PKGCONFIG_version=`eval echo "$PKGCONFIG_version"` +PKGCONFIG_version=`eval echo "$PKGCONFIG_version"` +AC_MSG_RESULT($PKGCONFIG_version) + +AC_MSG_CHECKING(our pkgconfig_libdir) +test ".$pkgconfig_libdir" = "." && \ +pkgconfig_libdir='${libdir}/pkgconfig' +PKGCONFIG_libdir=`eval echo "$pkgconfig_libdir"` +PKGCONFIG_libdir=`eval echo "$PKGCONFIG_libdir"` +PKGCONFIG_libdir=`eval echo "$PKGCONFIG_libdir"` +AC_MSG_RESULT($pkgconfig_libdir) +test "$pkgconfig_libdir" != "$PKGCONFIG_libdir" && ( +AC_MSG_RESULT(expanded our pkgconfig_libdir... $PKGCONFIG_libdir)) +AC_SUBST([pkgconfig_libdir]) + +AC_MSG_CHECKING(our pkgconfig_libfile) +test ".$pkgconfig_libfile" != "." || \ +pkgconfig_libfile="ifelse($1,,$PKGCONFIG_libname.pc,`basename $1`)" +PKGCONFIG_libfile=`eval echo "$pkgconfig_libfile"` +PKGCONFIG_libfile=`eval echo "$PKGCONFIG_libfile"` +AC_MSG_RESULT($pkgconfig_libfile) +test "$pkgconfig_libfile" != "$PKGCONFIG_libfile" && ( +AC_MSG_RESULT(expanded our pkgconfig_libfile... $PKGCONFIG_libfile)) +AC_SUBST([pkgconfig_libfile]) + +AC_MSG_CHECKING(our package / suffix) +PKGCONFIG_suffix="$program_suffix" +test ".$PKGCONFIG_suffix" != .NONE || PKGCONFIG_suffix="" +AC_MSG_RESULT(${PACKAGE_NAME} / ${PKGCONFIG_suffix}) + +AC_MSG_CHECKING(our pkgconfig description) +PKGCONFIG_description="ifelse($4,,$PACKAGE_SUMMARY,$4)" +test ".$PKGCONFIG_description" != "." || \ +PKGCONFIG_description="$PKGCONFIG_libname Library" +PKGCONFIG_description=`eval echo "$PKGCONFIG_description"` +PKGCONFIG_description=`eval echo "$PKGCONFIG_description"` +AC_MSG_RESULT($PKGCONFIG_description) + +AC_MSG_CHECKING(our pkgconfig requires) +PKGCONFIG_requires="ifelse($2,,$PACKAGE_REQUIRES,$2)" +PKGCONFIG_requires=`eval echo "$PKGCONFIG_requires"` +PKGCONFIG_requires=`eval echo "$PKGCONFIG_requires"` +AC_MSG_RESULT($PKGCONFIG_requires) + +AC_MSG_CHECKING(our pkgconfig ext libs) +PKGCONFIG_pkglibs="$PACKAGE_LIBS" +test ".$PKGCONFIG_pkglibs" != "." || PKGCONFIG_pkglibs="-l$PKGCONFIG_libname" +PKGCONFIG_libs="ifelse($3,,$PKGCONFIG_pkglibs $LIBS,$3)" +PKGCONFIG_libs=`eval echo "$PKGCONFIG_libs"` +PKGCONFIG_libs=`eval echo "$PKGCONFIG_libs"` +AC_MSG_RESULT($PKGCONFIG_libs) + +AC_MSG_CHECKING(our pkgconfig cppflags) +PKGCONFIG_cppflags="ifelse($5,,$PACKAGE_CFLAGS,$5)" +PKGCONFIG_cppflags=`eval echo "$PKGCONFIG_cppflags"` +PKGCONFIG_cppflags=`eval echo "$PKGCONFIG_cppflags"` +AC_MSG_RESULT($PKGCONFIG_cppflags) + +AC_MSG_CHECKING(our pkgconfig ldflags) +PKGCONFIG_ldflags="ifelse($6,,$PACKAGE_LDFLAGS,$5)" +PKGCONFIG_ldflags=`eval echo "$PKGCONFIG_ldflags"` +PKGCONFIG_ldflags=`eval echo "$PKGCONFIG_ldflags"` +AC_MSG_RESULT($PKGCONFIG_ldflags) + +test ".$PKGCONFIG_generate" != "." || \ +PKGCONFIG_generate="ifelse($1,,$PKGCONFIG_libname.pc,$1)" +PKGCONFIG_generate=`eval echo "$PKGCONFIG_generate"` +PKGCONFIG_generate=`eval echo "$PKGCONFIG_generate"` +test "$pkgconfig_libfile" != "$PKGCONFIG_generate" && ( +AC_MSG_RESULT(generate the pkgconfig later... $PKGCONFIG_generate)) + +if test ".$PKGCONFIG_src_libdir" = "." ; then +PKGCONFIG_src_libdir=`pwd` +PKGCONFIG_src_libdir=`AS_DIRNAME("$PKGCONFIG_src_libdir/$PKGCONFIG_generate")` +test ! -d $PKGCONFIG_src_libdir/src || \ +PKGCONFIG_src_libdir="$PKGCONFIG_src_libdir/src" +case ".$objdir" in +*libs) PKGCONFIG_src_libdir="$PKGCONFIG_src_libdir/$objdir" ;; esac +AC_MSG_RESULT(noninstalled pkgconfig -L $PKGCONFIG_src_libdir) +fi + +if test ".$PKGCONFIG_src_headers" = "." ; then +PKGCONFIG_src_headers=`pwd` +v="$ac_top_srcdir" ; +test ".$v" != "." || v="$ax_spec_dir" +test ".$v" != "." || v="$srcdir" +case "$v" in /*) PKGCONFIG_src_headers="" ;; esac +PKGCONFIG_src_headers=`AS_DIRNAME("$PKGCONFIG_src_headers/$v/x")` +test ! -d $PKGCONFIG_src_headers/incl[]ude || \ +PKGCONFIG_src_headers="$PKGCONFIG_src_headers/incl[]ude" +AC_MSG_RESULT(noninstalled pkgconfig -I $PKGCONFIG_src_headers) +fi + + +dnl AC_CONFIG_COMMANDS crap disallows to use $PKGCONFIG_libfile here... +AC_CONFIG_COMMANDS([$ax_create_pkgconfig_generate],[ +pkgconfig_generate="$ax_create_pkgconfig_generate" +if test ! -f "$pkgconfig_generate.in" +then generate="true" +elif grep ' generated by configure ' $pkgconfig_generate.in >/dev/null +then generate="true" +else generate="false"; +fi +if $generate ; then +AC_MSG_NOTICE(creating $pkgconfig_generate.in) +cat > $pkgconfig_generate.in <conftest.sed < $pkgconfig_generate +if test ! -s $pkgconfig_generate ; then + AC_MSG_ERROR([$pkgconfig_generate is empty]) +fi ; rm conftest.sed # DONE generate $pkgconfig_generate +pkgconfig_uninstalled=`echo $pkgconfig_generate |sed 's/.pc$/-uninstalled.pc/'` +AC_MSG_NOTICE(creating $pkgconfig_uninstalled) +cat >conftest.sed < $pkgconfig_uninstalled +if test ! -s $pkgconfig_uninstalled ; then + AC_MSG_ERROR([$pkgconfig_uninstalled is empty]) +fi ; rm conftest.sed # DONE generate $pkgconfig_uninstalled + pkgconfig_requires_add=`echo ${pkgconfig_requires}` +if test ".$pkgconfig_requires_add" != "." ; then + pkgconfig_requires_add="pkg-config $pkgconfig_requires_add" + else pkgconfig_requires_add=":" ; fi +pkgconfig_uninstalled=`echo $pkgconfig_generate |sed 's/.pc$/-uninstalled.sh/'` +AC_MSG_NOTICE(creating $pkgconfig_uninstalled) +cat >conftest.sed <Name:>for option\\; do case \"\$option\" in --list-all|--name) echo > +s>Description: *>\\;\\; --help) pkg-config --help \\; echo Buildscript Of > +s>Version: *>\\;\\; --modversion|--version) echo > +s>Requires:>\\;\\; --requires) echo $pkgconfig_requires_add> +s>Libs: *>\\;\\; --libs) echo > +s>Cflags: *>\\;\\; --cflags) echo > +/--libs)/a\\ + $pkgconfig_requires_add +/--cflags)/a\\ + $pkgconfig_requires_add\\ +;; --variable=*) eval echo '\$'\`echo \$option | sed -e 's/.*=//'\`\\ +;; --uninstalled) exit 0 \\ +;; *) ;; esac done +AXEOF +sed -f conftest.sed $pkgconfig_generate.in > $pkgconfig_uninstalled +if test ! -s $pkgconfig_uninstalled ; then + AC_MSG_ERROR([$pkgconfig_uninstalled is empty]) +fi ; rm conftest.sed # DONE generate $pkgconfig_uninstalled +],[ +dnl AC_CONFIG_COMMANDS crap, the AS_PUSHVAR defines are invalid here... +ax_create_pkgconfig_generate="$ax_create_pkgconfig_generate" +pkgconfig_prefix='$prefix' +pkgconfig_execprefix='$exec_prefix' +pkgconfig_bindir='$bindir' +pkgconfig_libdir='$libdir' +pkgconfig_includedir='$includedir' +pkgconfig_datarootdir='$datarootdir' +pkgconfig_datadir='$datadir' +pkgconfig_sysconfdir='$sysconfdir' +pkgconfig_suffix='$ax_create_pkgconfig_suffix' +pkgconfig_package='$PACKAGE_NAME' +pkgconfig_libname='$ax_create_pkgconfig_libname' +pkgconfig_description='$ax_create_pkgconfig_description' +pkgconfig_version='$ax_create_pkgconfig_version' +pkgconfig_requires='$ax_create_pkgconfig_requires' +pkgconfig_libs='$ax_create_pkgconfig_libs' +pkgconfig_ldflags='$ax_create_pkgconfig_ldflags' +pkgconfig_cppflags='$ax_create_pkgconfig_cppflags' +pkgconfig_src_libdir='$ax_create_pkgconfig_src_libdir' +pkgconfig_src_headers='$ax_create_pkgconfig_src_headers' +])dnl +AS_VAR_POPDEF([PKGCONFIG_suffix])dnl +AS_VAR_POPDEF([PKGCONFIG_libdir])dnl +AS_VAR_POPDEF([PKGCONFIG_libfile])dnl +AS_VAR_POPDEF([PKGCONFIG_libname])dnl +AS_VAR_POPDEF([PKGCONFIG_version])dnl +AS_VAR_POPDEF([PKGCONFIG_description])dnl +AS_VAR_POPDEF([PKGCONFIG_requires])dnl +AS_VAR_POPDEF([PKGCONFIG_pkglibs])dnl +AS_VAR_POPDEF([PKGCONFIG_libs])dnl +AS_VAR_POPDEF([PKGCONFIG_ldflags])dnl +AS_VAR_POPDEF([PKGCONFIG_cppflags])dnl +AS_VAR_POPDEF([PKGCONFIG_generate])dnl +AS_VAR_POPDEF([PKGCONFIG_src_libdir])dnl +AS_VAR_POPDEF([PKGCONFIG_src_headers])dnl +]) Index: lib/Analysis/isl/m4/ax_create_stdint_h.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_create_stdint_h.m4 @@ -0,0 +1,739 @@ +# =========================================================================== +# http://autoconf-archive.cryp.to/ax_create_stdint_h.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of typedefs, +# especially uint8_t,int32_t,uintptr_t. Many older installations will not +# provide this file, but some will have the very same definitions in +# . In other enviroments we can use the inet-types in +# which would define the typedefs int8_t and u_int8_t +# respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given as +# an argument. In many cases that file will just "#include " or +# "#include ", while in other environments it will provide the +# set of basic 'stdint's definitions/typedefs: +# +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t +# +# which may or may not rely on the definitions of other files, or using +# the AC_CHECK_SIZEOF macro to determine the actual sizeof each type. +# +# if your header files require the stdint-types you will want to create an +# installable file mylib-int.h that all your other installable header may +# include. So if you have a library package named "mylib", just use +# +# AX_CREATE_STDINT_H(mylib-int.h) +# +# in configure.ac and go to install that very header file in Makefile.am +# along with the other headers (mylib.h) - and the mylib-specific headers +# can simply use "#include " to obtain the stdint-types. +# +# Remember, if the system already had a valid , the generated +# file will include it directly. No need for fuzzy HAVE_STDINT_H things... +# (oops, GCC 4.2.x has deliberatly disabled its stdint.h for non-c99 +# compilation and the c99-mode is not the default. Therefore this macro +# will not use the compiler's stdint.h - please complain to the GCC +# developers). +# +# LAST MODIFICATION +# +# 2008-04-12 +# +# COPYLEFT +# +# Copyright (c) 2008 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Macro Archive. When you make and +# distribute a modified version of the Autoconf Macro, you may extend this +# special exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_CHECK_DATA_MODEL],[ + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + AC_MSG_CHECKING([data model]) + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)]) +]) + +dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF]) +AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[ +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h sys/types.h]) + do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[ +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h sys/types.h stdint.h]) + do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[ +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +]) + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then +CFLAGS="-std=c99" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[AC_MSG_WARN(your GCC compiler has a defunct stdint.h for its default-mode)]) +fi +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type, +dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW +dnl instead that is triggered with 3 or more arguments (see types.m4) + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AX_CHECK_HEADER_STDINT_X(dnl + stdint.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)") + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AX_CHECK_HEADER_STDINT_O(dnl, + inttypes.h sys/inttypes.h stdint.h $inttype_headers, + ac_cv_stdint_result="(seen uint32_t$and64 in $i)") +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AX_CHECK_HEADER_STDINT_U(dnl, + sys/types.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)") +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AX_CHECK_DATA_MODEL +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +dnl ----------------------------------------------------------------- +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +#ifndef INT64_MIN +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +#endif +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +#ifndef INT64_MAX +# define INT64_MAX (__INT64_C(9223372036854775807)) +#endif + +/* Maximum of unsigned integral types. */ +#ifndef UINT8_MAX +# define UINT8_MAX (255) +#endif +#ifndef UINT16_MAX +# define UINT16_MAX (65535) +#endif +# define UINT32_MAX (4294967295U) +#ifndef UINT64_MAX +# define UINT64_MAX (__UINT64_C(18446744073709551615)) +#endif + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) Index: lib/Analysis/isl/m4/ax_detect_git_head.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_detect_git_head.m4 @@ -0,0 +1,32 @@ +AC_DEFUN([AX_DETECT_GIT_HEAD], [ + AC_SUBST(GIT_HEAD_ID) + AC_SUBST(GIT_HEAD) + AC_SUBST(GIT_HEAD_VERSION) + if test -f $srcdir/.git; then + gitdir=`GIT_DIR=$srcdir/.git git rev-parse --git-dir` + GIT_HEAD="$gitdir/index" + GIT_REPO="$gitdir" + GIT_HEAD_ID=`GIT_DIR=$GIT_REPO git describe --always` + elif test -f $srcdir/.git/HEAD; then + GIT_HEAD="$srcdir/.git/index" + GIT_REPO="$srcdir/.git" + GIT_HEAD_ID=`GIT_DIR=$GIT_REPO git describe --always` + elif test -f $srcdir/GIT_HEAD_ID; then + GIT_HEAD_ID=`cat $srcdir/GIT_HEAD_ID` + else + mysrcdir=`(cd $srcdir; pwd)` + head=`basename $mysrcdir | sed -e 's/.*-//'` + head2=`echo $head | sed -e 's/[^0-9a-f]//'` + head3=`echo $head2 | sed -e 's/........................................//'` + if test "x$head3" = "x" -a "x$head" = "x$head2"; then + GIT_HEAD_ID="$head" + else + GIT_HEAD_ID="UNKNOWN" + fi + fi + if test -z "$GIT_REPO" ; then + GIT_HEAD_VERSION="$GIT_HEAD_ID" + else + GIT_HEAD_VERSION="\`GIT_DIR=$GIT_REPO git describe --always\`" + fi +]) Index: lib/Analysis/isl/m4/ax_detect_gmp.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_detect_gmp.m4 @@ -0,0 +1,48 @@ +AC_DEFUN([AX_DETECT_GMP], [ +AC_DEFINE([USE_GMP_FOR_MP], [], [use gmp to implement isl_int]) +AX_SUBMODULE(gmp,system|build,system) +case "$with_gmp" in +system) + if test "x$with_gmp_prefix" != "x"; then + isl_configure_args="$isl_configure_args --with-gmp=$with_gmp_prefix" + MP_CPPFLAGS="-I$with_gmp_prefix/include" + MP_LDFLAGS="-L$with_gmp_prefix/lib" + fi + MP_LIBS=-lgmp + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + SAVE_LIBS="$LIBS" + CPPFLAGS="$MP_CPPFLAGS $CPPFLAGS" + LDFLAGS="$MP_LDFLAGS $LDFLAGS" + LIBS="$MP_LIBS $LIBS" + AC_CHECK_HEADER([gmp.h], [], [AC_ERROR([gmp.h header not found])]) + AC_CHECK_LIB([gmp], [main], [], [AC_ERROR([gmp library not found])]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ + mpz_t n, d; + if (mpz_divisible_p(n, d)) + mpz_divexact_ui(n, n, 4); + ]])], [], [AC_ERROR([gmp library too old])]) + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" + LIBS="$SAVE_LIBS" + ;; +build) + MP_CPPFLAGS="-I$gmp_srcdir -I$with_gmp_builddir" + MP_LIBS="$with_gmp_builddir/libgmp.la" + ;; +esac +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" +SAVE_LIBS="$LIBS" +CPPFLAGS="$MP_CPPFLAGS $CPPFLAGS" +LDFLAGS="$MP_LDFLAGS $LDFLAGS" +LIBS="$MP_LIBS $LIBS" +need_get_memory_functions=false +AC_CHECK_DECLS(mp_get_memory_functions,[],[ + need_get_memory_functions=true +],[#include ]) +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" +LIBS="$SAVE_LIBS" +AM_CONDITIONAL(NEED_GET_MEMORY_FUNCTIONS, test x$need_get_memory_functions = xtrue) +]) Index: lib/Analysis/isl/m4/ax_detect_imath.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_detect_imath.m4 @@ -0,0 +1,15 @@ +AC_DEFUN([AX_DETECT_IMATH], [ +AC_DEFINE([USE_IMATH_FOR_MP], [], [use imath to implement isl_int]) + +MP_CPPFLAGS="-I$srcdir/imath_wrap" +MP_LDFLAGS="" +MP_LIBS="" + +SAVE_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$MP_CPPFLAGS $CPPFLAGS" +AC_CHECK_HEADER([imath.h], [], [AC_ERROR([imath.h header not found])]) +AC_CHECK_HEADER([gmp_compat.h], [], [AC_ERROR([gmp_compat.h header not found])]) +CPPFLAGS="$SAVE_CPPFLAGS" + +AM_CONDITIONAL(NEED_GET_MEMORY_FUNCTIONS, test x = xfalse) +]) Index: lib/Analysis/isl/m4/ax_gcc_archflag.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_gcc_archflag.m4 @@ -0,0 +1,213 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_gcc_archflag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_ARCHFLAG([PORTABLE?], [ACTION-SUCCESS], [ACTION-FAILURE]) +# +# DESCRIPTION +# +# This macro tries to guess the "native" arch corresponding to the target +# architecture for use with gcc's -march=arch or -mtune=arch flags. If +# found, the cache variable $ax_cv_gcc_archflag is set to this flag and +# ACTION-SUCCESS is executed; otherwise $ax_cv_gcc_archflag is is set to +# "unknown" and ACTION-FAILURE is executed. The default ACTION-SUCCESS is +# to add $ax_cv_gcc_archflag to the end of $CFLAGS. +# +# PORTABLE? should be either [yes] (default) or [no]. In the former case, +# the flag is set to -mtune (or equivalent) so that the architecture is +# only used for tuning, but the instruction set used is still portable. In +# the latter case, the flag is set to -march (or equivalent) so that +# architecture-specific instructions are enabled. +# +# The user can specify --with-gcc-arch= in order to override the +# macro's choice of architecture, or --without-gcc-arch to disable this. +# +# When cross-compiling, or if $CC is not gcc, then ACTION-FAILURE is +# called unless the user specified --with-gcc-arch manually. +# +# Requires macros: AX_CHECK_COMPILER_FLAGS, AX_GCC_X86_CPUID +# +# (The main emphasis here is on recent CPUs, on the principle that doing +# high-performance computing on old hardware is uncommon.) +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_GCC_ARCHFLAG], +[AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_CANONICAL_HOST]) + +AC_ARG_WITH(gcc-arch, [AC_HELP_STRING([--with-gcc-arch=], [use architecture for gcc -march/-mtune, instead of guessing])], + ax_gcc_arch=$withval, ax_gcc_arch=yes) + +AC_MSG_CHECKING([for gcc architecture flag]) +AC_MSG_RESULT([]) +AC_CACHE_VAL(ax_cv_gcc_archflag, +[ +ax_cv_gcc_archflag="unknown" + +if test "$GCC" = yes; then + +if test "x$ax_gcc_arch" = xyes; then +ax_gcc_arch="" +if test "$cross_compiling" = no; then +case $host_cpu in + i[[3456]]86*|x86_64*) # use cpuid codes, in part from x86info-1.7 by D. Jones + AX_GCC_X86_CPUID(0) + AX_GCC_X86_CPUID(1) + case $ax_cv_gcc_x86_cpuid_0 in + *:756e6547:*:*) # Intel + case $ax_cv_gcc_x86_cpuid_1 in + *5[[48]]?:*:*:*) ax_gcc_arch="pentium-mmx pentium" ;; + *5??:*:*:*) ax_gcc_arch=pentium ;; + *6[[3456]]?:*:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; + *6a?:*[[01]]:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; + *6a?:*[[234]]:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; + *6[[9d]]?:*:*:*) ax_gcc_arch="pentium-m pentium3 pentiumpro" ;; + *6[[78b]]?:*:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; + *6??:*:*:*) ax_gcc_arch=pentiumpro ;; + *f3[[347]]:*:*:*|*f4[1347]:*:*:*) + case $host_cpu in + x86_64*) ax_gcc_arch="nocona pentium4 pentiumpro" ;; + *) ax_gcc_arch="prescott pentium4 pentiumpro" ;; + esac ;; + *f??:*:*:*) ax_gcc_arch="pentium4 pentiumpro";; + esac ;; + *:68747541:*:*) # AMD + case $ax_cv_gcc_x86_cpuid_1 in + *5[[67]]?:*:*:*) ax_gcc_arch=k6 ;; + *5[[8d]]?:*:*:*) ax_gcc_arch="k6-2 k6" ;; + *5[[9]]?:*:*:*) ax_gcc_arch="k6-3 k6" ;; + *60?:*:*:*) ax_gcc_arch=k7 ;; + *6[[12]]?:*:*:*) ax_gcc_arch="athlon k7" ;; + *6[[34]]?:*:*:*) ax_gcc_arch="athlon-tbird k7" ;; + *67?:*:*:*) ax_gcc_arch="athlon-4 athlon k7" ;; + *6[[68a]]?:*:*:*) + AX_GCC_X86_CPUID(0x80000006) # L2 cache size + case $ax_cv_gcc_x86_cpuid_0x80000006 in + *:*:*[[1-9a-f]]??????:*) # (L2 = ecx >> 16) >= 256 + ax_gcc_arch="athlon-xp athlon-4 athlon k7" ;; + *) ax_gcc_arch="athlon-4 athlon k7" ;; + esac ;; + *f[[4cef8b]]?:*:*:*) ax_gcc_arch="athlon64 k8" ;; + *f5?:*:*:*) ax_gcc_arch="opteron k8" ;; + *f7?:*:*:*) ax_gcc_arch="athlon-fx opteron k8" ;; + *f??:*:*:*) ax_gcc_arch="k8" ;; + esac ;; + *:746e6543:*:*) # IDT + case $ax_cv_gcc_x86_cpuid_1 in + *54?:*:*:*) ax_gcc_arch=winchip-c6 ;; + *58?:*:*:*) ax_gcc_arch=winchip2 ;; + *6[[78]]?:*:*:*) ax_gcc_arch=c3 ;; + *69?:*:*:*) ax_gcc_arch="c3-2 c3" ;; + esac ;; + esac + if test x"$ax_gcc_arch" = x; then # fallback + case $host_cpu in + i586*) ax_gcc_arch=pentium ;; + i686*) ax_gcc_arch=pentiumpro ;; + esac + fi + ;; + + sparc*) + AC_PATH_PROG([PRTDIAG], [prtdiag], [prtdiag], [$PATH:/usr/platform/`uname -i`/sbin/:/usr/platform/`uname -m`/sbin/]) + cputype=`(((grep cpu /proc/cpuinfo | cut -d: -f2) ; ($PRTDIAG -v |grep -i sparc) ; grep -i cpu /var/run/dmesg.boot ) | head -n 1) 2> /dev/null` + cputype=`echo "$cputype" | tr -d ' -' |tr $as_cr_LETTERS $as_cr_letters` + case $cputype in + *ultrasparciv*) ax_gcc_arch="ultrasparc4 ultrasparc3 ultrasparc v9" ;; + *ultrasparciii*) ax_gcc_arch="ultrasparc3 ultrasparc v9" ;; + *ultrasparc*) ax_gcc_arch="ultrasparc v9" ;; + *supersparc*|*tms390z5[[05]]*) ax_gcc_arch="supersparc v8" ;; + *hypersparc*|*rt62[[056]]*) ax_gcc_arch="hypersparc v8" ;; + *cypress*) ax_gcc_arch=cypress ;; + esac ;; + + alphaev5) ax_gcc_arch=ev5 ;; + alphaev56) ax_gcc_arch=ev56 ;; + alphapca56) ax_gcc_arch="pca56 ev56" ;; + alphapca57) ax_gcc_arch="pca57 pca56 ev56" ;; + alphaev6) ax_gcc_arch=ev6 ;; + alphaev67) ax_gcc_arch=ev67 ;; + alphaev68) ax_gcc_arch="ev68 ev67" ;; + alphaev69) ax_gcc_arch="ev69 ev68 ev67" ;; + alphaev7) ax_gcc_arch="ev7 ev69 ev68 ev67" ;; + alphaev79) ax_gcc_arch="ev79 ev7 ev69 ev68 ev67" ;; + + powerpc*) + cputype=`((grep cpu /proc/cpuinfo | head -n 1 | cut -d: -f2 | cut -d, -f1 | sed 's/ //g') ; /usr/bin/machine ; /bin/machine; grep CPU /var/run/dmesg.boot | head -n 1 | cut -d" " -f2) 2> /dev/null` + cputype=`echo $cputype | sed -e 's/ppc//g;s/ *//g'` + case $cputype in + *750*) ax_gcc_arch="750 G3" ;; + *740[[0-9]]*) ax_gcc_arch="$cputype 7400 G4" ;; + *74[[4-5]][[0-9]]*) ax_gcc_arch="$cputype 7450 G4" ;; + *74[[0-9]][[0-9]]*) ax_gcc_arch="$cputype G4" ;; + *970*) ax_gcc_arch="970 G5 power4";; + *POWER4*|*power4*|*gq*) ax_gcc_arch="power4 970";; + *POWER5*|*power5*|*gr*|*gs*) ax_gcc_arch="power5 power4 970";; + 603ev|8240) ax_gcc_arch="$cputype 603e 603";; + *) ax_gcc_arch=$cputype ;; + esac + ax_gcc_arch="$ax_gcc_arch powerpc" + ;; +esac +fi # not cross-compiling +fi # guess arch + +if test "x$ax_gcc_arch" != x -a "x$ax_gcc_arch" != xno; then +for arch in $ax_gcc_arch; do + if test "x[]m4_default([$1],yes)" = xyes; then # if we require portable code + flags="-mtune=$arch" + # -mcpu=$arch and m$arch generate nonportable code on every arch except + # x86. And some other arches (e.g. Alpha) don't accept -mtune. Grrr. + case $host_cpu in i*86|x86_64*) flags="$flags -mcpu=$arch -m$arch";; esac + else + flags="-march=$arch -mcpu=$arch -m$arch" + fi + for flag in $flags; do + AX_CHECK_COMPILER_FLAGS($flag, [ax_cv_gcc_archflag=$flag; break]) + done + test "x$ax_cv_gcc_archflag" = xunknown || break +done +fi + +fi # $GCC=yes +]) +AC_MSG_CHECKING([for gcc architecture flag]) +AC_MSG_RESULT($ax_cv_gcc_archflag) +if test "x$ax_cv_gcc_archflag" = xunknown; then + m4_default([$3],:) +else + m4_default([$2], [CFLAGS="$CFLAGS $ax_cv_gcc_archflag"]) +fi +]) Index: lib/Analysis/isl/m4/ax_gcc_warn_unused_result.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_gcc_warn_unused_result.m4 @@ -0,0 +1,56 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_gcc_warn_unused_result.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_WARN_UNUSED_RESULT +# +# DESCRIPTION +# +# The macro will compile a test program to see whether the compiler does +# understand the per-function postfix pragma. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_GCC_WARN_UNUSED_RESULT],[dnl +AC_CACHE_CHECK( + [whether the compiler supports function __attribute__((__warn_unused_result__))], + ax_cv_gcc_warn_unused_result,[ + AC_TRY_COMPILE([__attribute__((__warn_unused_result__)) + int f(int i) { return i; }], + [], + ax_cv_gcc_warn_unused_result=yes, ax_cv_gcc_warn_unused_result=no)]) + if test "$ax_cv_gcc_warn_unused_result" = yes; then + AC_DEFINE([GCC_WARN_UNUSED_RESULT],[__attribute__((__warn_unused_result__))], + [most gcc compilers know a function __attribute__((__warn_unused_result__))]) + fi +]) Index: lib/Analysis/isl/m4/ax_gcc_x86_cpuid.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_gcc_x86_cpuid.m4 @@ -0,0 +1,77 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_gcc_x86_cpuid.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_X86_CPUID(OP) +# +# DESCRIPTION +# +# On Pentium and later x86 processors, with gcc or a compiler that has a +# compatible syntax for inline assembly instructions, run a small program +# that executes the cpuid instruction with input OP. This can be used to +# detect the CPU type. +# +# On output, the values of the eax, ebx, ecx, and edx registers are stored +# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable +# ax_cv_gcc_x86_cpuid_OP. +# +# If the cpuid instruction fails (because you are running a +# cross-compiler, or because you are not using gcc, or because you are on +# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP +# is set to the string "unknown". +# +# This macro mainly exists to be used in AX_GCC_ARCHFLAG. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_GCC_X86_CPUID], +[AC_REQUIRE([AC_PROG_CC]) +AC_LANG_PUSH([C]) +AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1, + [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], [ + int op = $1, eax, ebx, ecx, edx; + FILE *f; + __asm__("cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op)); + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; +])], + [ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid], + [ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid], + [ax_cv_gcc_x86_cpuid_$1=unknown])]) +AC_LANG_POP([C]) +]) Index: lib/Analysis/isl/m4/ax_set_warning_flags.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_set_warning_flags.m4 @@ -0,0 +1,17 @@ +dnl Add a set of flags to WARNING_FLAGS, that enable compiler warnings for +dnl isl. The warnings that are enabled vary with the compiler and only include +dnl warnings that did not trigger at the time of adding these flags. +AC_DEFUN([AX_SET_WARNING_FLAGS],[dnl + AX_COMPILER_VENDOR + + WARNING_FLAGS="" + + if test "${ax_cv_c_compiler_vendor}" = "clang"; then + dnl isl is at the moment clean of -Wall warnings. If clang adds + dnl new warnings to -Wall which cause false positives, the + dnl specific warning types will be disabled explicitally (by + dnl adding for example -Wno-return-type). To temporarily disable + dnl all warnings run configure with CFLAGS=-Wno-all. + WARNING_FLAGS="-Wall" + fi +]) Index: lib/Analysis/isl/m4/ax_submodule.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ax_submodule.m4 @@ -0,0 +1,71 @@ +AC_DEFUN([AX_SUBMODULE], +[ + +m4_if(m4_bregexp($2,|,choice),choice, + [AC_ARG_WITH($1, + [AS_HELP_STRING([--with-$1=$2], + [Which $1 to use [default=$3]])])]) +case "system" in +$2) + AC_ARG_WITH($1_prefix, + [AS_HELP_STRING([--with-$1-prefix=DIR], + [Prefix of $1 installation])]) + AC_ARG_WITH($1_exec_prefix, + [AS_HELP_STRING([--with-$1-exec-prefix=DIR], + [Exec prefix of $1 installation])]) +esac +m4_if(m4_bregexp($2,build,build),build, + [AC_ARG_WITH($1_builddir, + [AS_HELP_STRING([--with-$1-builddir=DIR], + [Location of $1 builddir])])]) +if test "x$with_$1_prefix" != "x" -a "x$with_$1_exec_prefix" = "x"; then + with_$1_exec_prefix=$with_$1_prefix +fi +if test "x$with_$1_prefix" != "x" -o "x$with_$1_exec_prefix" != "x"; then + if test "x$with_$1" != "x" -a "x$with_$1" != "xyes" -a "x$with_$1" != "xsystem"; then + AC_MSG_ERROR([Setting $with_$1_prefix implies use of system $1]) + fi + with_$1="system" +fi +if test "x$with_$1_builddir" != "x"; then + if test "x$with_$1" != "x" -a "x$with_$1" != "xyes" -a "x$with_$1" != "xbuild"; then + AC_MSG_ERROR([Setting $with_$1_builddir implies use of build $1]) + fi + with_$1="build" + $1_srcdir=`echo @abs_srcdir@ | $with_$1_builddir/config.status --file=-` + AC_MSG_NOTICE($1 sources in $$1_srcdir) +fi +if test "x$with_$1_exec_prefix" != "x"; then + export PKG_CONFIG_PATH="$with_$1_exec_prefix/lib/pkgconfig${PKG_CONFIG_PATH+:$PKG_CONFIG_PATH}" +fi +case "$with_$1" in +$2) + ;; +*) + case "$3" in + bundled) + if test -d $srcdir/.git -a \ + -d $srcdir/$1 -a \ + ! -d $srcdir/$1/.git; then + AC_MSG_WARN([git repo detected, but submodule $1 not initialized]) + AC_MSG_WARN([You may want to run]) + AC_MSG_WARN([ git submodule init]) + AC_MSG_WARN([ git submodule update]) + AC_MSG_WARN([ sh autogen.sh]) + fi + if test -f $srcdir/$1/configure; then + with_$1="bundled" + else + with_$1="no" + fi + ;; + *) + with_$1="$3" + ;; + esac + ;; +esac +AC_MSG_CHECKING([which $1 to use]) +AC_MSG_RESULT($with_$1) + +]) Index: lib/Analysis/isl/m4/libtool.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/libtool.m4 @@ -0,0 +1,7997 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) + +# serial 57 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to `config.status' so that its +# declaration there will have the same value as in `configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# `#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test $lt_write_fail = 0 && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_REPLACE_SHELLFNS + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script which will find a shell with a builtin +# printf (which we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case "$ECHO" in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[ --with-sysroot[=DIR] Search for dependent libraries within DIR + (or the compiler's sysroot if not specified).], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([${with_sysroot}]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and in which our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS="$save_LDFLAGS"]) + if test "$lt_cv_irix_exported_symbol" = yes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" + CFLAGS="$lt_save_CFLAGS" +fi # test "$_lt_disable_F77" != yes + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test "$_lt_disable_FC" != yes + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) +# ------------------------------------------------------ +# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and +# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. +m4_defun([_LT_PROG_FUNCTION_REPLACE], +[dnl { +sed -e '/^$1 ()$/,/^} # $1 /c\ +$1 ()\ +{\ +m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) +} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: +]) + + +# _LT_PROG_REPLACE_SHELLFNS +# ------------------------- +# Replace existing portable implementations of several shell functions with +# equivalent extended shell implementations where those features are available.. +m4_defun([_LT_PROG_REPLACE_SHELLFNS], +[if test x"$xsi_shell" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl + func_split_long_opt_name=${1%%=*} + func_split_long_opt_arg=${1#*=}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) + + _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) + + _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) + + _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) +fi + +if test x"$lt_shell_append" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) + + _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl + func_quote_for_eval "${2}" +dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ + eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) +fi +]) + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine which file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS Index: lib/Analysis/isl/m4/ltoptions.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ltoptions.m4 @@ -0,0 +1,384 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 7 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option `$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the `shared' and +# `disable-shared' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the `static' and +# `disable-static' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the `fast-install' +# and `disable-fast-install' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# LT_INIT options. +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [pic_mode=default]) + +test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) Index: lib/Analysis/isl/m4/ltsugar.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ltsugar.m4 @@ -0,0 +1,123 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59 which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) Index: lib/Analysis/isl/m4/ltversion.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 3337 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.2]) +m4_define([LT_PACKAGE_REVISION], [1.3337]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.2' +macro_revision='1.3337' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) Index: lib/Analysis/isl/m4/lt~obsolete.m4 =================================================================== --- /dev/null +++ lib/Analysis/isl/m4/lt~obsolete.m4 @@ -0,0 +1,98 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) Index: lib/Analysis/isl/missing =================================================================== --- /dev/null +++ lib/Analysis/isl/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: Index: lib/Analysis/isl/mp_get_memory_functions.c =================================================================== --- /dev/null +++ lib/Analysis/isl/mp_get_memory_functions.c @@ -0,0 +1,14 @@ +#include + +void mp_get_memory_functions( + void *(**alloc_func_ptr) (size_t), + void *(**realloc_func_ptr) (void *, size_t, size_t), + void (**free_func_ptr) (void *, size_t)) +{ + if (alloc_func_ptr) + *alloc_func_ptr = __gmp_allocate_func; + if (realloc_func_ptr) + *realloc_func_ptr = __gmp_reallocate_func; + if (free_func_ptr) + *free_func_ptr = __gmp_free_func; +} Index: lib/Analysis/isl/pip.c =================================================================== --- /dev/null +++ lib/Analysis/isl/pip.c @@ -0,0 +1,382 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include +#include "isl_tab.h" +#include "isl_sample.h" +#include "isl_scan.h" +#include +#include +#include +#include +#include +#include +#include + +/* The input of this program is the same as that of the "example" program + * from the PipLib distribution, except that the "big parameter column" + * should always be -1. + * + * Context constraints in PolyLib format + * -1 + * Problem constraints in PolyLib format + * Optional list of options + * + * The options are + * Maximize compute maximum instead of minimum + * Rational compute rational optimum instead of integer optimum + * Urs_parms don't assume parameters are non-negative + * Urs_unknowns don't assume unknowns are non-negative + */ + +struct options { + struct isl_options *isl; + unsigned verify; + unsigned format; +}; + +#define FORMAT_SET 0 +#define FORMAT_AFF 1 + +struct isl_arg_choice pip_format[] = { + {"set", FORMAT_SET}, + {"affine", FORMAT_AFF}, + {0} +}; + +ISL_ARGS_START(struct options, options_args) +ISL_ARG_CHILD(struct options, isl, "isl", &isl_options_args, "isl options") +ISL_ARG_BOOL(struct options, verify, 'T', "verify", 0, NULL) +ISL_ARG_CHOICE(struct options, format, 0, "format", + pip_format, FORMAT_SET, "output format") +ISL_ARGS_END + +ISL_ARG_DEF(options, struct options, options_args) + +static __isl_give isl_basic_set *set_bounds(__isl_take isl_basic_set *bset) +{ + unsigned nparam; + int i, r; + isl_point *pt, *pt2; + isl_basic_set *box; + + nparam = isl_basic_set_dim(bset, isl_dim_param); + r = nparam >= 8 ? 4 : nparam >= 5 ? 6 : 30; + + pt = isl_basic_set_sample_point(isl_basic_set_copy(bset)); + pt2 = isl_point_copy(pt); + + for (i = 0; i < nparam; ++i) { + pt = isl_point_add_ui(pt, isl_dim_param, i, r); + pt2 = isl_point_sub_ui(pt2, isl_dim_param, i, r); + } + + box = isl_basic_set_box_from_points(pt, pt2); + + return isl_basic_set_intersect(bset, box); +} + +static struct isl_basic_set *to_parameter_domain(struct isl_basic_set *context) +{ + context = isl_basic_set_move_dims(context, isl_dim_param, 0, + isl_dim_set, 0, isl_basic_set_dim(context, isl_dim_set)); + context = isl_basic_set_params(context); + return context; +} + +isl_basic_set *plug_in_parameters(isl_basic_set *bset, struct isl_vec *params) +{ + int i; + + for (i = 0; i < params->size - 1; ++i) + bset = isl_basic_set_fix(bset, + isl_dim_param, i, params->el[1 + i]); + + bset = isl_basic_set_remove_dims(bset, + isl_dim_param, 0, params->size - 1); + + isl_vec_free(params); + + return bset; +} + +isl_set *set_plug_in_parameters(isl_set *set, struct isl_vec *params) +{ + int i; + + for (i = 0; i < params->size - 1; ++i) + set = isl_set_fix(set, isl_dim_param, i, params->el[1 + i]); + + set = isl_set_remove_dims(set, isl_dim_param, 0, params->size - 1); + + isl_vec_free(params); + + return set; +} + +/* Compute the lexicographically minimal (or maximal if max is set) + * element of bset for the given values of the parameters, by + * successively solving an ilp problem in each direction. + */ +struct isl_vec *opt_at(struct isl_basic_set *bset, + struct isl_vec *params, int max) +{ + unsigned dim; + struct isl_vec *opt; + struct isl_vec *obj; + int i; + + dim = isl_basic_set_dim(bset, isl_dim_set); + + bset = plug_in_parameters(bset, params); + + if (isl_basic_set_plain_is_empty(bset)) { + opt = isl_vec_alloc(bset->ctx, 0); + isl_basic_set_free(bset); + return opt; + } + + opt = isl_vec_alloc(bset->ctx, 1 + dim); + assert(opt); + + obj = isl_vec_alloc(bset->ctx, 1 + dim); + assert(obj); + + isl_int_set_si(opt->el[0], 1); + isl_int_set_si(obj->el[0], 0); + + for (i = 0; i < dim; ++i) { + enum isl_lp_result res; + + isl_seq_clr(obj->el + 1, dim); + isl_int_set_si(obj->el[1 + i], 1); + res = isl_basic_set_solve_ilp(bset, max, obj->el, + &opt->el[1 + i], NULL); + if (res == isl_lp_empty) + goto empty; + assert(res == isl_lp_ok); + bset = isl_basic_set_fix(bset, isl_dim_set, i, opt->el[1 + i]); + } + + isl_basic_set_free(bset); + isl_vec_free(obj); + + return opt; +empty: + isl_vec_free(opt); + opt = isl_vec_alloc(bset->ctx, 0); + isl_basic_set_free(bset); + isl_vec_free(obj); + + return opt; +} + +struct isl_scan_pip { + struct isl_scan_callback callback; + isl_basic_set *bset; + isl_set *sol; + isl_set *empty; + int stride; + int n; + int max; +}; + +/* Check if the "manually" computed optimum of bset at the "sample" + * values of the parameters agrees with the solution of pilp problem + * represented by the pair (sol, empty). + * In particular, if there is no solution for this value of the parameters, + * then it should be an element of the parameter domain "empty". + * Otherwise, the optimal solution, should be equal to the result of + * plugging in the value of the parameters in "sol". + */ +static isl_stat scan_one(struct isl_scan_callback *callback, + __isl_take isl_vec *sample) +{ + struct isl_scan_pip *sp = (struct isl_scan_pip *)callback; + struct isl_vec *opt; + + sp->n--; + + opt = opt_at(isl_basic_set_copy(sp->bset), isl_vec_copy(sample), sp->max); + assert(opt); + + if (opt->size == 0) { + isl_point *sample_pnt; + sample_pnt = isl_point_alloc(isl_set_get_space(sp->empty), sample); + assert(isl_set_contains_point(sp->empty, sample_pnt)); + isl_point_free(sample_pnt); + isl_vec_free(opt); + } else { + isl_set *sol; + isl_set *opt_set; + opt_set = isl_set_from_basic_set(isl_basic_set_from_vec(opt)); + sol = set_plug_in_parameters(isl_set_copy(sp->sol), sample); + assert(isl_set_is_equal(opt_set, sol)); + isl_set_free(sol); + isl_set_free(opt_set); + } + + if (!(sp->n % sp->stride)) { + printf("o"); + fflush(stdout); + } + + return sp->n >= 1 ? isl_stat_ok : isl_stat_error; +} + +static void check_solution(isl_basic_set *bset, isl_basic_set *context, + isl_set *sol, isl_set *empty, int max) +{ + struct isl_scan_pip sp; + isl_int count, count_max; + int i, n; + int r; + + context = set_bounds(context); + context = isl_basic_set_underlying_set(context); + + isl_int_init(count); + isl_int_init(count_max); + + isl_int_set_si(count_max, 2000); + r = isl_basic_set_count_upto(context, count_max, &count); + assert(r >= 0); + n = isl_int_get_si(count); + + isl_int_clear(count_max); + isl_int_clear(count); + + sp.callback.add = scan_one; + sp.bset = bset; + sp.sol = sol; + sp.empty = empty; + sp.n = n; + sp.stride = n > 70 ? 1 + (n + 1)/70 : 1; + sp.max = max; + + for (i = 0; i < n; i += sp.stride) + printf("."); + printf("\r"); + fflush(stdout); + + isl_basic_set_scan(context, &sp.callback); + + printf("\n"); + + isl_basic_set_free(bset); +} + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx; + struct isl_basic_set *context, *bset, *copy, *context_copy; + struct isl_set *set = NULL; + struct isl_set *empty; + isl_pw_multi_aff *pma = NULL; + int neg_one; + char s[1024]; + int urs_parms = 0; + int urs_unknowns = 0; + int max = 0; + int rational = 0; + int n; + int nparam; + struct options *options; + + options = options_new_with_defaults(); + assert(options); + argc = options_parse(options, argc, argv, ISL_ARG_ALL); + + ctx = isl_ctx_alloc_with_options(&options_args, options); + + context = isl_basic_set_read_from_file(ctx, stdin); + assert(context); + n = fscanf(stdin, "%d", &neg_one); + assert(n == 1); + assert(neg_one == -1); + bset = isl_basic_set_read_from_file(ctx, stdin); + + while (fgets(s, sizeof(s), stdin)) { + if (strncasecmp(s, "Maximize", 8) == 0) + max = 1; + if (strncasecmp(s, "Rational", 8) == 0) { + rational = 1; + bset = isl_basic_set_set_rational(bset); + } + if (strncasecmp(s, "Urs_parms", 9) == 0) + urs_parms = 1; + if (strncasecmp(s, "Urs_unknowns", 12) == 0) + urs_unknowns = 1; + } + if (!urs_parms) + context = isl_basic_set_intersect(context, + isl_basic_set_positive_orthant(isl_basic_set_get_space(context))); + context = to_parameter_domain(context); + nparam = isl_basic_set_dim(context, isl_dim_param); + if (nparam != isl_basic_set_dim(bset, isl_dim_param)) { + int dim = isl_basic_set_dim(bset, isl_dim_set); + bset = isl_basic_set_move_dims(bset, isl_dim_param, 0, + isl_dim_set, dim - nparam, nparam); + } + if (!urs_unknowns) + bset = isl_basic_set_intersect(bset, + isl_basic_set_positive_orthant(isl_basic_set_get_space(bset))); + + if (options->verify) { + copy = isl_basic_set_copy(bset); + context_copy = isl_basic_set_copy(context); + } + + if (options->format == FORMAT_AFF) { + if (max) + pma = isl_basic_set_partial_lexmax_pw_multi_aff(bset, + context, &empty); + else + pma = isl_basic_set_partial_lexmin_pw_multi_aff(bset, + context, &empty); + } else { + if (max) + set = isl_basic_set_partial_lexmax(bset, + context, &empty); + else + set = isl_basic_set_partial_lexmin(bset, + context, &empty); + } + + if (options->verify) { + assert(!rational); + if (options->format == FORMAT_AFF) + set = isl_set_from_pw_multi_aff(pma); + check_solution(copy, context_copy, set, empty, max); + isl_set_free(set); + } else { + isl_printer *p; + p = isl_printer_to_file(ctx, stdout); + if (options->format == FORMAT_AFF) + p = isl_printer_print_pw_multi_aff(p, pma); + else + p = isl_printer_print_set(p, set); + p = isl_printer_end_line(p); + p = isl_printer_print_str(p, "no solution: "); + p = isl_printer_print_set(p, empty); + p = isl_printer_end_line(p); + isl_printer_free(p); + isl_set_free(set); + isl_pw_multi_aff_free(pma); + } + + isl_set_free(empty); + isl_ctx_free(ctx); + + return 0; +} Index: lib/Analysis/isl/pip_test.sh.in =================================================================== --- /dev/null +++ lib/Analysis/isl/pip_test.sh.in @@ -0,0 +1,28 @@ +#!/bin/sh + +EXEEXT=@EXEEXT@ + +PIP_TESTS="\ + boulet.pip \ + brisebarre.pip \ + cg1.pip \ + esced.pip \ + ex2.pip \ + ex.pip \ + fimmel.pip \ + max.pip \ + negative.pip \ + seghir-vd.pip \ + small.pip \ + sor1d.pip \ + square.pip \ + sven.pip \ + tobi.pip" + +for i in $PIP_TESTS; do + echo $i; + ./isl_pip$EXEEXT --format=set --context=gbr -T < $srcdir/test_inputs/$i || exit + ./isl_pip$EXEEXT --format=set --context=lexmin -T < $srcdir/test_inputs/$i || exit + ./isl_pip$EXEEXT --format=affine --context=gbr -T < $srcdir/test_inputs/$i || exit + ./isl_pip$EXEEXT --format=affine --context=lexmin -T < $srcdir/test_inputs/$i || exit +done Index: lib/Analysis/isl/polyhedron_detect_equalities.c =================================================================== --- /dev/null +++ lib/Analysis/isl/polyhedron_detect_equalities.c @@ -0,0 +1,30 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx = isl_ctx_alloc(); + struct isl_basic_set *bset; + isl_printer *p; + + bset = isl_basic_set_read_from_file(ctx, stdin); + bset = isl_basic_set_detect_equalities(bset); + + p = isl_printer_to_file(ctx, stdout); + p = isl_printer_set_output_format(p, ISL_FORMAT_POLYLIB); + p = isl_printer_print_basic_set(p, bset); + isl_printer_free(p); + + isl_basic_set_free(bset); + isl_ctx_free(ctx); + + return 0; +} Index: lib/Analysis/isl/polyhedron_minimize.c =================================================================== --- /dev/null +++ lib/Analysis/isl/polyhedron_minimize.c @@ -0,0 +1,105 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include +#include +#include +#include + +/* The input of this program is the same as that of the "polytope_minimize" + * program from the barvinok distribution. + * + * Constraints of set is PolyLib format. + * Linear or affine objective function in PolyLib format. + */ + +static struct isl_vec *isl_vec_lin_to_aff(struct isl_vec *vec) +{ + struct isl_vec *aff; + + if (!vec) + return NULL; + aff = isl_vec_alloc(vec->ctx, 1 + vec->size); + if (!aff) + goto error; + isl_int_set_si(aff->el[0], 0); + isl_seq_cpy(aff->el + 1, vec->el, vec->size); + isl_vec_free(vec); + return aff; +error: + isl_vec_free(vec); + return NULL; +} + +/* Rotate elements of vector right. + * In particular, move the constant term from the end of the + * vector to the start of the vector. + */ +static struct isl_vec *vec_ror(struct isl_vec *vec) +{ + int i; + + if (!vec) + return NULL; + for (i = vec->size - 2; i >= 0; --i) + isl_int_swap(vec->el[i], vec->el[i + 1]); + return vec; +} + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx = isl_ctx_alloc(); + struct isl_basic_set *bset; + struct isl_vec *obj; + struct isl_vec *sol; + isl_int opt; + unsigned dim; + enum isl_lp_result res; + isl_printer *p; + + isl_int_init(opt); + bset = isl_basic_set_read_from_file(ctx, stdin); + assert(bset); + obj = isl_vec_read_from_file(ctx, stdin); + assert(obj); + dim = isl_basic_set_total_dim(bset); + assert(obj->size >= dim && obj->size <= dim + 1); + if (obj->size != dim + 1) + obj = isl_vec_lin_to_aff(obj); + else + obj = vec_ror(obj); + res = isl_basic_set_solve_ilp(bset, 0, obj->el, &opt, &sol); + switch (res) { + case isl_lp_error: + fprintf(stderr, "error\n"); + return -1; + case isl_lp_empty: + fprintf(stdout, "empty\n"); + break; + case isl_lp_unbounded: + fprintf(stdout, "unbounded\n"); + break; + case isl_lp_ok: + p = isl_printer_to_file(ctx, stdout); + p = isl_printer_print_vec(p, sol); + p = isl_printer_end_line(p); + p = isl_printer_print_isl_int(p, opt); + p = isl_printer_end_line(p); + isl_printer_free(p); + } + isl_basic_set_free(bset); + isl_vec_free(obj); + isl_vec_free(sol); + isl_ctx_free(ctx); + isl_int_clear(opt); + + return 0; +} Index: lib/Analysis/isl/polyhedron_sample.c =================================================================== --- /dev/null +++ lib/Analysis/isl/polyhedron_sample.c @@ -0,0 +1,36 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include "isl_sample.h" +#include + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx = isl_ctx_alloc(); + struct isl_basic_set *bset; + struct isl_vec *sample; + isl_printer *p; + + bset = isl_basic_set_read_from_file(ctx, stdin); + sample = isl_basic_set_sample_vec(isl_basic_set_copy(bset)); + p = isl_printer_to_file(ctx, stdout); + p = isl_printer_print_vec(p, sample); + p = isl_printer_end_line(p); + isl_printer_free(p); + assert(sample); + if (isl_vec_size(sample) > 0) + assert(isl_basic_set_contains(bset, sample)); + isl_basic_set_free(bset); + isl_vec_free(sample); + isl_ctx_free(ctx); + + return 0; +} Index: lib/Analysis/isl/polytope_scan.c =================================================================== --- /dev/null +++ lib/Analysis/isl/polytope_scan.c @@ -0,0 +1,104 @@ +/* + * Copyright 2008-2009 Katholieke Universiteit Leuven + * + * Use of this software is governed by the MIT license + * + * Written by Sven Verdoolaege, K.U.Leuven, Departement + * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium + */ + +#include +#include +#include "isl_equalities.h" +#include +#include "isl_scan.h" +#include +#include + +/* The input of this program is the same as that of the "polytope_scan" + * program from the barvinok distribution. + * + * Constraints of set is PolyLib format. + * + * The input set is assumed to be bounded. + */ + +struct scan_samples { + struct isl_scan_callback callback; + struct isl_mat *samples; +}; + +static isl_stat scan_samples_add_sample(struct isl_scan_callback *cb, + __isl_take isl_vec *sample) +{ + struct scan_samples *ss = (struct scan_samples *)cb; + + ss->samples = isl_mat_extend(ss->samples, ss->samples->n_row + 1, + ss->samples->n_col); + if (!ss->samples) + goto error; + + isl_seq_cpy(ss->samples->row[ss->samples->n_row - 1], + sample->el, sample->size); + + isl_vec_free(sample); + return isl_stat_ok; +error: + isl_vec_free(sample); + return isl_stat_error; +} + +static struct isl_mat *isl_basic_set_scan_samples(struct isl_basic_set *bset) +{ + isl_ctx *ctx; + unsigned dim; + struct scan_samples ss; + + ctx = isl_basic_set_get_ctx(bset); + dim = isl_basic_set_total_dim(bset); + ss.callback.add = scan_samples_add_sample; + ss.samples = isl_mat_alloc(ctx, 0, 1 + dim); + if (!ss.samples) + goto error; + + if (isl_basic_set_scan(bset, &ss.callback) < 0) { + isl_mat_free(ss.samples); + return NULL; + } + + return ss.samples; +error: + isl_basic_set_free(bset); + return NULL; +} + +static struct isl_mat *isl_basic_set_samples(struct isl_basic_set *bset) +{ + struct isl_mat *T; + struct isl_mat *samples; + + if (!bset) + return NULL; + + if (bset->n_eq == 0) + return isl_basic_set_scan_samples(bset); + + bset = isl_basic_set_remove_equalities(bset, &T, NULL); + samples = isl_basic_set_scan_samples(bset); + return isl_mat_product(samples, isl_mat_transpose(T)); +} + +int main(int argc, char **argv) +{ + struct isl_ctx *ctx = isl_ctx_alloc(); + struct isl_basic_set *bset; + struct isl_mat *samples; + + bset = isl_basic_set_read_from_file(ctx, stdin); + samples = isl_basic_set_samples(bset); + isl_mat_print_internal(samples, stdout, 0); + isl_mat_free(samples); + isl_ctx_free(ctx); + + return 0; +} Index: lib/Analysis/isl/print.c =================================================================== --- /dev/null +++ lib/Analysis/isl/print.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef BASE +#define BASE id +#include +#undef BASE +#define BASE val +#include +#undef BASE +#define BASE multi_val +#include +#undef BASE +#define BASE space +#include +#undef BASE +#define BASE local_space +#include +#undef BASE +#define BASE basic_set +#include +#undef BASE +#define BASE basic_map +#include +#undef BASE +#define BASE set +#include +#undef BASE +#define BASE map +#include +#undef BASE +#define BASE union_set +#include +#undef BASE +#define BASE union_map +#include +#undef BASE +#define BASE qpolynomial +#include +#undef BASE +#define BASE qpolynomial_fold +#include +#undef BASE +#define BASE pw_qpolynomial +#include +#undef BASE +#define BASE pw_qpolynomial_fold +#include +#undef BASE +#define BASE union_pw_qpolynomial +#include +#undef BASE +#define BASE union_pw_qpolynomial_fold +#include +#undef BASE +#define BASE band +#include +#undef BASE +#define BASE constraint +#include +#undef BASE +#define BASE aff +#include +#undef BASE +#define BASE pw_aff +#include +#undef BASE +#define BASE multi_aff +#include +#undef BASE +#define BASE pw_multi_aff +#include +#undef BASE +#define BASE union_pw_multi_aff +#include +#undef BASE +#define BASE multi_pw_aff +#include +#undef BASE +#define BASE union_pw_aff +#include +#undef BASE +#define BASE multi_union_pw_aff +#include +#undef BASE +#define BASE point +#include +#undef BASE +#define BASE ast_expr +#include +#undef BASE +#define BASE ast_node +#include Index: lib/Analysis/isl/print_templ.c =================================================================== --- /dev/null +++ lib/Analysis/isl/print_templ.c @@ -0,0 +1,36 @@ +#include + +#define xCAT(A,B) A ## B +#define CAT(A,B) xCAT(A,B) +#undef TYPE +#define TYPE CAT(isl_,BASE) +#define xFN(TYPE,NAME) TYPE ## _ ## NAME +#define FN(TYPE,NAME) xFN(TYPE,NAME) + +void FN(TYPE,dump)(__isl_keep TYPE *obj) +{ + isl_printer *p; + + if (!obj) + return; + p = isl_printer_to_file(FN(TYPE,get_ctx)(obj), stderr); + p = isl_printer_set_dump(p, 1); + p = FN(isl_printer_print,BASE)(p, obj); + p = isl_printer_end_line(p); + isl_printer_free(p); +} + +__isl_give char *FN(TYPE,to_str)(__isl_keep TYPE *obj) +{ + isl_printer *p; + char *s; + + if (!obj) + return NULL; + p = isl_printer_to_str(FN(TYPE,get_ctx)(obj)); + p = FN(isl_printer_print,BASE)(p, obj); + s = isl_printer_get_str(p); + isl_printer_free(p); + + return s; +} Index: test/Analysis/PolyhedralInfo/conditionals_simple.ll =================================================================== --- /dev/null +++ test/Analysis/PolyhedralInfo/conditionals_simple.ll @@ -0,0 +1,750 @@ +; RUN: opt -polyhedral-value-info -analyze < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; void conditional0(int *A) { +; for (int i = 0; i < 1000; i++) +; if (i > 50) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +; +define void @conditional0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, 50 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional1(int *A) { +; for (int i = 0; i < 1000; i++) { +; if (i > 50) +; A[i]++; +; if (i > 50) +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then3] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end7] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, 50 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + %cmp2 = icmp sgt i64 %indvars.iv, 50 + br i1 %cmp2, label %if.then3, label %if.end7 + +if.then3: ; preds = %if.end + %arrayidx5 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx5, align 4 + %inc6 = add nsw i32 %tmp1, 1 + store i32 %inc6, i32* %arrayidx5, align 4 + br label %if.end7 + +if.end7: ; preds = %if.then3, %if.end + br label %for.inc + +for.inc: ; preds = %if.end7 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional2(int *A) { +; for (int i = 0; i < 1000; i++) { +; if (i > 50) +; A[i]++; +; else +; A[i]--; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 50 } [if.else] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, 50 + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %for.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.else: ; preds = %for.body + %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx3, align 4 + %dec = add nsw i32 %tmp1, -1 + store i32 %dec, i32* %arrayidx3, align 4 + br label %if.end + +if.end: ; preds = %if.else, %if.then + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional3(int *A) { +; for (int i = 0; i < 1000; i++) { +; if (i > 50) +; A[i]++; +; if (i < 20) +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 19 } [if.then3] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end7] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, 50 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + %cmp2 = icmp slt i64 %indvars.iv, 20 + br i1 %cmp2, label %if.then3, label %if.end7 + +if.then3: ; preds = %if.end + %arrayidx5 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx5, align 4 + %inc6 = add nsw i32 %tmp1, 1 + store i32 %inc6, i32* %arrayidx5, align 4 + br label %if.end7 + +if.end7: ; preds = %if.then3, %if.end + br label %for.inc + +for.inc: ; preds = %if.end7 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional4(int *A) { +; for (int i = 0; i < 1000; i++) { +; if (i > 50) +; if (i < 80) +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 79 } [if.then3] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end4] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional4(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, 50 + br i1 %cmp1, label %if.then, label %if.end4 + +if.then: ; preds = %for.body + %cmp2 = icmp slt i64 %indvars.iv, 80 + br i1 %cmp2, label %if.then3, label %if.end + +if.then3: ; preds = %if.then + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.then3, %if.then + br label %if.end4 + +if.end4: ; preds = %if.end, %for.body + br label %for.inc + +for.inc: ; preds = %if.end4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional5(int *A) { +; for (int i = 0; i < 1000; i++) { +; if (i > 50) +; if (i < 50) +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.then] +; CHECK: { } [if.then3] +; CHECK: { [i0] -> [(1)] : 51 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end4] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional5(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ 0, %entry ], [ %inc5, %for.inc ] + %exitcond = icmp ne i32 %i.0, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i32 %i.0, 50 + br i1 %cmp1, label %if.then, label %if.end4 + +if.then: ; preds = %for.body + %cmp2 = icmp slt i32 %i.0, 50 + br i1 %cmp2, label %if.then3, label %if.end + +if.then3: ; preds = %if.then + br label %if.end + +if.end: ; preds = %if.then3, %if.then + br label %if.end4 + +if.end4: ; preds = %if.end, %for.body + br label %for.inc + +for.inc: ; preds = %if.end4 + %inc5 = add nuw nsw i32 %i.0, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional6(int *A, int P) { +; for (int i = 0; i < 1000; i++) { +; if (i > P) +;Con: A[i]++; +; else /* (i < P) */ +;Alt: A[i]--; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: [P] -> { [i0] -> [(1)] : i0 > P and 0 <= i0 <= 999 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : i0 > P and 0 <= i0 <= 999 } [Con] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and i0 <= P } [if.else] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and i0 <= P } [Alt] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional6(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %for.body + br label %Con + +Con: ; preds = %if.then + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.else: ; preds = %for.body + br label %Alt + +Alt: ; preds = %if.else + %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx3, align 4 + %dec = add nsw i32 %tmp2, -1 + store i32 %dec, i32* %arrayidx3, align 4 + br label %if.end + +if.end: ; preds = %Alt, %Con + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional7(int *A, int P) { +; for (int i = 0; i < 1000; i++) { +; if (i > P) +;Con: A[i]++; +; if (i < P) +;Alt: A[i]--; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: [P] -> { [i0] -> [(1)] : i0 > P and 0 <= i0 <= 999 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : i0 > P and 0 <= i0 <= 999 } [Con] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and i0 < P } [if.then3] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and i0 < P } [Alt] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end6] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional7(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + %tmp1 = sext i32 %P to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + br label %Con + +Con: ; preds = %if.then + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp2, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %Con, %for.body + %cmp2 = icmp slt i64 %indvars.iv, %tmp1 + br i1 %cmp2, label %if.then3, label %if.end6 + +if.then3: ; preds = %if.end + br label %Alt + +Alt: ; preds = %if.then3 + %arrayidx5 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp3 = load i32, i32* %arrayidx5, align 4 + %dec = add nsw i32 %tmp3, -1 + store i32 %dec, i32* %arrayidx5, align 4 + br label %if.end6 + +if.end6: ; preds = %Alt, %if.end + br label %for.inc + +for.inc: ; preds = %if.end6 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional8(int *A, int P) { +; for (int i = 0; i < 1000; i++) { +; if (i > P) +; A[i]++; +; else +; A[i]--; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] +; CHECK: [P] -> { [i0] -> [(1)] : i0 > P and 0 <= i0 <= 999 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and i0 <= P } [if.else] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional8(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %cmp1 = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp1, label %if.then, label %if.else + +if.then: ; preds = %for.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %if.end + +if.else: ; preds = %for.body + %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx3, align 4 + %dec = add nsw i32 %tmp2, -1 + store i32 %dec, i32* %arrayidx3, align 4 + br label %if.end + +if.end: ; preds = %if.else, %if.then + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional9(int *A, int P) { +; if (P) +; for (int i = 0; i < 1000; i++) { +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [] -> [(1)] : P < 0 or P > 0 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 1000 and (P < 0 or P > 0) } [for.cond] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and (P < 0 or P > 0) } [for.body] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and (P < 0 or P > 0) } [for.inc] +; CHECK: [P] -> { [] -> [(1)] : P < 0 or P > 0 } [for.end] +; CHECK: { [] -> [(1)] } [if.end] +define void @conditional9(i32* %A, i32 %P) { +entry: + %tobool = icmp eq i32 %P, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %for.cond + +for.cond: ; preds = %for.inc, %if.then + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %if.then ] + %exitcond = icmp ne i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + br label %if.end + +if.end: ; preds = %entry, %for.end + ret void +} + +; void conditional10(int *A, int P) { +; for (int i = 0; P && i < 1000; i++) { +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (P < 0 and 0 <= i0 <= 1000) or (P > 0 and 0 <= i0 <= 1000) or i0 = 0 } [for.cond] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 1000 and (P < 0 or P > 0) } [land.rhs] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and (P < 0 or P > 0) } [for.body] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 999 and (P < 0 or P > 0) } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @conditional10(i32* %A, i32 %P) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %tobool = icmp eq i32 %P, 0 + br i1 %tobool, label %land.end, label %land.rhs + +land.rhs: ; preds = %for.cond + %cmp = icmp slt i64 %indvars.iv, 1000 + br label %land.end + +land.end: ; preds = %for.cond, %land.rhs + %tmp = phi i1 [ false, %for.cond ], [ %cmp, %land.rhs ] + br i1 %tmp, label %for.body, label %for.end + +for.body: ; preds = %land.end + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %land.end + ret void +} + +; void conditional11(int *A, int P) { +; for (int i = 0; P & (i < 1000); i++) { +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [and] -> { [i0] -> [(1)] : (and < 0 and i0 >= 0) or (and > 0 and i0 >= 0) or i0 = 0 } [for.cond] +; CHECK: [and] -> { [i0] -> [(1)] : i0 >= 0 and (and < 0 or and > 0) } [for.body] +; CHECK: [and] -> { [i0] -> [(1)] : i0 >= 0 and (and < 0 or and > 0) } [for.inc] +; CHECK: [and] -> { [] -> [(1)] : and = 0 } [for.end] +define void @conditional11(i32* %A, i32 %P) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 1000 + %conv = zext i1 %cmp to i32 + %and = and i32 %conv, %P + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %for.end, label %for.body + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void conditional12(int *A) { +; int i = 0; +; do { +; if (i > 4) +; A[i] = i; +; } while (i++ < 10); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [do.body] +; CHECK: { [i0] -> [(1)] : 5 <= i0 <= 10 } [if.then] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [if.end] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [do.cond] +; CHECK: { [] -> [(1)] } [do.end] +define void @conditional12(i32* %A) { +entry: + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %cmp = icmp sgt i64 %indvars.iv, 4 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %do.body + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = trunc i64 %indvars.iv to i32 + store i32 %tmp, i32* %arrayidx, align 4 + br label %if.end + +if.end: ; preds = %if.then, %do.body + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp ne i64 %indvars.iv.next, 11 + br i1 %exitcond, label %do.body, label %do.end + +do.end: ; preds = %do.cond + ret void +} + +; void conditional13(int *A, int P, int N) { +; int i = 0; +; do { +; A[i] = 0; +; if (i > P) +; A[i] += i; +; else +; A[i] -= i; +; A[i] *= 2; +; } while (i++ < N); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 or (0 <= i0 <= N) } [do.body] +; CHECK: { [i0] -> [(1)] : (i0 > P and 0 <= i0 <= N) or (i0 = 0 and P < 0) } [if.then] +; CHECK: { [i0] -> [(1)] : (0 <= i0 <= N and i0 <= P) or (i0 = 0 and P >= 0) } [if.else] +; CHECK: { [i0] -> [(1)] : (0 <= i0 <= N) or i0 = 0 } [if.end] +; CHECK: { [i0] -> [(1)] : (0 <= i0 <= N) or i0 = 0 } [do.cond] +; CHECK: { [] -> [(1)] } [do.end] +define void @conditional13(i32* %A, i32 %P, i32 %N) { +entry: + %tmp = sext i32 %P to i64 + %tmp1 = sext i32 %N to i64 + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + store i32 0, i32* %arrayidx, align 4 + %cmp = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp, label %if.then, label %if.else + +if.then: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx2, align 4 + %tmp3 = trunc i64 %indvars.iv to i32 + %add = add nsw i32 %tmp2, %tmp3 + store i32 %add, i32* %arrayidx2, align 4 + br label %if.end + +if.else: ; preds = %do.body + %arrayidx4 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp4 = load i32, i32* %arrayidx4, align 4 + %tmp5 = trunc i64 %indvars.iv to i32 + %sub = sub nsw i32 %tmp4, %tmp5 + store i32 %sub, i32* %arrayidx4, align 4 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %arrayidx6 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp6 = load i32, i32* %arrayidx6, align 4 + %mul = mul nsw i32 %tmp6, 2 + store i32 %mul, i32* %arrayidx6, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp7 = icmp slt i64 %indvars.iv, %tmp1 + br i1 %cmp7, label %do.body, label %do.end + +do.end: ; preds = %do.cond + ret void +} Index: test/Analysis/PolyhedralInfo/domain_simple.ll =================================================================== --- /dev/null +++ test/Analysis/PolyhedralInfo/domain_simple.ll @@ -0,0 +1,173 @@ +; RUN: opt -polyhedral-value-info -analyze < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; void simple1(int *A) { +; for (int i = 0; i < 10; i++) +; A[i] = i; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 9 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 9 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @simple1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp slt i64 %indvars.iv, 10 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = trunc i64 %indvars.iv to i32 + store i32 %tmp, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; +; void simple2(int *A) { +; for (int i = 2; i < 10; i++) +; A[i] = i; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 8 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 7 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 7 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @simple2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 2, %entry ] + %exitcond = icmp slt i64 %indvars.iv, 10 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = trunc i64 %indvars.iv to i32 + store i32 %tmp, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; +; void simple3(int *A, long N) { +; for (int i = 0; i < N; i++) +; A[i] = i; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [N] -> { [i0] -> [(1)] : (0 <= i0 <= N) or i0 = 0 } [for.cond] +; CHECK: [N] -> { [i0] -> [(1)] : 0 <= i0 < N } [for.body] +; CHECK: [N] -> { [i0] -> [(1)] : 0 <= i0 < N } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @simple3(i32* %A, i64 %N) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp slt i64 %indvars.iv, %N + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = trunc i64 %indvars.iv to i32 + store i32 %tmp, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; +; void simple4(int *A, long I, long E) { +; for (int i = I; i < E; i++) +; A[i] = i; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : (0 < i0 <= -I + E) or i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1)] : 0 <= i0 < -I + E } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 < -I + E } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @simple4(i32* %A, i64 %I, i64 %E) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ %I, %entry ] + %exitcond = icmp slt i64 %indvars.iv, %E + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = trunc i64 %indvars.iv to i32 + store i32 %tmp, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; +; void simple5(int *A) { +; int i = 0; +; do { +; S: A[i] = i; +; } while (i++ < 10); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [do.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [S] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 10 } [do.cond] +; CHECK: { [] -> [(1)] } [do.end] +define void @simple5(i32* %A) { +entry: + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + br label %S + +S: + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = trunc i64 %indvars.iv to i32 + store i32 %tmp, i32* %arrayidx, align 4 + br label %do.cond + +do.cond: ; preds = %do.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp slt i64 %indvars.iv.next, 11 + br i1 %exitcond, label %do.body, label %do.end + +do.end: ; preds = %do.cond + ret void +} Index: test/Analysis/PolyhedralInfo/loops_1d.ll =================================================================== --- /dev/null +++ test/Analysis/PolyhedralInfo/loops_1d.ll @@ -0,0 +1,1063 @@ +; RUN: opt -polyhedral-value-info -analyze < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; void d1_c_slt_0(int *A) { +; for (int i = 0; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_slt_0(i32* %A) { +entry: + br label %for.cond +; CHECK: { [] -> [(1)] } [entry] [DOMAIN] [Scope: ] + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp slt i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end +; CHECK: { [] -> [(1)] } [for.cond.cleanup] [DOMAIN] [Scope: ] + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.body] [DOMAIN] [Scope: ] + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 999 } [for.inc] [DOMAIN] [Scope: ] + +for.end: ; preds = %for.cond.cleanup + ret void +; CHECK: { [] -> [(1)] } [for.end] [DOMAIN] [Scope: ] +} + + +; void d1_c_slt_1(int *A) { +; for (int i = 500; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_slt_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %exitcond = icmp slt i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(500 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_slt_2(int *A) { +; for (int i = -500; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_slt_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ -500, %entry ] + %exitcond = icmp slt i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(-500 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_slt_3(int *A) { +; for (int i = 1000; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_slt_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + br i1 false, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc +; CHECK: { } [for.body] [DOMAIN] [Scope: ] + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_0(int *A) { +; for (int i = 0; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_sne_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_1(int *A) { +; for (int i = 500; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_sne_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(500 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_2(int *A) { +; for (int i = -500; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_sne_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ -500, %entry ] + %exitcond = icmp eq i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(-500 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_3(int *A) { +; for (int i = 1000; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_sne_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_0_r(int *A) { +; for (int i = 0; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_sgt_0_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp slt i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_1_r(int *A) { +; for (int i = 1000; i > 500; i--) +; A[i]++; +; } +define void @d1_c_sgt_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, 500 + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_2_r(int *A) { +; for (int i = 1000; i > -500; i--) +; A[i]++; +; } +define void @d1_c_sgt_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp sgt i64 %indvars.iv, -500 + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_3_r(int *A) { +; for (int i = 1000; i > 1000; i--) +; A[i]++; +; } +define void @d1_c_sgt_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + br i1 false, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_0_r(int *A) { +; for (int i = 1000; i != 0; i--) +; A[i]++; +; } +define void @d1_c_sne_0_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 0 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_1_r(int *A) { +; for (int i = 1000; i != 500; i--) +; A[i]++; +; } +define void @d1_c_sne_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 500 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_2_r(int *A) { +; for (int i = 1000; i != -500; i--) +; A[i]++; +; } +define void @d1_c_sne_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %tmp = trunc i64 %indvars.iv to i32 + %cmp = icmp eq i32 %tmp, -500 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_3_r(int *A) { +; for (int i = 1000; i != 1000; i--) +; A[i]++; +; } +define void @d1_c_sne_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_0(unsigned *A) { +; for (unsigned i = 0; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_ult_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ult i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_1(unsigned *A) { +; for (unsigned i = 500; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_ult_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %exitcond = icmp ult i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(500 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_2(unsigned *A) { +; for (unsigned i = -500; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_ult_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 4294966796, %entry ] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(4294966796 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + br i1 false, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_3(unsigned *A) { +; for (unsigned i = 1000; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_ult_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + br i1 false, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_0(unsigned *A) { +; for (unsigned i = 0; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_une_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_1(unsigned *A) { +; for (unsigned i = 500; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_une_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(500 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_2(unsigned *A) { +; for (unsigned i = -500; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_une_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ -500, %entry ], [ %inc1, %for.inc ] + %cmp = icmp eq i32 %i.0, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(-500 + i0)] } [i.0] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %inc1 = add nsw i32 %i.0, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_3(unsigned *A) { +; for (unsigned i = 1000; i != 1000; i++) +; A[i]++; +; } +define void @d1_c_une_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 + i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_0(unsigned *A) { +; for (unsigned i = 0; i < 1000; i++) +; A[i]++; +; } +define void @d1_c_ugt_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ult i64 %indvars.iv, 1000 + br i1 %exitcond, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_1_r(unsigned *A) { +; for (unsigned i = 1000; i > 500; i--) +; A[i]++; +; } +define void @d1_c_ugt_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, 500 + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_2_r(unsigned *A) { +; for (unsigned i = 1000; i > -500; i--) +; A[i]++; +; } +define void @d1_c_ugt_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i32 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i32 %indvars.iv, -500 + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i32 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add i32 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_3_r(unsigned *A) { +; for (unsigned i = 1000; i > 1000; i--) +; A[i]++; +; } +define void @d1_c_ugt_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 + 4294967295i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4294967295 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_0_r(unsigned *A) { +; for (unsigned i = 1000; i != 0; i--) +; A[i]++; +; } +define void @d1_c_une_0_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 0 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1000 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_1_r(unsigned *A) { +; for (unsigned i = 1000; i != 500; i--) +; A[i]++; +; } +define void @d1_c_une_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 500 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_2_r(unsigned *A) { +; for (unsigned i = 1000; i != -500; i--) +; A[i]++; +; } +define void @d1_c_une_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ 1000, %entry ], [ %dec, %for.inc ] + %cmp = icmp eq i32 %i.0, -500 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 1500 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [i.0] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %dec = add nsw i32 %i.0, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_3_r(unsigned *A) { +; for (unsigned i = 1000; i != 1000; i--) +; A[i]++; +; } +define void @d1_c_une_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] [DOMAIN] [Scope: ] +; CHECK: { [i0] -> [(1000 - i0)] } [indvars.iv] [UNKNOWN] [Scope: ] + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -1 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} Index: test/Analysis/PolyhedralInfo/loops_1d_strided.ll =================================================================== --- /dev/null +++ test/Analysis/PolyhedralInfo/loops_1d_strided.ll @@ -0,0 +1,1237 @@ +; RUN: opt -polyhedral-value-info -analyze < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; void d1_c_slt_0(int *A) { +; for (int i = 0; i < 1000; i += 2) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] +; CHECK: { [i0] -> [(2i0)] } [indvars.iv] +; CHECK: { [] -> [(1)] } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 499 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 499 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_slt_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_slt_1(int *A) { +; for (int i = 500; i < 1000; i += 3) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 167 } [for.cond] +; CHECK: { [i0] -> [(500 + 3i0)] } [indvars.iv] +; CHECK: { [] -> [(1)] } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 166 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 166 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_slt_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 3 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_slt_2(int *A) { +; for (int i = -500; i < 1000; i += 4) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 375 } [for.cond] +; CHECK: { [i0] -> [(-500 + 4i0)] } [indvars.iv] +; CHECK: { [] -> [(1)] } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 374 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 374 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_slt_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ -500, %entry ] + %cmp = icmp slt i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, 4 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_slt_3(int *A) { +; for (int i = 1000; i < 1000; i += 5) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 5i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_slt_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp slt i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 5 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_0(int *A) { +; for (int i = 0; i != 1000; i += 6) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(6i0)] } [indvars.iv] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_sne_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp ne i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 6 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_1(int *A) { +; for (int i = 500; i != 1000; i += 7) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(500 + 7i0)] } [indvars.iv] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_sne_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %cmp = icmp ne i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 7 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_2(int *A) { +; for (int i = -500; i != 1000; i += 8) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(-500 + 8i0)] } [indvars.iv] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_sne_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ -500, %entry ] + %cmp = icmp ne i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, 8 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_3(int *A) { +; for (int i = 1000; i != 1000; i += 9) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 9i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sne_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 9 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_0_r(int *A) { +; for (int i = 0; i < 1000; i += 10) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 100 } [for.cond] +; CHECK: { [i0] -> [(10i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 99 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 99 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sgt_0_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 10 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_1_r(int *A) { +; for (int i = 1000; i > 500; i -= 2) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 250 } [for.cond] +; CHECK: { [i0] -> [(1000 - 2i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 249 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 249 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sgt_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, 500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -2 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_2_r(int *A) { +; for (int i = 1000; i > -500; i -= 3) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] +; CHECK: { [i0] -> [(1000 - 3i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 499 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 499 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sgt_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp sgt i64 %indvars.iv, -500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -3 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sgt_3_r(int *A) { +; for (int i = 1000; i > 1000; i -= 4) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 - 4i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sgt_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp sgt i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -4 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_0_r(int *A) { +; for (int i = 1000; i != 0; i -= 5) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 200 } [for.cond] +; CHECK: { [i0] -> [(1000 - 5i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 199 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 199 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sne_0_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 0 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -5 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_1_r(int *A) { +; for (int i = 1000; i != 500; i -= 6) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(1000 - 6i0)] } [indvars.iv] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_sne_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ne i64 %indvars.iv, 500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -6 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_2_r(int *A) { +; for (int i = 1000; i != -500; i -= 7) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(1000 - 7i0)] } [indvars.iv] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_sne_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ne i64 %indvars.iv, -500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -7 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_sne_3_r(int *A) { +; for (int i = 1000; i != 1000; i -= 8) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 - 8i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_sne_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -8 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_0(unsigned *A) { +; for (unsigned i = 0; i < 1000; i += 2) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 500 } [for.cond] +; CHECK: { [i0] -> [(2i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 499 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 499 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ult_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_1(unsigned *A) { +; for (unsigned i = 500; i < 1000; i += 3) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 167 } [for.cond] +; CHECK: { [i0] -> [(500 + 3i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 166 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 166 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ult_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 500, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 3 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_2(unsigned *A) { +; for (unsigned i = -500; i < 1000; i += 4) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(4294966796 + 4i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ult_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 4294966796, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ult_3(unsigned *A) { +; for (unsigned i = 1000; i < 1000; i += 5) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 5i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ult_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 5 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_0(unsigned *A) { +; for (unsigned i = 0; i != 1000; i += 6) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(6i0)] } [i.0] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_une_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ 0, %entry ], [ %add, %for.inc ] + %cmp = icmp eq i32 %i.0, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %add = add i32 %i.0, 6 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_1(unsigned *A) { +; for (unsigned i = 500; i != 1000; i += 7) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(500 + 7i0)] } [i.0] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_une_1(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ 500, %entry ], [ %add, %for.inc ] + %cmp = icmp eq i32 %i.0, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %add = add i32 %i.0, 7 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_2(unsigned *A) { +; for (unsigned i = -500; i != 1000; i += 8) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(-500 + 8i0)] } [i.0] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_une_2(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ -500, %entry ], [ %add, %for.inc ] + %cmp = icmp eq i32 %i.0, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %add = add i32 %i.0, 8 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_3(unsigned *A) { +; for (unsigned i = 1000; i != 1000; i += 9) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 9i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_une_3(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 9 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_0(unsigned *A) { +; for (unsigned i = 0; i < 1000; i += 10) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 100 } [for.cond] +; CHECK: { [i0] -> [(10i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 99 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 99 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ugt_0(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp ult i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 10 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_1_r(unsigned *A) { +; for (unsigned i = 1000; i > 500; i -= 2) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 250 } [for.cond] +; CHECK: { [i0] -> [(1000 - 2i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 249 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 249 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ugt_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, 500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -2 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_2_r(unsigned *A) { +; for (unsigned i = 1000; i > -500; i -= 3) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 4294967293i0)] } [indvars.iv] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_ugt_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, -500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4294967293 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_ugt_3_r(unsigned *A) { +; for (unsigned i = 1000; i > 1000; i -= 4) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 4294967292i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_ugt_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp ugt i64 %indvars.iv, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4294967292 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_0_r(unsigned *A) { +; for (unsigned i = 1000; i != 0; i -= 5) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 200 } [for.cond] +; CHECK: { [i0] -> [(1000 - 5i0)] } [indvars.iv] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 199 } [for.body] +; CHECK: { [i0] -> [(1)] : 0 <= i0 <= 199 } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_une_0_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 0 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nsw i64 %indvars.iv, -5 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_1_r(unsigned *A) { +; for (unsigned i = 1000; i != 500; i -= 6) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(1000 - 6i0)] } [i.0] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_une_1_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ 1000, %entry ], [ %sub, %for.inc ] + %cmp = icmp eq i32 %i.0, 500 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %sub = add i32 %i.0, -6 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_2_r(unsigned *A) { +; for (unsigned i = 1000; i != -500; i -= 7) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.cond] +; CHECK: { [i0] -> [(1000 - 7i0)] } [i.0] +; CHECK: { } [for.cond.cleanup] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.body] +; CHECK: { [i0] -> [(1)] : i0 >= 0 } [for.inc] +; CHECK: { } [for.end] +define void @d1_c_une_2_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %i.0 = phi i32 [ 1000, %entry ], [ %sub, %for.inc ] + %cmp = icmp eq i32 %i.0, -500 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %idxprom = zext i32 %i.0 to i64 + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %sub = add i32 %i.0, -7 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} + +; void d1_c_une_3_r(unsigned *A) { +; for (unsigned i = 1000; i != 1000; i -= 8) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: { [i0] -> [(1)] : i0 = 0 } [for.cond] +; CHECK: { [i0] -> [(1000 + 4294967288i0)] } [indvars.iv] +; CHECK: { } [for.body] +; CHECK: { } [for.inc] +; CHECK: { [] -> [(1)] } [for.end] +define void @d1_c_une_3_r(i32* %A) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 1000, %entry ] + %cmp = icmp eq i64 %indvars.iv, 1000 + br i1 %cmp, label %for.cond.cleanup, label %for.body + +for.cond.cleanup: ; preds = %for.cond + br label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 4294967288 + br label %for.cond + +for.end: ; preds = %for.cond.cleanup + ret void +} Index: test/Analysis/PolyhedralInfo/loops_early_exits.ll =================================================================== --- /dev/null +++ test/Analysis/PolyhedralInfo/loops_early_exits.ll @@ -0,0 +1,648 @@ +; RUN: opt -polyhedral-value-info -analyze < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; void lwe0(int *A, int P) { +; for (int i = 0; i < 100; i++) { +; A[i]++; +; if (i > P) +; return; +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 100 and i0 <= 1 + P) or i0 = 0 } [for.cond] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 99 and i0 <= 1 + P) or i0 = 0 } [for.body] +; CHECK: [P] -> { [] -> [(1)] : P <= 98 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 99 and i0 <= P } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 99 and i0 <= P } [for.inc] +; CHECK: [P] -> { [] -> [(1)] : P >= 99 } [for.end.loopexit] +; CHECK: { [] -> [(1)] } [for.end] +define void @lwe0(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %for.body, label %for.end.loopexit + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + %cmp1 = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + br label %for.end + +if.end: ; preds = %for.body + %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx3, align 4 + %inc4 = add nsw i32 %tmp2, 1 + store i32 %inc4, i32* %arrayidx3, align 4 + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end.loopexit: ; preds = %for.cond + br label %for.end + +for.end: ; preds = %for.end.loopexit, %if.then + ret void +} + +; void lwe1(int *A, int P) { +; int i = 0; +; do { +; A[i]++; +; if (i > P) +; return; +; A[i]++; +; } while (i++ < 100); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 100 and i0 <= 1 + P) or i0 = 0 } [do.body] +; CHECK: [P] -> { [] -> [(1)] : P <= 99 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 100 and i0 <= P } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 100 and i0 <= P } [do.cond] +; CHECK: [P] -> { [] -> [(1)] : P >= 100 } [do.end.loopexit] +; CHECK: { [] -> [(1)] } [do.end] +define void @lwe1(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + %cmp = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %do.body + br label %do.end + +if.end: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp2, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp5 = icmp slt i64 %indvars.iv, 100 + br i1 %cmp5, label %do.body, label %do.end.loopexit + +do.end.loopexit: ; preds = %do.cond + br label %do.end + +do.end: ; preds = %do.end.loopexit, %if.then + ret void +} + +; void lwe2(int *A, int P) { +; for (int i = 0; i < 100; i++) { +; A[i]++; +; if (i > P) +; break; +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 100 and i0 <= 1 + P) or i0 = 0 } [for.cond] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 99 and i0 <= 1 + P) or i0 = 0 } [for.body] +; CHECK: [P] -> { [] -> [(1)] : P <= 98 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 99 and i0 <= P } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 99 and i0 <= P } [for.inc] +; CHECK: [P] -> { [] -> [(1)] : P >= 99 } [for.end.loopexit] +; CHECK: { [] -> [(1)] } [for.end] +define void @lwe2(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %for.body, label %for.end.loopexit + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + %cmp1 = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + br label %for.end + +if.end: ; preds = %for.body + %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx3, align 4 + %inc4 = add nsw i32 %tmp2, 1 + store i32 %inc4, i32* %arrayidx3, align 4 + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end.loopexit: ; preds = %for.cond + br label %for.end + +for.end: ; preds = %for.end.loopexit, %if.then + ret void +} + +; void lwe3(int *A, int P) { +; int i = 0; +; do { +; A[i]++; +; if (i > P) +; break; +; A[i]++; +; } while (i++ < 100); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 100 and i0 <= 1 + P) or i0 = 0 } [do.body] +; CHECK: [P] -> { [] -> [(1)] : P <= 99 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 100 and i0 <= P } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 100 and i0 <= P } [do.cond] +; CHECK: [P] -> { [] -> [(1)] : P >= 100 } [do.end.loopexit] +; CHECK: { [] -> [(1)] } [do.end] +define void @lwe3(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + %cmp = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %do.body + br label %do.end + +if.end: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp2, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp5 = icmp slt i64 %indvars.iv, 100 + br i1 %cmp5, label %do.body, label %do.end.loopexit + +do.end.loopexit: ; preds = %do.cond + br label %do.end + +do.end: ; preds = %do.end.loopexit, %if.then + ret void +} + +; void lwe4(int *A, int P) { +; for (int i = 0; i < 100; i++) { +; A[i]++; +; if (i > P) +; abort(); +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 100 and i0 <= 1 + P) or i0 = 0 } [for.cond] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 99 and i0 <= 1 + P) or i0 = 0 } [for.body] +; CHECK: [P] -> { [] -> [(1)] : P <= 98 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 99 and i0 <= P } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 99 and i0 <= P } [for.inc] +; CHECK: [P] -> { [] -> [(1)] : P >= 99 } [for.end] +define void @lwe4(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + %cmp1 = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + call void @abort() + unreachable + +if.end: ; preds = %for.body + %arrayidx3 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx3, align 4 + %inc4 = add nsw i32 %tmp2, 1 + store i32 %inc4, i32* %arrayidx3, align 4 + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +declare void @abort() + +; void lwe5(int *A, int P) { +; int i = 0; +; do { +; A[i]++; +; if (i > P) +; abort(); +; A[i]++; +; } while (i++ < 100); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [P] -> { [i0] -> [(1)] : (0 <= i0 <= 100 and i0 <= 1 + P) or i0 = 0 } [do.body] +; CHECK: [P] -> { [] -> [(1)] : P <= 99 } [if.then] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 100 and i0 <= P } [if.end] +; CHECK: [P] -> { [i0] -> [(1)] : 0 <= i0 <= 100 and i0 <= P } [do.cond] +; CHECK: [P] -> { [] -> [(1)] : P >= 100 } [do.end] +define void @lwe5(i32* %A, i32 %P) { +entry: + %tmp = sext i32 %P to i64 + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + %cmp = icmp sgt i64 %indvars.iv, %tmp + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %do.body + call void @abort() + unreachable + +if.end: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp2 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp2, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp5 = icmp slt i64 %indvars.iv, 100 + br i1 %cmp5, label %do.body, label %do.end + +do.end: ; preds = %do.cond + ret void +} + +; void lwe6(int *A, int P) { +; for (int i = 0; i < 100; i++) { +; A[i]++; +; if (f()) +; return; +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 100) } [for.cond] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 99) } [for.body] +; CHECK: [call] -> { [] -> [(1)] : call < 0 or call > 0 } [if.then] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 99 } [if.end] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 99 } [for.inc] +; CHECK: [call] -> { [] -> [(1)] : call = 0 } [for.end.loopexit] +; CHECK: { [] -> [(1)] } [for.end] +define void @lwe6(i32* %A, i32 %P) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %for.body, label %for.end.loopexit + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + %call = call i32 (...) @f() + %tobool = icmp eq i32 %call, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %for.body + br label %for.end + +if.end: ; preds = %for.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp1, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end.loopexit: ; preds = %for.cond + br label %for.end + +for.end: ; preds = %for.end.loopexit, %if.then + ret void +} + +declare i32 @f(...) + +; void lwe7(int *A, int P) { +; int i = 0; +; do { +; A[i]++; +; if (f()) +; return; +; A[i]++; +; } while (i++ < 100); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 100) } [do.body] +; CHECK: [call] -> { [] -> [(1)] : call < 0 or call > 0 } [if.then] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 100 } [if.end] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 100 } [do.cond] +; CHECK: [call] -> { [] -> [(1)] : call = 0 } [do.end.loopexit] +; CHECK: { [] -> [(1)] } [do.end] +define void @lwe7(i32* %A, i32 %P) { +entry: + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + %call = call i32 (...) @f() + %tobool = icmp eq i32 %call, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %do.body + br label %do.end + +if.end: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp1, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %do.body, label %do.end.loopexit + +do.end.loopexit: ; preds = %do.cond + br label %do.end + +do.end: ; preds = %do.end.loopexit, %if.then + ret void +} + +; void lwe8(int *A, int P) { +; for (int i = 0; i < 100; i++) { +; A[i]++; +; if (f()) +; break; +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 100) } [for.cond] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 99) } [for.body] +; CHECK: [call] -> { [] -> [(1)] : call < 0 or call > 0 } [if.then] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 99 } [if.end] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 99 } [for.inc] +; CHECK: [call] -> { [] -> [(1)] : call = 0 } [for.end.loopexit] +; CHECK: { [] -> [(1)] } [for.end] +define void @lwe8(i32* %A, i32 %P) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %for.body, label %for.end.loopexit + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + %call = call i32 (...) @f() + %tobool = icmp eq i32 %call, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %for.body + br label %for.end + +if.end: ; preds = %for.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp1, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end.loopexit: ; preds = %for.cond + br label %for.end + +for.end: ; preds = %for.end.loopexit, %if.then + ret void +} + +; void lwe9(int *A, int P) { +; int i = 0; +; do { +; A[i]++; +; if (f()) +; break; +; A[i]++; +; } while (i++ < 100); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 100) } [do.body] +; CHECK: [call] -> { [] -> [(1)] : call < 0 or call > 0 } [if.then] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 100 } [if.end] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 100 } [do.cond] +; CHECK: [call] -> { [] -> [(1)] : call = 0 } [do.end.loopexit] +; CHECK: { [] -> [(1)] } [do.end] +define void @lwe9(i32* %A, i32 %P) { +entry: + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + %call = call i32 (...) @f() + %tobool = icmp eq i32 %call, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %do.body + br label %do.end + +if.end: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp1, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %do.body, label %do.end.loopexit + +do.end.loopexit: ; preds = %do.cond + br label %do.end + +do.end: ; preds = %do.end.loopexit, %if.then + ret void +} + +; void lwe10(int *A, int P) { +; for (int i = 0; i < 100; i++) { +; A[i]++; +; if (f()) +; abort(); +; A[i]++; +; } +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 100) } [for.cond] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 99) } [for.body] +; CHECK: [call] -> { [] -> [(1)] : call < 0 or call > 0 } [if.then] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 99 } [if.end] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 99 } [for.inc] +; CHECK: [call] -> { [] -> [(1)] : call = 0 } [for.end] +define void @lwe10(i32* %A, i32 %P) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + %call = call i32 (...) @f() + %tobool = icmp eq i32 %call, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %for.body + call void @abort() + unreachable + +if.end: ; preds = %for.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp1, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %for.inc + +for.inc: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; void lwe11(int *A, int P) { +; int i = 0; +; do { +; A[i]++; +; if (f()) +; abort(); +; A[i]++; +; } while (i++ < 100); +; } +; +; CHECK: { [] -> [(1)] } [entry] +; CHECK: [call] -> { [i0] -> [(1)] : i0 = 0 or (call = 0 and 0 <= i0 <= 100) } [do.body] +; CHECK: [call] -> { [] -> [(1)] : call < 0 or call > 0 } [if.then] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 100 } [if.end] +; CHECK: [call] -> { [i0] -> [(1)] : call = 0 and 0 <= i0 <= 100 } [do.cond] +; CHECK: [call] -> { [] -> [(1)] : call = 0 } [do.end] +define void @lwe11(i32* %A, i32 %P) { +entry: + br label %do.body + +do.body: ; preds = %do.cond, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %do.cond ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp, 1 + store i32 %inc, i32* %arrayidx, align 4 + %call = call i32 (...) @f() + %tobool = icmp eq i32 %call, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %do.body + call void @abort() + unreachable + +if.end: ; preds = %do.body + %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx2, align 4 + %inc3 = add nsw i32 %tmp1, 1 + store i32 %inc3, i32* %arrayidx2, align 4 + br label %do.cond + +do.cond: ; preds = %if.end + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %cmp = icmp slt i64 %indvars.iv, 100 + br i1 %cmp, label %do.body, label %do.end + +do.end: ; preds = %do.cond + ret void +} Index: test/Analysis/PolyhedralInfo/unbounded_domains.ll =================================================================== --- /dev/null +++ test/Analysis/PolyhedralInfo/unbounded_domains.ll @@ -0,0 +1,42 @@ +; RUN: opt -polyhedral-value-info -analyze < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; void ud0(int *A, unsigned N) { +; for (unsigned i = 0; i != N; i++) +; A[i]++; +; } +; +; CHECK: { [] -> [(1)] } [entry] + +; Verify we have an invalid domain for "negative" values of N: +; CHECK: [N] -> { [i0] -> [(1)] : (N < 0 and i0 >= 0) or (0 <= i0 <= N) or i0 = 0 } [for.cond] +; CHECK-SAME: [ID: [N] -> { [] : N < 0 }] + +; CHECK: [N] -> { [i0] -> [(1)] : i0 >= 0 and (N < 0 or i0 < N) } [for.body] +; CHECK: [N] -> { [i0] -> [(1)] : i0 >= 0 and (N < 0 or i0 < N) } [for.inc] +; CHECK: [N] -> { [] -> [(1)] : N >= 0 } [for.end] +; +define void @ud0(i32* %A, i32 %N) { +entry: + %tmp = zext i32 %N to i64 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %cmp = icmp ne i64 %indvars.iv, %tmp + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp1 = load i32, i32* %arrayidx, align 4 + %inc = add nsw i32 %tmp1, 1 + store i32 %inc, i32* %arrayidx, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -33,6 +33,7 @@ # Add LTO, llvm-ar, llvm-config, and llvm-profdata before clang, ExternalProject # requires targets specified in DEPENDS to exist before the call to # ExternalProject_Add. +add_llvm_tool_subdirectory(isl) add_llvm_tool_subdirectory(lto) add_llvm_tool_subdirectory(gold) add_llvm_tool_subdirectory(llvm-ar)